Wise&mystical  1.0
Project about Europe
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.8 - 2022-02-12
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===============
15miniaudio is a single file library for audio playback and capture. To use it, do the following in
16one .c file:
17
18 ```c
19 #define MINIAUDIO_IMPLEMENTATION
20 #include "miniaudio.h"
21 ```
22
23You can do `#include "miniaudio.h"` in other parts of the program just like any other header.
24
25miniaudio includes both low level and high level APIs. The low level API is good for those who want
26to do all of their mixing themselves and only require a light weight interface to the underlying
27audio device. The high level API is good for those who have complex mixing and effect requirements.
28
29In miniaudio, objects are transparent structures. Unlike many other libraries, there are no handles
30to opaque objects which means you need to allocate memory for objects yourself. In the examples
31presented in this documentation you will often see objects declared on the stack. You need to be
32careful when translating these examples to your own code so that you don't accidentally declare
33your objects on the stack and then cause them to become invalid once the function returns. In
34addition, you must ensure the memory address of your objects remain the same throughout their
35lifetime. You therefore cannot be making copies of your objects.
36
37A config/init pattern is used throughout the entire library. The idea is that you set up a config
38object and pass that into the initialization routine. The advantage to this system is that the
39config object can be initialized with logical defaults and new properties added to it without
40breaking the API. The config object can be allocated on the stack and does not need to be
41maintained after initialization of the corresponding object.
42
43
441.1. Low Level API
45------------------
46The low level API gives you access to the raw audio data of an audio device. It supports playback,
47capture, full-duplex and loopback (WASAPI only). You can enumerate over devices to determine which
48physical device(s) you want to connect to.
49
50The low level API uses the concept of a "device" as the abstraction for physical devices. The idea
51is that you choose a physical device to emit or capture audio from, and then move data to/from the
52device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a
53callback which you specify when initializing the device.
54
55When initializing the device you first need to configure it. The device configuration allows you to
56specify things like the format of the data delivered via the callback, the size of the internal
57buffer and the ID of the device you want to emit or capture audio from.
58
59Once you have the device configuration set up you can initialize the device. When initializing a
60device you need to allocate memory for the device object beforehand. This gives the application
61complete control over how the memory is allocated. In the example below we initialize a playback
62device on the stack, but you could allocate it on the heap if that suits your situation better.
63
64 ```c
65 void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
66 {
67 // In playback mode copy data to pOutput. In capture mode read data from pInput. In full-duplex mode, both
68 // pOutput and pInput will be valid and you can move data from pInput into pOutput. Never process more than
69 // frameCount frames.
70 }
71
72 int main()
73 {
74 ma_device_config config = ma_device_config_init(ma_device_type_playback);
75 config.playback.format = ma_format_f32; // Set to ma_format_unknown to use the device's native format.
76 config.playback.channels = 2; // Set to 0 to use the device's native channel count.
77 config.sampleRate = 48000; // Set to 0 to use the device's native sample rate.
78 config.dataCallback = data_callback; // This function will be called when miniaudio needs more data.
79 config.pUserData = pMyCustomData; // Can be accessed from the device object (device.pUserData).
80
81 ma_device device;
82 if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) {
83 return -1; // Failed to initialize the device.
84 }
85
86 ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually.
87
88 // Do something here. Probably your program's main loop.
89
90 ma_device_uninit(&device); // This will stop the device so no need to do that manually.
91 return 0;
92 }
93 ```
94
95In the example above, `data_callback()` is where audio data is written and read from the device.
96The idea is in playback mode you cause sound to be emitted from the speakers by writing audio data
97to the output buffer (`pOutput` in the example). In capture mode you read data from the input
98buffer (`pInput`) to extract sound captured by the microphone. The `frameCount` parameter tells you
99how many frames can be written to the output buffer and read from the input buffer. A "frame" is
100one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2
101samples: one for the left, one for the right. The channel count is defined by the device config.
102The size in bytes of an individual sample is defined by the sample format which is also specified
103in the device config. Multi-channel audio data is always interleaved, which means the samples for
104each frame are stored next to each other in memory. For example, in a stereo stream the first pair
105of samples will be the left and right samples for the first frame, the second pair of samples will
106be the left and right samples for the second frame, etc.
107
108The configuration of the device is defined by the `ma_device_config` structure. The config object
109is always initialized with `ma_device_config_init()`. It's important to always initialize the
110config with this function as it initializes it with logical defaults and ensures your program
111doesn't break when new members are added to the `ma_device_config` structure. The example above
112uses a fairly simple and standard device configuration. The call to `ma_device_config_init()` takes
113a single parameter, which is whether or not the device is a playback, capture, duplex or loopback
114device (loopback devices are not supported on all backends). The `config.playback.format` member
115sets the sample format which can be one of the following (all formats are native-endian):
116
117 +---------------+----------------------------------------+---------------------------+
118 | Symbol | Description | Range |
119 +---------------+----------------------------------------+---------------------------+
120 | ma_format_f32 | 32-bit floating point | [-1, 1] |
121 | ma_format_s16 | 16-bit signed integer | [-32768, 32767] |
122 | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] |
123 | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] |
124 | ma_format_u8 | 8-bit unsigned integer | [0, 255] |
125 +---------------+----------------------------------------+---------------------------+
126
127The `config.playback.channels` member sets the number of channels to use with the device. The
128channel count cannot exceed MA_MAX_CHANNELS. The `config.sampleRate` member sets the sample rate
129(which must be the same for both playback and capture in full-duplex configurations). This is
130usually set to 44100 or 48000, but can be set to anything. It's recommended to keep this between
1318000 and 384000, however.
132
133Note that leaving the format, channel count and/or sample rate at their default values will result
134in the internal device's native configuration being used which is useful if you want to avoid the
135overhead of miniaudio's automatic data conversion.
136
137In addition to the sample format, channel count and sample rate, the data callback and user data
138pointer are also set via the config. The user data pointer is not passed into the callback as a
139parameter, but is instead set to the `pUserData` member of `ma_device` which you can access
140directly since all miniaudio structures are transparent.
141
142Initializing the device is done with `ma_device_init()`. This will return a result code telling you
143what went wrong, if anything. On success it will return `MA_SUCCESS`. After initialization is
144complete the device will be in a stopped state. To start it, use `ma_device_start()`.
145Uninitializing the device will stop it, which is what the example above does, but you can also stop
146the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again.
147Note that it's important to never stop or start the device from inside the callback. This will
148result in a deadlock. Instead you set a variable or signal an event indicating that the device
149needs to stop and handle it in a different thread. The following APIs must never be called inside
150the callback:
151
152 ```c
153 ma_device_init()
154 ma_device_init_ex()
155 ma_device_uninit()
156 ma_device_start()
157 ma_device_stop()
158 ```
159
160You must never try uninitializing and reinitializing a device inside the callback. You must also
161never try to stop and start it from inside the callback. There are a few other things you shouldn't
162do in the callback depending on your requirements, however this isn't so much a thread-safety
163thing, but rather a real-time processing thing which is beyond the scope of this introduction.
164
165The example above demonstrates the initialization of a playback device, but it works exactly the
166same for capture. All you need to do is change the device type from `ma_device_type_playback` to
167`ma_device_type_capture` when setting up the config, like so:
168
169 ```c
170 ma_device_config config = ma_device_config_init(ma_device_type_capture);
171 config.capture.format = MY_FORMAT;
172 config.capture.channels = MY_CHANNEL_COUNT;
173 ```
174
175In the data callback you just read from the input buffer (`pInput` in the example above) and leave
176the output buffer alone (it will be set to NULL when the device type is set to
177`ma_device_type_capture`).
178
179These are the available device types and how you should handle the buffers in the callback:
180
181 +-------------------------+--------------------------------------------------------+
182 | Device Type | Callback Behavior |
183 +-------------------------+--------------------------------------------------------+
184 | ma_device_type_playback | Write to output buffer, leave input buffer untouched. |
185 | ma_device_type_capture | Read from input buffer, leave output buffer untouched. |
186 | ma_device_type_duplex | Read from input buffer, write to output buffer. |
187 | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. |
188 +-------------------------+--------------------------------------------------------+
189
190You will notice in the example above that the sample format and channel count is specified
191separately for playback and capture. This is to support different data formats between the playback
192and capture devices in a full-duplex system. An example may be that you want to capture audio data
193as a monaural stream (one channel), but output sound to a stereo speaker system. Note that if you
194use different formats between playback and capture in a full-duplex configuration you will need to
195convert the data yourself. There are functions available to help you do this which will be
196explained later.
197
198The example above did not specify a physical device to connect to which means it will use the
199operating system's default device. If you have multiple physical devices connected and you want to
200use a specific one you will need to specify the device ID in the configuration, like so:
201
202 ```c
203 config.playback.pDeviceID = pMyPlaybackDeviceID; // Only if requesting a playback or duplex device.
204 config.capture.pDeviceID = pMyCaptureDeviceID; // Only if requesting a capture, duplex or loopback device.
205 ```
206
207To retrieve the device ID you will need to perform device enumeration, however this requires the
208use of a new concept called the "context". Conceptually speaking the context sits above the device.
209There is one context to many devices. The purpose of the context is to represent the backend at a
210more global level and to perform operations outside the scope of an individual device. Mainly it is
211used for performing run-time linking against backend libraries, initializing backends and
212enumerating devices. The example below shows how to enumerate devices.
213
214 ```c
215 ma_context context;
216 if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) {
217 // Error.
218 }
219
220 ma_device_info* pPlaybackInfos;
221 ma_uint32 playbackCount;
222 ma_device_info* pCaptureInfos;
223 ma_uint32 captureCount;
224 if (ma_context_get_devices(&context, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount) != MA_SUCCESS) {
225 // Error.
226 }
227
228 // Loop over each device info and do something with it. Here we just print the name with their index. You may want
229 // to give the user the opportunity to choose which device they'd prefer.
230 for (ma_uint32 iDevice = 0; iDevice < playbackCount; iDevice += 1) {
231 printf("%d - %s\n", iDevice, pPlaybackInfos[iDevice].name);
232 }
233
234 ma_device_config config = ma_device_config_init(ma_device_type_playback);
235 config.playback.pDeviceID = &pPlaybackInfos[chosenPlaybackDeviceIndex].id;
236 config.playback.format = MY_FORMAT;
237 config.playback.channels = MY_CHANNEL_COUNT;
238 config.sampleRate = MY_SAMPLE_RATE;
239 config.dataCallback = data_callback;
240 config.pUserData = pMyCustomData;
241
242 ma_device device;
243 if (ma_device_init(&context, &config, &device) != MA_SUCCESS) {
244 // Error
245 }
246
247 ...
248
249 ma_device_uninit(&device);
250 ma_context_uninit(&context);
251 ```
252
253The first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`.
254The first parameter is a pointer to a list of `ma_backend` values which are used to override the
255default backend priorities. When this is NULL, as in this example, miniaudio's default priorities
256are used. The second parameter is the number of backends listed in the array pointed to by the
257first parameter. The third parameter is a pointer to a `ma_context_config` object which can be
258NULL, in which case defaults are used. The context configuration is used for setting the logging
259callback, custom memory allocation callbacks, user-defined data and some backend-specific
260configurations.
261
262Once the context has been initialized you can enumerate devices. In the example above we use the
263simpler `ma_context_get_devices()`, however you can also use a callback for handling devices by
264using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer
265to a pointer that will, upon output, be set to a pointer to a buffer containing a list of
266`ma_device_info` structures. You also provide a pointer to an unsigned integer that will receive
267the number of items in the returned buffer. Do not free the returned buffers as their memory is
268managed internally by miniaudio.
269
270The `ma_device_info` structure contains an `id` member which is the ID you pass to the device
271config. It also contains the name of the device which is useful for presenting a list of devices
272to the user via the UI.
273
274When creating your own context you will want to pass it to `ma_device_init()` when initializing the
275device. Passing in NULL, like we do in the first example, will result in miniaudio creating the
276context for you, which you don't want to do since you've already created a context. Note that
277internally the context is only tracked by it's pointer which means you must not change the location
278of the `ma_context` object. If this is an issue, consider using `malloc()` to allocate memory for
279the context.
280
281
2821.2. High Level API
283-------------------
284The high level API consists of three main parts:
285
286 * Resource management for loading and streaming sounds.
287 * A node graph for advanced mixing and effect processing.
288 * A high level "engine" that wraps around the resource manager and node graph.
289
290The resource manager (`ma_resource_manager`) is used for loading sounds. It supports loading sounds
291fully into memory and also streaming. It will also deal with reference counting for you which
292avoids the same sound being loaded multiple times.
293
294The node graph is used for mixing and effect processing. The idea is that you connect a number of
295nodes into the graph by connecting each node's outputs to another node's inputs. Each node can
296implement it's own effect. By chaining nodes together, advanced mixing and effect processing can
297be achieved.
298
299The engine encapsulates both the resource manager and the node graph to create a simple, easy to
300use high level API. The resource manager and node graph APIs are covered in more later sections of
301this manual.
302
303The code below shows how you can initialize an engine using it's default configuration.
304
305 ```c
306 ma_result result;
307 ma_engine engine;
308
309 result = ma_engine_init(NULL, &engine);
310 if (result != MA_SUCCESS) {
311 return result; // Failed to initialize the engine.
312 }
313 ```
314
315This creates an engine instance which will initialize a device internally which you can access with
316`ma_engine_get_device()`. It will also initialize a resource manager for you which can be accessed
317with `ma_engine_get_resource_manager()`. The engine itself is a node graph (`ma_node_graph`) which
318means you can pass a pointer to the engine object into any of the `ma_node_graph` APIs (with a
319cast). Alternatively, you can use `ma_engine_get_node_graph()` instead of a cast.
320
321Note that all objects in miniaudio, including the `ma_engine` object in the example above, are
322transparent structures. There are no handles to opaque structures in miniaudio which means you need
323to be mindful of how you declare them. In the example above we are declaring it on the stack, but
324this will result in the struct being invalidated once the function encapsulating it returns. If
325allocating the engine on the heap is more appropriate, you can easily do so with a standard call
326to `malloc()` or whatever heap allocation routine you like:
327
328 ```c
329 ma_engine* pEngine = malloc(sizeof(*pEngine));
330 ```
331
332The `ma_engine` API uses the same config/init pattern used all throughout miniaudio. To configure
333an engine, you can fill out a `ma_engine_config` object and pass it into the first parameter of
334`ma_engine_init()`:
335
336 ```c
337 ma_result result;
338 ma_engine engine;
339 ma_engine_config engineConfig;
340
341 engineConfig = ma_engine_config_init();
342 engineConfig.pResourceManager = &myCustomResourceManager; // <-- Initialized as some earlier stage.
343
344 result = ma_engine_init(&engineConfig, &engine);
345 if (result != MA_SUCCESS) {
346 return result;
347 }
348 ```
349
350This creates an engine instance using a custom config. In this particular example it's showing how
351you can specify a custom resource manager rather than having the engine initialize one internally.
352This is particularly useful if you want to have multiple engine's share the same resource manager.
353
354The engine must be uninitialized with `ma_engine_uninit()` when it's no longer needed.
355
356By default the engine will be started, but nothing will be playing because no sounds have been
357initialized. The easiest but least flexible way of playing a sound is like so:
358
359 ```c
360 ma_engine_play_sound(&engine, "my_sound.wav", NULL);
361 ```
362
363This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the
364internal sound up for recycling. The last parameter is used to specify which sound group the sound
365should be associated with which will be explained later. This particular way of playing a sound is
366simple, but lacks flexibility and features. A more flexible way of playing a sound is to first
367initialize a sound:
368
369 ```c
370 ma_result result;
371 ma_sound sound;
372
373 result = ma_sound_init_from_file(&engine, "my_sound.wav", 0, NULL, NULL, &sound);
374 if (result != MA_SUCCESS) {
375 return result;
376 }
377
378 ma_sound_start(&sound);
379 ```
380
381This returns a `ma_sound` object which represents a single instance of the specified sound file. If
382you want to play the same file multiple times simultaneously, you need to create one sound for each
383instance.
384
385Sounds should be uninitialized with `ma_sound_uninit()`.
386
387Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with
388`ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use
389`ma_sound_seek_to_pcm_frames(&sound, 0)` to seek back to the start of a sound. By default, starting
390and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound
391the be started and/or stopped at a specific time. This can be done with the following functions:
392
393 ```c
394 ma_sound_set_start_time_in_pcm_frames()
395 ma_sound_set_start_time_in_milliseconds()
396 ma_sound_set_stop_time_in_pcm_frames()
397 ma_sound_set_stop_time_in_milliseconds()
398 ```
399
400The start/stop time needs to be specified based on the absolute timer which is controlled by the
401engine. The current global time time in PCM frames can be retrieved with `ma_engine_get_time()`.
402The engine's global time can be changed with `ma_engine_set_time()` for synchronization purposes if
403required.
404
405The third parameter of `ma_sound_init_from_file()` is a set of flags that control how the sound be
406loaded and a few options on which features should be enabled for that sound. By default, the sound
407is synchronously loaded fully into memory straight from the file system without any kind of
408decoding. If you want to decode the sound before storing it in memory, you need to specify the
409`MA_SOUND_FLAG_DECODE` flag. This is useful if you want to incur the cost of decoding at an earlier
410stage, such as a loading stage. Without this option, decoding will happen dynamically at mixing
411time which might be too expensive on the audio thread.
412
413If you want to load the sound asynchronously, you can specify the `MA_SOUND_FLAG_ASYNC` flag. This
414will result in `ma_sound_init_from_file()` returning quickly, but the sound will not start playing
415until the sound has had some audio decoded.
416
417The fourth parameter is a pointer to sound group. A sound group is used as a mechanism to organise
418sounds into groups which have their own effect processing and volume control. An example is a game
419which might have separate groups for sfx, voice and music. Each of these groups have their own
420independent volume control. Use `ma_sound_group_init()` or `ma_sound_group_init_ex()` to initialize
421a sound group.
422
423Sounds and sound groups are nodes in the engine's node graph and can be plugged into any `ma_node`
424API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex
425effect chains.
426
427A sound can have it's volume changed with `ma_sound_set_volume()`. If you prefer decibel volume
428control you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear.
429
430Panning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know
431a sound will never have it's pitch changed with `ma_sound_set_pitch()` or via the doppler effect,
432you can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization.
433
434By default, sounds and sound groups have spatialization enabled. If you don't ever want to
435spatialize your sounds, initialize the sound with the `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. The
436spatialization model is fairly simple and is roughly on feature parity with OpenAL. HRTF and
437environmental occlusion are not currently supported, but planned for the future. The supported
438features include:
439
440 * Sound and listener positioning and orientation with cones
441 * Attenuation models: none, inverse, linear and exponential
442 * Doppler effect
443
444Sounds can be faded in and out with `ma_sound_set_fade_in_pcm_frames()`.
445
446To check if a sound is currently playing, you can use `ma_sound_is_playing()`. To check if a sound
447is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled with
448`ma_sound_set_looping()`. Use `ma_sound_is_looping()` to check whether or not the sound is looping.
449
450
451
4522. Building
453===========
454miniaudio should work cleanly out of the box without the need to download or install any
455dependencies. See below for platform-specific details.
456
457
4582.1. Windows
459------------
460The Windows build should compile cleanly on all popular compilers without the need to configure any
461include paths nor link to any libraries.
462
463The UWP build may require linking to mmdevapi.lib if you get errors about an unresolved external
464symbol for `ActivateAudioInterfaceAsync()`.
465
466
4672.2. macOS and iOS
468------------------
469The macOS build should compile cleanly without the need to download any dependencies nor link to
470any libraries or frameworks. The iOS build needs to be compiled as Objective-C and will need to
471link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling
472through the command line requires linking to `-lpthread` and `-lm`.
473
474Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's
475notarization process. To fix this there are two options. The first is to use the
476`MA_NO_RUNTIME_LINKING` option, like so:
477
478 ```c
479 #ifdef __APPLE__
480 #define MA_NO_RUNTIME_LINKING
481 #endif
482 #define MINIAUDIO_IMPLEMENTATION
483 #include "miniaudio.h"
484 ```
485
486This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioUnit`.
487Alternatively, if you would rather keep using runtime linking you can add the following to your
488entitlements.xcent file:
489
490 ```
491 <key>com.apple.security.cs.allow-dyld-environment-variables</key>
492 <true/>
493 <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
494 <true/>
495 ```
496
497See this discussion for more info: https://github.com/mackron/miniaudio/issues/203.
498
499
5002.3. Linux
501----------
502The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any
503development packages. You may need to link with `-latomic` if you're compiling for 32-bit ARM.
504
505
5062.4. BSD
507--------
508The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses
509sndio and FreeBSD uses OSS. You may need to link with `-latomic` if you're compiling for 32-bit
510ARM.
511
512
5132.5. Android
514------------
515AAudio is the highest priority backend on Android. This should work out of the box without needing
516any kind of compiler configuration. Support for AAudio starts with Android 8 which means older
517versions will fall back to OpenSL|ES which requires API level 16+.
518
519There have been reports that the OpenSL|ES backend fails to initialize on some Android based
520devices due to `dlopen()` failing to open "libOpenSLES.so". If this happens on your platform
521you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES.
522
523
5242.6. Emscripten
525---------------
526The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box.
527You cannot use `-std=c*` compiler flags, nor `-ansi`.
528
529
5302.7. Build Options
531------------------
532`#define` these options before including miniaudio.h.
533
534 +----------------------------------+--------------------------------------------------------------------+
535 | Option | Description |
536 +----------------------------------+--------------------------------------------------------------------+
537 | MA_NO_WASAPI | Disables the WASAPI backend. |
538 +----------------------------------+--------------------------------------------------------------------+
539 | MA_NO_DSOUND | Disables the DirectSound backend. |
540 +----------------------------------+--------------------------------------------------------------------+
541 | MA_NO_WINMM | Disables the WinMM backend. |
542 +----------------------------------+--------------------------------------------------------------------+
543 | MA_NO_ALSA | Disables the ALSA backend. |
544 +----------------------------------+--------------------------------------------------------------------+
545 | MA_NO_PULSEAUDIO | Disables the PulseAudio backend. |
546 +----------------------------------+--------------------------------------------------------------------+
547 | MA_NO_JACK | Disables the JACK backend. |
548 +----------------------------------+--------------------------------------------------------------------+
549 | MA_NO_COREAUDIO | Disables the Core Audio backend. |
550 +----------------------------------+--------------------------------------------------------------------+
551 | MA_NO_SNDIO | Disables the sndio backend. |
552 +----------------------------------+--------------------------------------------------------------------+
553 | MA_NO_AUDIO4 | Disables the audio(4) backend. |
554 +----------------------------------+--------------------------------------------------------------------+
555 | MA_NO_OSS | Disables the OSS backend. |
556 +----------------------------------+--------------------------------------------------------------------+
557 | MA_NO_AAUDIO | Disables the AAudio backend. |
558 +----------------------------------+--------------------------------------------------------------------+
559 | MA_NO_OPENSL | Disables the OpenSL|ES backend. |
560 +----------------------------------+--------------------------------------------------------------------+
561 | MA_NO_WEBAUDIO | Disables the Web Audio backend. |
562 +----------------------------------+--------------------------------------------------------------------+
563 | MA_NO_NULL | Disables the null backend. |
564 +----------------------------------+--------------------------------------------------------------------+
565 | MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to |
566 | | enable specific backends. |
567 +----------------------------------+--------------------------------------------------------------------+
568 | MA_ENABLE_WASAPI | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
569 | | enable the WASAPI backend. |
570 +----------------------------------+--------------------------------------------------------------------+
571 | MA_ENABLE_DSOUND | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
572 | | enable the DirectSound backend. |
573 +----------------------------------+--------------------------------------------------------------------+
574 | MA_ENABLE_WINMM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
575 | | enable the WinMM backend. |
576 +----------------------------------+--------------------------------------------------------------------+
577 | MA_ENABLE_ALSA | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
578 | | enable the ALSA backend. |
579 +----------------------------------+--------------------------------------------------------------------+
580 | MA_ENABLE_PULSEAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
581 | | enable the PulseAudio backend. |
582 +----------------------------------+--------------------------------------------------------------------+
583 | MA_ENABLE_JACK | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
584 | | enable the JACK backend. |
585 +----------------------------------+--------------------------------------------------------------------+
586 | MA_ENABLE_COREAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
587 | | enable the Core Audio backend. |
588 +----------------------------------+--------------------------------------------------------------------+
589 | MA_ENABLE_SNDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
590 | | enable the sndio backend. |
591 +----------------------------------+--------------------------------------------------------------------+
592 | MA_ENABLE_AUDIO4 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
593 | | enable the audio(4) backend. |
594 +----------------------------------+--------------------------------------------------------------------+
595 | MA_ENABLE_OSS | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
596 | | enable the OSS backend. |
597 +----------------------------------+--------------------------------------------------------------------+
598 | MA_ENABLE_AAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
599 | | enable the AAudio backend. |
600 +----------------------------------+--------------------------------------------------------------------+
601 | MA_ENABLE_OPENSL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
602 | | enable the OpenSL|ES backend. |
603 +----------------------------------+--------------------------------------------------------------------+
604 | MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
605 | | enable the Web Audio backend. |
606 +----------------------------------+--------------------------------------------------------------------+
607 | MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
608 | | enable the null backend. |
609 +----------------------------------+--------------------------------------------------------------------+
610 | MA_NO_DECODING | Disables decoding APIs. |
611 +----------------------------------+--------------------------------------------------------------------+
612 | MA_NO_ENCODING | Disables encoding APIs. |
613 +----------------------------------+--------------------------------------------------------------------+
614 | MA_NO_WAV | Disables the built-in WAV decoder and encoder. |
615 +----------------------------------+--------------------------------------------------------------------+
616 | MA_NO_FLAC | Disables the built-in FLAC decoder. |
617 +----------------------------------+--------------------------------------------------------------------+
618 | MA_NO_MP3 | Disables the built-in MP3 decoder. |
619 +----------------------------------+--------------------------------------------------------------------+
620 | MA_NO_DEVICE_IO | Disables playback and recording. This will disable `ma_context` |
621 | | and `ma_device` APIs. This is useful if you only want to use |
622 | | miniaudio's data conversion and/or decoding APIs. |
623 +----------------------------------+--------------------------------------------------------------------+
624 | MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and |
625 | | `ma_event` APIs. This option is useful if you only need to use |
626 | | miniaudio for data conversion, decoding and/or encoding. Some |
627 | | families of APIsrequire threading which means the following |
628 | | options must also be set: |
629 | | |
630 | | ``` |
631 | | MA_NO_DEVICE_IO |
632 | | ``` |
633 +----------------------------------+--------------------------------------------------------------------+
634 | MA_NO_GENERATION | Disables generation APIs such a `ma_waveform` and `ma_noise`. |
635 +----------------------------------+--------------------------------------------------------------------+
636 | MA_NO_SSE2 | Disables SSE2 optimizations. |
637 +----------------------------------+--------------------------------------------------------------------+
638 | MA_NO_AVX2 | Disables AVX2 optimizations. |
639 +----------------------------------+--------------------------------------------------------------------+
640 | MA_NO_NEON | Disables NEON optimizations. |
641 +----------------------------------+--------------------------------------------------------------------+
642 | MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's |
643 | | notarization process. When enabling this, you may need to avoid |
644 | | using `-std=c89` or `-std=c99` on Linux builds or else you may end |
645 | | up with compilation errors due to conflicts with `timespec` and |
646 | | `timeval` data types. |
647 | | |
648 | | You may need to enable this if your target platform does not allow |
649 | | runtime linking via `dlopen()`. |
650 +----------------------------------+--------------------------------------------------------------------+
651 | MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). |
652 +----------------------------------+--------------------------------------------------------------------+
653 | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to |
654 | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. |
655 +----------------------------------+--------------------------------------------------------------------+
656 | MA_API | Controls how public APIs should be decorated. Default is `extern`. |
657 +----------------------------------+--------------------------------------------------------------------+
658
659
6603. Definitions
661==============
662This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity
663in the use of terms throughout the audio space, so this section is intended to clarify how miniaudio
664uses each term.
665
6663.1. Sample
667-----------
668A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit
669floating point number.
670
6713.2. Frame / PCM Frame
672----------------------
673A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2
674samples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame"
675and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame.
676If ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always
677clarify what it's referring to with something like "FLAC frame".
678
6793.3. Channel
680------------
681A stream of monaural audio that is emitted from an individual speaker in a speaker system, or
682received from an individual microphone in a microphone system. A stereo stream has two channels (a
683left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio
684systems refer to a channel as a complex audio stream that's mixed with other channels to produce
685the final mix - this is completely different to miniaudio's use of the term "channel" and should
686not be confused.
687
6883.4. Sample Rate
689----------------
690The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number
691of PCM frames that are processed per second.
692
6933.5. Formats
694------------
695Throughout miniaudio you will see references to different sample formats:
696
697 +---------------+----------------------------------------+---------------------------+
698 | Symbol | Description | Range |
699 +---------------+----------------------------------------+---------------------------+
700 | ma_format_f32 | 32-bit floating point | [-1, 1] |
701 | ma_format_s16 | 16-bit signed integer | [-32768, 32767] |
702 | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] |
703 | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] |
704 | ma_format_u8 | 8-bit unsigned integer | [0, 255] |
705 +---------------+----------------------------------------+---------------------------+
706
707All formats are native-endian.
708
709
710
7114. Data Sources
712===============
713The data source abstraction in miniaudio is used for retrieving audio data from some source. A few
714examples include `ma_decoder`, `ma_noise` and `ma_waveform`. You will need to be familiar with data
715sources in order to make sense of some of the higher level concepts in miniaudio.
716
717The `ma_data_source` API is a generic interface for reading from a data source. Any object that
718implements the data source interface can be plugged into any `ma_data_source` function.
719
720To read data from a data source:
721
722 ```c
723 ma_result result;
724 ma_uint64 framesRead;
725
726 result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead, loop);
727 if (result != MA_SUCCESS) {
728 return result; // Failed to read data from the data source.
729 }
730 ```
731
732If you don't need the number of frames that were successfully read you can pass in `NULL` to the
733`pFramesRead` parameter. If this returns a value less than the number of frames requested it means
734the end of the file has been reached. `MA_AT_END` will be returned only when the number of frames
735read is 0.
736
737When calling any data source function, with the exception of `ma_data_source_init()` and
738`ma_data_source_uninit()`, you can pass in any object that implements a data source. For example,
739you could plug in a decoder like so:
740
741 ```c
742 ma_result result;
743 ma_uint64 framesRead;
744 ma_decoder decoder; // <-- This would be initialized with `ma_decoder_init_*()`.
745
746 result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead, loop);
747 if (result != MA_SUCCESS) {
748 return result; // Failed to read data from the decoder.
749 }
750 ```
751
752If you want to seek forward you can pass in `NULL` to the `pFramesOut` parameter. Alternatively you
753can use `ma_data_source_seek_pcm_frames()`.
754
755To seek to a specific PCM frame:
756
757 ```c
758 result = ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex);
759 if (result != MA_SUCCESS) {
760 return result; // Failed to seek to PCM frame.
761 }
762 ```
763
764You can retrieve the total length of a data source in PCM frames, but note that some data sources
765may not have the notion of a length, such as noise and waveforms, and others may just not have a
766way of determining the length such as some decoders. To retrieve the length:
767
768 ```c
769 ma_uint64 length;
770
771 result = ma_data_source_get_length_in_pcm_frames(pDataSource, &length);
772 if (result != MA_SUCCESS) {
773 return result; // Failed to retrieve the length.
774 }
775 ```
776
777Care should be taken when retrieving the length of a data source where the underlying decoder is
778pulling data from a data stream with an undefined length, such as internet radio or some kind of
779broadcast. If you do this, `ma_data_source_get_length_in_pcm_frames()` may never return.
780
781The current position of the cursor in PCM frames can also be retrieved:
782
783 ```c
784 ma_uint64 cursor;
785
786 result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor);
787 if (result != MA_SUCCESS) {
788 return result; // Failed to retrieve the cursor.
789 }
790 ```
791
792You will often need to know the data format that will be returned after reading. This can be
793retrieved like so:
794
795 ```c
796 ma_format format;
797 ma_uint32 channels;
798 ma_uint32 sampleRate;
799 ma_channel channelMap[MA_MAX_CHANNELS];
800
801 result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS);
802 if (result != MA_SUCCESS) {
803 return result; // Failed to retrieve data format.
804 }
805 ```
806
807If you do not need a specific data format property, just pass in NULL to the respective parameter.
808
809There may be cases where you want to implement something like a sound bank where you only want to
810read data within a certain range of the underlying data. To do this you can use a range:
811
812 ```c
813 result = ma_data_source_set_range_in_pcm_frames(pDataSource, rangeBegInFrames, rangeEndInFrames);
814 if (result != MA_SUCCESS) {
815 return result; // Failed to set the range.
816 }
817 ```
818
819This is useful if you have a sound bank where many sounds are stored in the same file and you want
820the data source to only play one of those sub-sounds.
821
822Custom loop points can also be used with data sources. By default, data sources will loop after
823they reach the end of the data source, but if you need to loop at a specific location, you can do
824the following:
825
826 ```c
827 result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames);
828 if (result != MA_SUCCESS) {
829 return result; // Failed to set the loop point.
830 }
831 ```
832
833The loop point is relative to the current range.
834
835It's sometimes useful to chain data sources together so that a seamless transition can be achieved.
836To do this, you can use chaining:
837
838 ```c
839 ma_decoder decoder1;
840 ma_decoder decoder2;
841
842 // ... initialize decoders with ma_decoder_init_*() ...
843
844 result = ma_data_source_set_next(&decoder1, &decoder2);
845 if (result != MA_SUCCESS) {
846 return result; // Failed to set the next data source.
847 }
848
849 result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead, MA_FALSE);
850 if (result != MA_SUCCESS) {
851 return result; // Failed to read from the decoder.
852 }
853 ```
854
855In the example above we're using decoders. When reading from a chain, you always want to read from
856the top level data source in the chain. In the example above, `decoder1` is the top level data
857source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any
858gaps.
859
860Note that the `loop` parameter is set to false in the example above. When this is set to true, only
861the current data source will be looped. You can loop the entire chain by linking in a loop like so:
862
863 ```c
864 ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2
865 ma_data_source_set_next(&decoder2, &decoder1); // decoder2 -> decoder1 (loop back to the start).
866 ```
867
868Note that setting up chaining is not thread safe, so care needs to be taken if you're dynamically
869changing links while the audio thread is in the middle of reading.
870
871Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple
872instances of the same sound simultaneously. Instead, initialize multiple data sources for each
873instance. This can be extremely inefficient depending on the data source and can result in
874glitching due to subtle changes to the state of internal filters.
875
876
8774.1. Custom Data Sources
878------------------------
879You can implement a custom data source by implementing the functions in `ma_data_source_vtable`.
880Your custom object must have `ma_data_source_base` as it's first member:
881
882 ```c
883 struct my_data_source
884 {
885 ma_data_source_base base;
886 ...
887 };
888 ```
889
890In your initialization routine, you need to call `ma_data_source_init()` in order to set up the
891base object (`ma_data_source_base`):
892
893 ```c
894 static ma_result my_data_source_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
895 {
896 // Read data here. Output in the same format returned by my_data_source_get_data_format().
897 }
898
899 static ma_result my_data_source_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
900 {
901 // Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported.
902 }
903
904 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)
905 {
906 // Return the format of the data here.
907 }
908
909 static ma_result my_data_source_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
910 {
911 // 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.
912 }
913
914 static ma_result my_data_source_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
915 {
916 // 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.
917 }
918
919 static g_my_data_source_vtable =
920 {
921 my_data_source_read,
922 my_data_source_seek,
923 my_data_source_get_data_format,
924 my_data_source_get_cursor,
925 my_data_source_get_length
926 };
927
928 ma_result my_data_source_init(my_data_source* pMyDataSource)
929 {
930 ma_result result;
931 ma_data_source_config baseConfig;
932
933 baseConfig = ma_data_source_config_init();
934 baseConfig.vtable = &g_my_data_source_vtable;
935
936 result = ma_data_source_init(&baseConfig, &pMyDataSource->base);
937 if (result != MA_SUCCESS) {
938 return result;
939 }
940
941 // ... do the initialization of your custom data source here ...
942
943 return MA_SUCCESS;
944 }
945
946 void my_data_source_uninit(my_data_source* pMyDataSource)
947 {
948 // ... do the uninitialization of your custom data source here ...
949
950 // You must uninitialize the base data source.
951 ma_data_source_uninit(&pMyDataSource->base);
952 }
953 ```
954
955Note that `ma_data_source_init()` and `ma_data_source_uninit()` are never called directly outside
956of the custom data source. It's up to the custom data source itself to call these within their own
957init/uninit functions.
958
959
960
9615. Engine
962=========
963The `ma_engine` API is a high level API for managing and mixing sounds and effect processing. The
964`ma_engine` object encapsulates a resource manager and a node graph, both of which will be
965explained in more detail later.
966
967Sounds are called `ma_sound` and are created from an engine. Sounds can be associated with a mixing
968group called `ma_sound_group` which are also created from the engine. Both `ma_sound` and
969`ma_sound_group` objects are nodes within the engine's node graph.
970
971When the engine is initialized, it will normally create a device internally. If you would rather
972manage the device yourself, you can do so and just pass a pointer to it via the engine config when
973you initialize the engine. You can also just use the engine without a device, which again can be
974configured via the engine config.
975
976The most basic way to initialize the engine is with a default config, like so:
977
978 ```c
979 ma_result result;
980 ma_engine engine;
981
982 result = ma_engine_init(NULL, &engine);
983 if (result != MA_SUCCESS) {
984 return result; // Failed to initialize the engine.
985 }
986 ```
987
988This will result in the engine initializing a playback device using the operating system's default
989device. This will be sufficient for many use cases, but if you need more flexibility you'll want to
990configure the engine with an engine config:
991
992 ```c
993 ma_result result;
994 ma_engine engine;
995 ma_engine_config engineConfig;
996
997 engineConfig = ma_engine_config_init();
998 engineConfig.pPlaybackDevice = &myDevice;
999
1000 result = ma_engine_init(&engineConfig, &engine);
1001 if (result != MA_SUCCESS) {
1002 return result; // Failed to initialize the engine.
1003 }
1004 ```
1005
1006In the example above we're passing in a pre-initialized device. Since the caller is the one in
1007control of the device's data callback, it's their responsibility to manually call
1008`ma_engine_read_pcm_frames()` from inside their data callback:
1009
1010 ```c
1011 void playback_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
1012 {
1013 ma_engine_read_pcm_frames(&g_Engine, pOutput, frameCount, NULL);
1014 }
1015 ```
1016
1017You can also use the engine independent of a device entirely:
1018
1019 ```c
1020 ma_result result;
1021 ma_engine engine;
1022 ma_engine_config engineConfig;
1023
1024 engineConfig = ma_engine_config_init();
1025 engineConfig.noDevice = MA_TRUE;
1026 engineConfig.channels = 2; // Must be set when not using a device.
1027 engineConfig.sampleRate = 48000; // Must be set when not using a device.
1028
1029 result = ma_engine_init(&engineConfig, &engine);
1030 if (result != MA_SUCCESS) {
1031 return result; // Failed to initialize the engine.
1032 }
1033 ```
1034
1035Note that when you're not using a device, you must set the channel count and sample rate in the
1036config or else miniaudio won't know what to use (miniaudio will use the device to determine this
1037normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio
1038data from the engine. This kind of setup is useful if you want to do something like offline
1039processing.
1040
1041When a sound is loaded it goes through a resource manager. By default the engine will initialize a
1042resource manager internally, but you can also specify a pre-initialized resource manager:
1043
1044 ```c
1045 ma_result result;
1046 ma_engine engine1;
1047 ma_engine engine2;
1048 ma_engine_config engineConfig;
1049
1050 engineConfig = ma_engine_config_init();
1051 engineConfig.pResourceManager = &myResourceManager;
1052
1053 ma_engine_init(&engineConfig, &engine1);
1054 ma_engine_init(&engineConfig, &engine2);
1055 ```
1056
1057In this example we are initializing two engines, both of which are sharing the same resource
1058manager. This is especially useful for saving memory when loading the same file across multiple
1059engines. If you were not to use a shared resource manager, each engine instance would use their own
1060which would result in any sounds that are used between both engine's being loaded twice. By using
1061a shared resource manager, it would only be loaded once. Using multiple engine's is useful when you
1062need to output to multiple playback devices, such as in a local multiplayer game where each player
1063is using their own set of headphones.
1064
1065By default an engine will be in a started state. To make it so the engine is not automatically
1066started you can configure it as such:
1067
1068 ```c
1069 engineConfig.noAutoStart = MA_TRUE;
1070
1071 // The engine will need to be started manually.
1072 ma_engine_start(&engine);
1073
1074 // Later on the engine can be stopped with ma_engine_stop().
1075 ma_engine_stop(&engine);
1076 ```
1077
1078The concept of starting or stopping an engine is only relevant when using the engine with a
1079device. Attempting to start or stop an engine that is not associated with a device will result in
1080`MA_INVALID_OPERATION`.
1081
1082The master volume of the engine can be controlled with `ma_engine_set_volume()` which takes a
1083linear scale, with 0 resulting in silence and anything above 1 resulting in amplification. If you
1084prefer decibel based volume control, use `ma_volume_db_to_linear()` to convert from dB to linear.
1085
1086When a sound is spatialized, it is done so relative to a listener. An engine can be configured to
1087have multiple listeners which can be configured via the config:
1088
1089 ```c
1090 engineConfig.listenerCount = 2;
1091 ```
1092
1093The maximum number of listeners is restricted to `MA_ENGINE_MAX_LISTENERS`. By default, when a
1094sound is spatialized, it will be done so relative to the closest listener. You can also pin a sound
1095to a specific listener which will be explained later. Listener's have a position, direction, cone,
1096and velocity (for doppler effect). A listener is referenced by an index, the meaning of which is up
1097to the caller (the index is 0 based and cannot go beyond the listener count, minus 1). The
1098position, direction and velocity are all specified in absolute terms:
1099
1100 ```c
1101 ma_engine_listener_set_position(&engine, listenerIndex, worldPosX, worldPosY, worldPosZ);
1102 ```
1103
1104The direction of the listener represents it's forward vector. The listener's up vector can also be
1105specified and defaults to +1 on the Y axis.
1106
1107 ```c
1108 ma_engine_listener_set_direction(&engine, listenerIndex, forwardX, forwardY, forwardZ);
1109 ma_engine_listener_set_world_up(&engine, listenerIndex, 0, 1, 0);
1110 ```
1111
1112The engine supports directional attenuation. The listener can have a cone the controls how sound is
1113attenuated based on the listener's direction. When a sound is between the inner and outer cones, it
1114will be attenuated between 1 and the cone's outer gain:
1115
1116 ```c
1117 ma_engine_listener_set_cone(&engine, listenerIndex, innerAngleInRadians, outerAngleInRadians, outerGain);
1118 ```
1119
1120When a sound is inside the inner code, no directional attenuation is applied. When the sound is
1121outside of the outer cone, the attenuation will be set to `outerGain` in the example above. When
1122the sound is in between the inner and outer cones, the attenuation will be interpolated between 1
1123and the outer gain.
1124
1125The engine's coordinate system follows the OpenGL coordinate system where positive X points right,
1126positive Y points up and negative Z points forward.
1127
1128The simplest and least flexible way to play a sound is like so:
1129
1130 ```c
1131 ma_engine_play_sound(&engine, "my_sound.wav", pGroup);
1132 ```
1133
1134This is a "fire and forget" style of function. The engine will manage the `ma_sound` object
1135internally. When the sound finishes playing, it'll be put up for recycling. For more flexibility
1136you'll want to initialize a sound object:
1137
1138 ```c
1139 ma_sound sound;
1140
1141 result = ma_sound_init_from_file(&engine, "my_sound.wav", flags, pGroup, NULL, &sound);
1142 if (result != MA_SUCCESS) {
1143 return result; // Failed to load sound.
1144 }
1145 ```
1146
1147Sounds need to be uninitialized with `ma_sound_uninit()`.
1148
1149The example above loads a sound from a file. If the resource manager has been disabled you will not
1150be able to use this function and instead you'll need to initialize a sound directly from a data
1151source:
1152
1153 ```c
1154 ma_sound sound;
1155
1156 result = ma_sound_init_from_data_source(&engine, &dataSource, flags, pGroup, &sound);
1157 if (result != MA_SUCCESS) {
1158 return result;
1159 }
1160 ```
1161
1162Each `ma_sound` object represents a single instance of the sound. If you want to play the same
1163sound multiple times at the same time, you need to initialize a separate `ma_sound` object.
1164
1165For the most flexibility when initializing sounds, use `ma_sound_init_ex()`. This uses miniaudio's
1166standard config/init pattern:
1167
1168 ```c
1169 ma_sound sound;
1170 ma_sound_config soundConfig;
1171
1172 soundConfig = ma_sound_config_init();
1173 soundConfig.pFilePath = NULL; // Set this to load from a file path.
1174 soundConfig.pDataSource = NULL; // Set this to initialize from an existing data source.
1175 soundConfig.pInitialAttachment = &someNodeInTheNodeGraph;
1176 soundConfig.initialAttachmentInputBusIndex = 0;
1177 soundConfig.channelsIn = 1;
1178 soundConfig.channelsOut = 0; // Set to 0 to use the engine's native channel count.
1179
1180 result = ma_sound_init_ex(&soundConfig, &sound);
1181 if (result != MA_SUCCESS) {
1182 return result;
1183 }
1184 ```
1185
1186In the example above, the sound is being initialized without a file nor a data source. This is
1187valid, in which case the sound acts as a node in the middle of the node graph. This means you can
1188connect other sounds to this sound and allow it to act like a sound group. Indeed, this is exactly
1189what a `ma_sound_group` is.
1190
1191When loading a sound, you specify a set of flags that control how the sound is loaded and what
1192features are enabled for that sound. When no flags are set, the sound will be fully loaded into
1193memory in exactly the same format as how it's stored on the file system. The resource manager will
1194allocate a block of memory and then load the file directly into it. When reading audio data, it
1195will be decoded dynamically on the fly. In order to save processing time on the audio thread, it
1196might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_FLAG_DECODE` flag:
1197
1198 ```c
1199 ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE, pGroup, NULL, &sound);
1200 ```
1201
1202By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until
1203the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously
1204by specificying the `MA_SOUND_FLAG_ASYNC` flag:
1205
1206 ```c
1207 ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound);
1208 ```
1209
1210This will result in `ma_sound_init_*()` returning quickly, but the sound won't yet have been fully
1211loaded. When you start the sound, it won't output anything until some sound is available. The sound
1212will start outputting audio before the sound has been fully decoded when the `MA_SOUND_FLAG_DECODE`
1213is specified.
1214
1215If you need to wait for an asynchronously loaded sound to be fully loaded, you can use a fence. A
1216fence in miniaudio is a simple synchronization mechanism which simply blocks until it's internal
1217counter hit's zero. You can specify a fence like so:
1218
1219 ```c
1220 ma_result result;
1221 ma_fence fence;
1222 ma_sound sounds[4];
1223
1224 result = ma_fence_init(&fence);
1225 if (result != MA_SUCCES) {
1226 return result;
1227 }
1228
1229 // Load some sounds asynchronously.
1230 for (int iSound = 0; iSound < 4; iSound += 1) {
1231 ma_sound_init_from_file(&engine, mySoundFilesPaths[iSound], MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, &fence, &sounds[iSound]);
1232 }
1233
1234 // ... do some other stuff here in the mean time ...
1235
1236 // Wait for all sounds to finish loading.
1237 ma_fence_wait(&fence);
1238 ```
1239
1240If loading the entire sound into memory is prohibitive, you can also configure the engine to stream
1241the audio data:
1242
1243 ```c
1244 ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_STREAM, pGroup, NULL, &sound);
1245 ```
1246
1247When streaming sounds, 2 seconds worth of audio data is stored in memory. Although it should work
1248fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music
1249tracks in games.
1250
1251When you initialize a sound, if you specify a sound group the sound will be attached to that group
1252automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint.
1253If you would instead rather leave the sound unattached by default, you can can specify the
1254`MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node
1255graph.
1256
1257Sounds are not started by default. To start a sound, use `ma_sound_start()`. Stop a sound with
1258`ma_sound_stop()`.
1259
1260Sounds can have their volume controlled with `ma_sound_set_volume()` in the same way as the
1261engine's master volume.
1262
1263Sounds support stereo panning and pitching. Set the pan with `ma_sound_set_pan()`. Setting the pan
1264to 0 will result in an unpanned sound. Setting it to -1 will shift everything to the left, whereas
1265+1 will shift it to the right. The pitch can be controlled with `ma_sound_set_pitch()`. A larger
1266value will result in a higher pitch. The pitch must be greater than 0.
1267
1268The engine supports 3D spatialization of sounds. By default sounds will have spatialization
1269enabled, but if a sound does not need to be spatialized it's best to disable it. There are two ways
1270to disable spatialization of a sound:
1271
1272 ```c
1273 // Disable spatialization at initialization time via a flag:
1274 ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, NULL, &sound);
1275
1276 // Dynamically disable or enable spatialization post-initialization:
1277 ma_sound_set_spatialization_enabled(&sound, isSpatializationEnabled);
1278 ```
1279
1280By default sounds will be spatialized based on the closest listener. If a sound should always be
1281spatialized relative to a specific listener it can be pinned to one:
1282
1283 ```c
1284 ma_sound_set_pinned_listener_index(&sound, listenerIndex);
1285 ```
1286
1287Like listeners, sounds have a position. By default, the position of a sound is in absolute space,
1288but it can be changed to be relative to a listener:
1289
1290 ```c
1291 ma_sound_set_positioning(&sound, ma_positioning_relative);
1292 ```
1293
1294Note that relative positioning of a sound only makes sense if there is either only one listener, or
1295the sound is pinned to a specific listener. To set the position of a sound:
1296
1297 ```c
1298 ma_sound_set_position(&sound, posX, posY, posZ);
1299 ```
1300
1301The direction works the same way as a listener and represents the sound's forward direction:
1302
1303 ```c
1304 ma_sound_set_direction(&sound, forwardX, forwardY, forwardZ);
1305 ```
1306
1307Sound's also have a cone for controlling directional attenuation. This works exactly the same as
1308listeners:
1309
1310 ```c
1311 ma_sound_set_cone(&sound, innerAngleInRadians, outerAngleInRadians, outerGain);
1312 ```
1313
1314The velocity of a sound is used for doppler effect and can be set as such:
1315
1316 ```c
1317 ma_sound_set_velocity(&sound, velocityX, velocityY, velocityZ);
1318 ```
1319
1320The engine supports different attenuation models which can be configured on a per-sound basis. By
1321default the attenuation model is set to `ma_attenuation_model_inverse` which is the equivalent to
1322OpenAL's `AL_INVERSE_DISTANCE_CLAMPED`. Configure the attenuation model like so:
1323
1324 ```c
1325 ma_sound_set_attenuation_model(&sound, ma_attenuation_model_inverse);
1326 ```
1327
1328The supported attenuation models include the following:
1329
1330 +----------------------------------+----------------------------------------------+
1331 | ma_attenuation_model_none | No distance attenuation. |
1332 +----------------------------------+----------------------------------------------+
1333 | ma_attenuation_model_inverse | Equivalent to `AL_INVERSE_DISTANCE_CLAMPED`. |
1334 +----------------------------------+----------------------------------------------+
1335 | ma_attenuation_model_linear | Linear attenuation. |
1336 +----------------------------------+----------------------------------------------+
1337 | ma_attenuation_model_exponential | Exponential attenuation. |
1338 +----------------------------------+----------------------------------------------+
1339
1340To control how quickly a sound rolls off as it moves away from the listener, you need to configure
1341the rolloff:
1342
1343 ```c
1344 ma_sound_set_rolloff(&sound, rolloff);
1345 ```
1346
1347You can control the minimum and maximum gain to apply from spatialization:
1348
1349 ```c
1350 ma_sound_set_min_gain(&sound, minGain);
1351 ma_sound_set_max_gain(&sound, maxGain);
1352 ```
1353
1354Likewise, in the calculation of attenuation, you can control the minimum and maximum distances for
1355the attenuation calculation. This is useful if you want to ensure sounds don't drop below a certain
1356volume after the listener moves further away and to have sounds play a maximum volume when the
1357listener is within a certain distance:
1358
1359 ```c
1360 ma_sound_set_min_distance(&sound, minDistance);
1361 ma_sound_set_max_distance(&sound, maxDistance);
1362 ```
1363
1364The engine's spatialization system supports doppler effect. The doppler factor can be configure on
1365a per-sound basis like so:
1366
1367 ```c
1368 ma_sound_set_doppler_factor(&sound, dopplerFactor);
1369 ```
1370
1371You can fade sounds in and out with `ma_sound_set_fade_in_pcm_frames()` and
1372`ma_sound_set_fade_in_milliseconds()`. Set the volume to -1 to use the current volume as the
1373starting volume:
1374
1375 ```c
1376 // Fade in over 1 second.
1377 ma_sound_set_fade_in_milliseconds(&sound, 0, 1, 1000);
1378
1379 // ... sometime later ...
1380
1381 // Fade out over 1 second, starting from the current volume.
1382 ma_sound_set_fade_in_milliseconds(&sound, -1, 0, 1000);
1383 ```
1384
1385By default sounds will start immediately, but sometimes for timing and synchronization purposes it
1386can be useful to schedule a sound to start or stop:
1387
1388 ```c
1389 // Start the sound in 1 second from now.
1390 ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 1));
1391
1392 // Stop the sound in 2 seconds from now.
1393 ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2));
1394 ```
1395
1396The time is specified in global time which is controlled by the engine. You can get the engine's
1397current time with `ma_engine_get_time()`. The engine's global time is incremented automatically as
1398audio data is read, but it can be reset with `ma_engine_set_time()` in case it needs to be
1399resynchronized for some reason.
1400
1401To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will
1402take the scheduled start and stop times into account.
1403
1404Whether or not a sound should loop can be controlled with `ma_sound_set_looping()`. Sounds will not
1405be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping.
1406
1407Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping
1408sound this should never return true.
1409
1410Internally a sound wraps around a data source. Some APIs exist to control the underlying data
1411source, mainly for convenience:
1412
1413 ```c
1414 ma_sound_seek_to_pcm_frame(&sound, frameIndex);
1415 ma_sound_get_data_format(&sound, &format, &channels, &sampleRate, pChannelMap, channelMapCapacity);
1416 ma_sound_get_cursor_in_pcm_frames(&sound, &cursor);
1417 ma_sound_get_length_in_pcm_frames(&sound, &length);
1418 ```
1419
1420Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do
1421not have any notion of a data source, anything relating to a data source is unavailable.
1422
1423Internally, sound data is loaded via the `ma_decoder` API which means by default in only supports
1424file formats that have built-in support in miniaudio. You can extend this to support any kind of
1425file format through the use of custom decoders. To do this you'll need to use a self-managed
1426resource manager and configure it appropriately. See the "Resource Management" section below for
1427details on how to set this up.
1428
1429
14306. Resource Management
1431======================
1432Many programs will want to manage sound resources for things such as reference counting and
1433streaming. This is supported by miniaudio via the `ma_resource_manager` API.
1434
1435The resource manager is mainly responsible for the following:
1436
1437 * Loading of sound files into memory with reference counting.
1438 * Streaming of sound data
1439
1440When loading a sound file, the resource manager will give you back a `ma_data_source` compatible
1441object called `ma_resource_manager_data_source`. This object can be passed into any
1442`ma_data_source` API which is how you can read and seek audio data. When loading a sound file, you
1443specify whether or not you want the sound to be fully loaded into memory (and optionally
1444pre-decoded) or streamed. When loading into memory, you can also specify whether or not you want
1445the data to be loaded asynchronously.
1446
1447The example below is how you can initialize a resource manager using it's default configuration:
1448
1449 ```c
1450 ma_resource_manager_config config;
1451 ma_resource_manager resourceManager;
1452
1453 config = ma_resource_manager_config_init();
1454 result = ma_resource_manager_init(&config, &resourceManager);
1455 if (result != MA_SUCCESS) {
1456 ma_device_uninit(&device);
1457 printf("Failed to initialize the resource manager.");
1458 return -1;
1459 }
1460 ```
1461
1462You can configure the format, channels and sample rate of the decoded audio data. By default it
1463will use the file's native data format, but you can configure it to use a consistent format. This
1464is useful for offloading the cost of data conversion to load time rather than dynamically
1465converting at mixing time. To do this, you configure the decoded format, channels and sample rate
1466like the code below:
1467
1468 ```c
1469 config = ma_resource_manager_config_init();
1470 config.decodedFormat = device.playback.format;
1471 config.decodedChannels = device.playback.channels;
1472 config.decodedSampleRate = device.sampleRate;
1473 ```
1474
1475In the code above, the resource manager will be configured so that any decoded audio data will be
1476pre-converted at load time to the device's native data format. If instead you used defaults and
1477the data format of the file did not match the device's data format, you would need to convert the
1478data at mixing time which may be prohibitive in high-performance and large scale scenarios like
1479games.
1480
1481Internally the resource manager uses the `ma_decoder` API to load sounds. This means by default it
1482only supports decoders that are built into miniaudio. It's possible to support additional encoding
1483formats through the use of custom decoders. To do so, pass in your `ma_decoding_backend_vtable`
1484vtables into the resource manager config:
1485
1486 ```c
1487 ma_decoding_backend_vtable* pCustomBackendVTables[] =
1488 {
1489 &g_ma_decoding_backend_vtable_libvorbis,
1490 &g_ma_decoding_backend_vtable_libopus
1491 };
1492
1493 ...
1494
1495 resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables;
1496 resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);
1497 resourceManagerConfig.pCustomDecodingBackendUserData = NULL;
1498 ```
1499
1500This system can allow you to support any kind of file format. See the "Decoding" section for
1501details on how to implement custom decoders. The miniaudio repository includes examples for Opus
1502via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile.
1503
1504Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the
1505decoding of a page, a job will be posted to a queue which will then be processed by a job thread.
1506By default there will be only one job thread running, but this can be configured, like so:
1507
1508 ```c
1509 config = ma_resource_manager_config_init();
1510 config.jobThreadCount = MY_JOB_THREAD_COUNT;
1511 ```
1512
1513By default job threads are managed internally by the resource manager, however you can also self
1514manage your job threads if, for example, you want to integrate the job processing into your
1515existing job infrastructure, or if you simply don't like the way the resource manager does it. To
1516do this, just set the job thread count to 0 and process jobs manually. To process jobs, you first
1517need to retrieve a job using `ma_resource_manager_next_job()` and then process it using
1518`ma_job_process()`:
1519
1520 ```c
1521 config = ma_resource_manager_config_init();
1522 config.jobThreadCount = 0; // Don't manage any job threads internally.
1523 config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; // Optional. Makes `ma_resource_manager_next_job()` non-blocking.
1524
1525 // ... Initialize your custom job threads ...
1526
1527 void my_custom_job_thread(...)
1528 {
1529 for (;;) {
1530 ma_job job;
1531 ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job);
1532 if (result != MA_SUCCESS) {
1533 if (result == MA_NOT_DATA_AVAILABLE) {
1534 // No jobs are available. Keep going. Will only get this if the resource manager was initialized
1535 // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING.
1536 continue;
1537 } else if (result == MA_CANCELLED) {
1538 // MA_JOB_TYPE_QUIT was posted. Exit.
1539 break;
1540 } else {
1541 // Some other error occurred.
1542 break;
1543 }
1544 }
1545
1546 ma_job_process(&job);
1547 }
1548 }
1549 ```
1550
1551In the example above, the `MA_JOB_TYPE_QUIT` event is the used as the termination
1552indicator, but you can use whatever you would like to terminate the thread. The call to
1553`ma_resource_manager_next_job()` is blocking by default, but can be configured to be non-blocking
1554by initializing the resource manager with the `MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING` configuration
1555flag. Note that the `MA_JOB_TYPE_QUIT` will never be removed from the job queue. This
1556is to give every thread the opportunity to catch the event and terminate naturally.
1557
1558When loading a file, it's sometimes convenient to be able to customize how files are opened and
1559read instead of using standard `fopen()`, `fclose()`, etc. which is what miniaudio will use by
1560default. This can be done by setting `pVFS` member of the resource manager's config:
1561
1562 ```c
1563 // Initialize your custom VFS object. See documentation for VFS for information on how to do this.
1564 my_custom_vfs vfs = my_custom_vfs_init();
1565
1566 config = ma_resource_manager_config_init();
1567 config.pVFS = &vfs;
1568 ```
1569
1570This is particularly useful in programs like games where you want to read straight from an archive
1571rather than the normal file system. If you do not specify a custom VFS, the resource manager will
1572use the operating system's normal file operations. This is default.
1573
1574To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When
1575loading a sound you need to specify the file path and options for how the sounds should be loaded.
1576By default a sound will be loaded synchronously. The returned data source is owned by the caller
1577which means the caller is responsible for the allocation and freeing of the data source. Below is
1578an example for initializing a data source:
1579
1580 ```c
1581 ma_resource_manager_data_source dataSource;
1582 ma_result result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource);
1583 if (result != MA_SUCCESS) {
1584 // Error.
1585 }
1586
1587 // ...
1588
1589 // A ma_resource_manager_data_source object is compatible with the `ma_data_source` API. To read data, just call
1590 // the `ma_data_source_read_pcm_frames()` like you would with any normal data source.
1591 result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead);
1592 if (result != MA_SUCCESS) {
1593 // Failed to read PCM frames.
1594 }
1595
1596 // ...
1597
1598 ma_resource_manager_data_source_uninit(pResourceManager, &dataSource);
1599 ```
1600
1601The `flags` parameter specifies how you want to perform loading of the sound file. It can be a
1602combination of the following flags:
1603
1604 ```
1605 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM
1606 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE
1607 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC
1608 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT
1609 ```
1610
1611When no flags are specified (set to 0), the sound will be fully loaded into memory, but not
1612decoded, meaning the raw file data will be stored in memory, and then dynamically decoded when
1613`ma_data_source_read_pcm_frames()` is called. To instead decode the audio data before storing it in
1614memory, use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` flag. By default, the sound file will
1615be loaded synchronously, meaning `ma_resource_manager_data_source_init()` will only return after
1616the entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You
1617can instead load the sound asynchronously using the `MA_RESOURCE_MANAGER_DATA_SOURCE_ASYNC` flag.
1618This will result in `ma_resource_manager_data_source_init()` returning quickly, but no data will be
1619returned by `ma_data_source_read_pcm_frames()` until some data is available. When no data is
1620available because the asynchronous decoding hasn't caught up, `MA_BUSY` will be returned by
1621`ma_data_source_read_pcm_frames()`.
1622
1623For large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you
1624can instead stream audio data which you can do by specifying the
1625`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. When streaming, data will be decoded in 1
1626second pages. When a new page needs to be decoded, a job will be posted to the job queue and then
1627subsequently processed in a job thread.
1628
1629For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means
1630multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in
1631the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be
1632matched up with a call to `ma_resource_manager_data_source_uninit()`. Sometimes it can be useful
1633for a program to register self-managed raw audio data and associate it with a file path. Use the
1634`ma_resource_manager_register_*()` and `ma_resource_manager_unregister_*()` APIs to do this.
1635`ma_resource_manager_register_decoded_data()` is used to associate a pointer to raw, self-managed
1636decoded audio data in the specified data format with the specified name. Likewise,
1637`ma_resource_manager_register_encoded_data()` is used to associate a pointer to raw self-managed
1638encoded audio data (the raw file data) with the specified name. Note that these names need not be
1639actual file paths. When `ma_resource_manager_data_source_init()` is called (without the
1640`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these
1641explicitly registered data buffers and, if found, will use it as the backing data for the data
1642source. Note that the resource manager does *not* make a copy of this data so it is up to the
1643caller to ensure the pointer stays valid for it's lifetime. Use
1644`ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use
1645`ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and
1646unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM`
1647flag with a self-managed data pointer.
1648
1649
16506.1. Asynchronous Loading and Synchronization
1651---------------------------------------------
1652When loading asynchronously, it can be useful to poll whether or not loading has finished. Use
1653`ma_resource_manager_data_source_result()` to determine this. For in-memory sounds, this will
1654return `MA_SUCCESS` when the file has been *entirely* decoded. If the sound is still being decoded,
1655`MA_BUSY` will be returned. Otherwise, some other error code will be returned if the sound failed
1656to load. For streaming data sources, `MA_SUCCESS` will be returned when the first page has been
1657decoded and the sound is ready to be played. If the first page is still being decoded, `MA_BUSY`
1658will be returned. Otherwise, some other error code will be returned if the sound failed to load.
1659
1660In addition to polling, you can also use a simple synchronization object called a "fence" to wait
1661for asynchronously loaded sounds to finish. This is called `ma_fence`. The advantage to using a
1662fence is that it can be used to wait for a group of sounds to finish loading rather than waiting
1663for sounds on an individual basis. There are two stages to loading a sound:
1664
1665 * Initialization of the internal decoder; and
1666 * Completion of decoding of the file (the file is fully decoded)
1667
1668You can specify separate fences for each of the different stages. Waiting for the initialization
1669of the internal decoder is important for when you need to know the sample format, channels and
1670sample rate of the file.
1671
1672The example below shows how you could use a fence when loading a number of sounds:
1673
1674 ```c
1675 // This fence will be released when all sounds are finished loading entirely.
1676 ma_fence fence;
1677 ma_fence_init(&fence);
1678
1679 // This will be passed into the initialization routine for each sound.
1680 ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init();
1681 notifications.done.pFence = &fence;
1682
1683 // Now load a bunch of sounds:
1684 for (iSound = 0; iSound < soundCount; iSound += 1) {
1685 ma_resource_manager_data_source_init(pResourceManager, pSoundFilePaths[iSound], flags, &notifications, &pSoundSources[iSound]);
1686 }
1687
1688 // ... DO SOMETHING ELSE WHILE SOUNDS ARE LOADING ...
1689
1690 // Wait for loading of sounds to finish.
1691 ma_fence_wait(&fence);
1692 ```
1693
1694In the example above we used a fence for waiting until the entire file has been fully decoded. If
1695you only need to wait for the initialization of the internal decoder to complete, you can use the
1696`init` member of the `ma_resource_manager_pipeline_notifications` object:
1697
1698 ```c
1699 notifications.init.pFence = &fence;
1700 ```
1701
1702If a fence is not appropriate for your situation, you can instead use a callback that is fired on
1703an individual sound basis. This is done in a very similar way to fences:
1704
1705 ```c
1706 typedef struct
1707 {
1708 ma_async_notification_callbacks cb;
1709 void* pMyData;
1710 } my_notification;
1711
1712 void my_notification_callback(ma_async_notification* pNotification)
1713 {
1714 my_notification* pMyNotification = (my_notification*)pNotification;
1715
1716 // Do something in response to the sound finishing loading.
1717 }
1718
1719 ...
1720
1721 my_notification myCallback;
1722 myCallback.cb.onSignal = my_notification_callback;
1723 myCallback.pMyData = pMyData;
1724
1725 ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init();
1726 notifications.done.pNotification = &myCallback;
1727
1728 ma_resource_manager_data_source_init(pResourceManager, "my_sound.wav", flags, &notifications, &mySound);
1729 ```
1730
1731In the example above we just extend the `ma_async_notification_callbacks` object and pass an
1732instantiation into the `ma_resource_manager_pipeline_notifications` in the same way as we did with
1733the fence, only we set `pNotification` instead of `pFence`. You can set both of these at the same
1734time and they should both work as expected. If using the `pNotification` system, you need to ensure
1735your `ma_async_notification_callbacks` object stays valid.
1736
1737
1738
17396.2. Resource Manager Implementation Details
1740--------------------------------------------
1741Resources are managed in two main ways:
1742
1743 * By storing the entire sound inside an in-memory buffer (referred to as a data buffer)
1744 * By streaming audio data on the fly (referred to as a data stream)
1745
1746A resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or
1747data stream, depending on whether or not the data source was initialized with the
1748`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a
1749`ma_resource_manager_data_stream` object. Otherwise it will use a `ma_resource_manager_data_buffer`
1750object. Both of these objects are data sources which means they can be used with any
1751`ma_data_source_*()` API.
1752
1753Another major feature of the resource manager is the ability to asynchronously decode audio files.
1754This relieves the audio thread of time-consuming decoding which can negatively affect scalability
1755due to the audio thread needing to complete it's work extremely quickly to avoid glitching.
1756Asynchronous decoding is achieved through a job system. There is a central multi-producer,
1757multi-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is
1758posted to the queue which is then read by a job thread. The number of job threads can be
1759configured for improved scalability, and job threads can all run in parallel without needing to
1760worry about the order of execution (how this is achieved is explained below).
1761
1762When a sound is being loaded asynchronously, playback can begin before the sound has been fully
1763decoded. This enables the application to start playback of the sound quickly, while at the same
1764time allowing to resource manager to keep loading in the background. Since there may be less
1765threads than the number of sounds being loaded at a given time, a simple scheduling system is used
1766to keep decoding time balanced and fair. The resource manager solves this by splitting decoding
1767into chunks called pages. By default, each page is 1 second long. When a page has been decoded, a
1768new job will be posted to start decoding the next page. By dividing up decoding into pages, an
1769individual sound shouldn't ever delay every other sound from having their first page decoded. Of
1770course, when loading many sounds at the same time, there will always be an amount of time required
1771to process jobs in the queue so in heavy load situations there will still be some delay. To
1772determine if a data source is ready to have some frames read, use
1773`ma_resource_manager_data_source_get_available_frames()`. This will return the number of frames
1774available starting from the current position.
1775
1776
17776.2.1. Job Queue
1778----------------
1779The resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity.
1780This job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety.
1781Only a fixed number of jobs can be allocated and inserted into the queue which is done through a
1782lock-free data structure for allocating an index into a fixed sized array, with reference counting
1783for mitigation of the ABA problem. The reference count is 32-bit.
1784
1785For many types of jobs it's important that they execute in a specific order. In these cases, jobs
1786are executed serially. For the resource manager, serial execution of jobs is only required on a
1787per-object basis (per data buffer or per data stream). Each of these objects stores an execution
1788counter. When a job is posted it is associated with an execution counter. When the job is
1789processed, it checks if the execution counter of the job equals the execution counter of the
1790owning object and if so, processes the job. If the counters are not equal, the job will be posted
1791back onto the job queue for later processing. When the job finishes processing the execution order
1792of the main object is incremented. This system means the no matter how many job threads are
1793executing, decoding of an individual sound will always get processed serially. The advantage to
1794having multiple threads comes into play when loading multiple sounds at the same time.
1795
1796The resource manager's job queue is not 100% lock-free and will use a spinlock to achieve
1797thread-safety for a very small section of code. This is only relevant when the resource manager
1798uses more than one job thread. If only using a single job thread, which is the default, the
1799lock should never actually wait in practice. The amount of time spent locking should be quite
1800short, but it's something to be aware of for those who have pedantic lock-free requirements and
1801need to use more than one job thread. There are plans to remove this lock in a future version.
1802
1803In addition, posting a job will release a semaphore, which on Win32 is implemented with
1804`ReleaseSemaphore` and on POSIX platforms via a condition variable:
1805
1806 ```c
1807 pthread_mutex_lock(&pSemaphore->lock);
1808 {
1809 pSemaphore->value += 1;
1810 pthread_cond_signal(&pSemaphore->cond);
1811 }
1812 pthread_mutex_unlock(&pSemaphore->lock);
1813 ```
1814
1815Again, this is relevant for those with strict lock-free requirements in the audio thread. To avoid
1816this, you can use non-blocking mode (via the `MA_JOB_QUEUE_FLAG_NON_BLOCKING`
1817flag) and implement your own job processing routine (see the "Resource Manager" section above for
1818details on how to do this).
1819
1820
1821
18226.2.2. Data Buffers
1823-------------------
1824When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag is excluded at initialization time, the
1825resource manager will try to load the data into an in-memory data buffer. Before doing so, however,
1826it will first check if the specified file is already loaded. If so, it will increment a reference
1827counter and just use the already loaded data. This saves both time and memory. When the data buffer
1828is uninitialized, the reference counter will be decremented. If the counter hits zero, the file
1829will be unloaded. This is a detail to keep in mind because it could result in excessive loading and
1830unloading of a sound. For example, the following sequence will result in a file be loaded twice,
1831once after the other:
1832
1833 ```c
1834 ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load.
1835 ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer0); // Refcount = 0. Unloaded.
1836
1837 ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it.
1838 ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer1); // Refcount = 0. Unloaded.
1839 ```
1840
1841A binary search tree (BST) is used for storing data buffers as it has good balance between
1842efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed
1843into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves
1844memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST
1845due to the random nature of the hash. The disadvantage is that file names are case-sensitive. If
1846this is an issue, you should normalize your file names to upper- or lower-case before initializing
1847your data sources.
1848
1849When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC`
1850flag is excluded, the file will be decoded synchronously by the calling thread. There are two
1851options for controlling how the audio is stored in the data buffer - encoded or decoded. When the
1852`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` option is excluded, the raw file data will be stored
1853in memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is
1854a very simple and standard process of simply adding an item to the BST, allocating a block of
1855memory and then decoding (if `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` is specified).
1856
1857When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is specified, loading of the data buffer
1858is done asynchronously. In this case, a job is posted to the queue to start loading and then the
1859function immediately returns, setting an internal result code to `MA_BUSY`. This result code is
1860returned when the program calls `ma_resource_manager_data_source_result()`. When decoding has fully
1861completed `MA_SUCCESS` will be returned. This can be used to know if loading has fully completed.
1862
1863When loading asynchronously, a single job is posted to the queue of the type
1864`MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE`. This involves making a copy of the file path and
1865associating it with job. When the job is processed by the job thread, it will first load the file
1866using the VFS associated with the resource manager. When using a custom VFS, it's important that it
1867be completely thread-safe because it will be used from one or more job threads at the same time.
1868Individual files should only ever be accessed by one thread at a time, however. After opening the
1869file via the VFS, the job will determine whether or not the file is being decoded. If not, it
1870simply allocates a block of memory and loads the raw file contents into it and returns. On the
1871other hand, when the file is being decoded, it will first allocate a decoder on the heap and
1872initialize it. Then it will check if the length of the file is known. If so it will allocate a
1873block of memory to store the decoded output and initialize it to silence. If the size is unknown,
1874it will allocate room for one page. After memory has been allocated, the first page will be
1875decoded. If the sound is shorter than a page, the result code will be set to `MA_SUCCESS` and the
1876completion event will be signalled and loading is now complete. If, however, there is more to
1877decode, a job with the code `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` is posted. This job
1878will decode the next page and perform the same process if it reaches the end. If there is more to
1879decode, the job will post another `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` job which will
1880keep on happening until the sound has been fully decoded. For sounds of an unknown length, each
1881page will be linked together as a linked list. Internally this is implemented via the
1882`ma_paged_audio_buffer` object.
1883
1884
18856.2.3. Data Streams
1886-------------------
1887Data streams only ever store two pages worth of data for each instance. They are most useful for
1888large sounds like music tracks in games that would consume too much memory if fully decoded in
1889memory. After every frame from a page has been read, a job will be posted to load the next page
1890which is done from the VFS.
1891
1892For data streams, the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag will determine whether or
1893not initialization of the data source waits until the two pages have been decoded. When unset,
1894`ma_resource_manager_data_source_init()` will wait until the two pages have been loaded, otherwise
1895it will return immediately.
1896
1897When frames are read from a data stream using `ma_resource_manager_data_source_read_pcm_frames()`,
1898`MA_BUSY` will be returned if there are no frames available. If there are some frames available,
1899but less than the number requested, `MA_SUCCESS` will be returned, but the actual number of frames
1900read will be less than the number requested. Due to the asynchronous nature of data streams,
1901seeking is also asynchronous. If the data stream is in the middle of a seek, `MA_BUSY` will be
1902returned when trying to read frames.
1903
1904When `ma_resource_manager_data_source_read_pcm_frames()` results in a page getting fully consumed
1905a job is posted to load the next page. This will be posted from the same thread that called
1906`ma_resource_manager_data_source_read_pcm_frames()`.
1907
1908Data streams are uninitialized by posting a job to the queue, but the function won't return until
1909that job has been processed. The reason for this is that the caller owns the data stream object and
1910therefore miniaudio needs to ensure everything completes before handing back control to the caller.
1911Also, if the data stream is uninitialized while pages are in the middle of decoding, they must
1912complete before destroying any underlying object and the job system handles this cleanly.
1913
1914Note that when a new page needs to be loaded, a job will be posted to the resource manager's job
1915thread from the audio thread. You must keep in mind the details mentioned in the "Job Queue"
1916section above regarding locking when posting an event if you require a strictly lock-free audio
1917thread.
1918
1919
1920
19217. Node Graph
1922=============
1923miniaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a
1924node whose outputs are attached to inputs of another node, thereby creating a graph. There are
1925different types of nodes, with each node in the graph processing input data to produce output,
1926which is then fed through the chain. Each node in the graph can apply their own custom effects. At
1927the start of the graph will usually be one or more data source nodes which have no inputs, but
1928instead pull their data from a data source. At the end of the graph is an endpoint which represents
1929the end of the chain and is where the final output is ultimately extracted from.
1930
1931Each node has a number of input buses and a number of output buses. An output bus from a node is
1932attached to an input bus of another. Multiple nodes can connect their output buses to another
1933node's input bus, in which case their outputs will be mixed before processing by the node. Below is
1934a diagram that illustrates a hypothetical node graph setup:
1935
1936 ```
1937 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
1938
1939 +---------------+ +-----------------+
1940 | Data Source 1 =----+ +----------+ +----= Low Pass Filter =----+
1941 +---------------+ | | =----+ +-----------------+ | +----------+
1942 +----= Splitter | +----= ENDPOINT |
1943 +---------------+ | | =----+ +-----------------+ | +----------+
1944 | Data Source 2 =----+ +----------+ +----= Echo / Delay =----+
1945 +---------------+ +-----------------+
1946 ```
1947
1948In the above graph, it starts with two data sources whose outputs are attached to the input of a
1949splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter
1950performs it's processing routine and produces two outputs which is simply a duplication of the
1951input stream. One output is attached to a low pass filter, whereas the other output is attached to
1952a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and
1953since they're both connected to the same input but, they'll be mixed.
1954
1955Each input bus must be configured to accept the same number of channels, but the number of channels
1956used by input buses can be different to the number of channels for output buses in which case
1957miniaudio will automatically convert the input data to the output channel count before processing.
1958The number of channels of an output bus of one node must match the channel count of the input bus
1959it's attached to. The channel counts cannot be changed after the node has been initialized. If you
1960attempt to attach an output bus to an input bus with a different channel count, attachment will
1961fail.
1962
1963To use a node graph, you first need to initialize a `ma_node_graph` object. This is essentially a
1964container around the entire graph. The `ma_node_graph` object is required for some thread-safety
1965issues which will be explained later. A `ma_node_graph` object is initialized using miniaudio's
1966standard config/init system:
1967
1968 ```c
1969 ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount);
1970
1971 result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph); // Second parameter is a pointer to allocation callbacks.
1972 if (result != MA_SUCCESS) {
1973 // Failed to initialize node graph.
1974 }
1975 ```
1976
1977When you initialize the node graph, you're specifying the channel count of the endpoint. The
1978endpoint is a special node which has one input bus and one output bus, both of which have the
1979same channel count, which is specified in the config. Any nodes that connect directly to the
1980endpoint must be configured such that their output buses have the same channel count. When you read
1981audio data from the node graph, it'll have the channel count you specified in the config. To read
1982data from the graph:
1983
1984 ```c
1985 ma_uint32 framesRead;
1986 result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead);
1987 if (result != MA_SUCCESS) {
1988 // Failed to read data from the node graph.
1989 }
1990 ```
1991
1992When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in
1993data from it's input attachments, which in turn recusively pull in data from their inputs, and so
1994on. At the start of the graph there will be some kind of data source node which will have zero
1995inputs and will instead read directly from a data source. The base nodes don't literally need to
1996read from a `ma_data_source` object, but they will always have some kind of underlying object that
1997sources some kind of audio. The `ma_data_source_node` node can be used to read from a
1998`ma_data_source`. Data is always in floating-point format and in the number of channels you
1999specified when the graph was initialized. The sample rate is defined by the underlying data sources.
2000It's up to you to ensure they use a consistent and appropraite sample rate.
2001
2002The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but
2003miniaudio includes a few stock nodes for common functionality. This is how you would initialize a
2004node which reads directly from a data source (`ma_data_source_node`) which is an example of one
2005of the stock nodes that comes with miniaudio:
2006
2007 ```c
2008 ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource);
2009
2010 ma_data_source_node dataSourceNode;
2011 result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode);
2012 if (result != MA_SUCCESS) {
2013 // Failed to create data source node.
2014 }
2015 ```
2016
2017The data source node will use the output channel count to determine the channel count of the output
2018bus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data
2019source). The data source must output to floating-point (`ma_format_f32`) or else an error will be
2020returned from `ma_data_source_node_init()`.
2021
2022By default the node will not be attached to the graph. To do so, use `ma_node_attach_output_bus()`:
2023
2024 ```c
2025 result = ma_node_attach_output_bus(&dataSourceNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0);
2026 if (result != MA_SUCCESS) {
2027 // Failed to attach node.
2028 }
2029 ```
2030
2031The code above connects the data source node directly to the endpoint. Since the data source node
2032has only a single output bus, the index will always be 0. Likewise, the endpoint only has a single
2033input bus which means the input bus index will also always be 0.
2034
2035To detach a specific output bus, use `ma_node_detach_output_bus()`. To detach all output buses, use
2036`ma_node_detach_all_output_buses()`. If you want to just move the output bus from one attachment to
2037another, you do not need to detach first. You can just call `ma_node_attach_output_bus()` and it'll
2038deal with it for you.
2039
2040Less frequently you may want to create a specialized node. This will be a node where you implement
2041your own processing callback to apply a custom effect of some kind. This is similar to initalizing
2042one of the stock node types, only this time you need to specify a pointer to a vtable containing a
2043pointer to the processing function and the number of input and output buses. Example:
2044
2045 ```c
2046 static void my_custom_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
2047 {
2048 // Do some processing of ppFramesIn (one stream of audio data per input bus)
2049 const float* pFramesIn_0 = ppFramesIn[0]; // Input bus @ index 0.
2050 const float* pFramesIn_1 = ppFramesIn[1]; // Input bus @ index 1.
2051 float* pFramesOut_0 = ppFramesOut[0]; // Output bus @ index 0.
2052
2053 // Do some processing. On input, `pFrameCountIn` will be the number of input frames in each
2054 // buffer in `ppFramesIn` and `pFrameCountOut` will be the capacity of each of the buffers
2055 // in `ppFramesOut`. On output, `pFrameCountIn` should be set to the number of input frames
2056 // your node consumed and `pFrameCountOut` should be set the number of output frames that
2057 // were produced.
2058 //
2059 // You should process as many frames as you can. If your effect consumes input frames at the
2060 // same rate as output frames (always the case, unless you're doing resampling), you need
2061 // only look at `ppFramesOut` and process that exact number of frames. If you're doing
2062 // resampling, you'll need to be sure to set both `pFrameCountIn` and `pFrameCountOut`
2063 // properly.
2064 }
2065
2066 static ma_node_vtable my_custom_node_vtable =
2067 {
2068 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.
2069 NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames.
2070 2, // 2 input buses.
2071 1, // 1 output bus.
2072 0 // Default flags.
2073 };
2074
2075 ...
2076
2077 // Each bus needs to have a channel count specified. To do this you need to specify the channel
2078 // counts in an array and then pass that into the node config.
2079 ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable.
2080 ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specicied in the vtable.
2081
2082 inputChannels[0] = channelsIn;
2083 inputChannels[1] = channelsIn;
2084 outputChannels[0] = channelsOut;
2085
2086 ma_node_config nodeConfig = ma_node_config_init();
2087 nodeConfig.vtable = &my_custom_node_vtable;
2088 nodeConfig.pInputChannels = inputChannels;
2089 nodeConfig.pOutputChannels = outputChannels;
2090
2091 ma_node_base node;
2092 result = ma_node_init(&nodeGraph, &nodeConfig, NULL, &node);
2093 if (result != MA_SUCCESS) {
2094 // Failed to initialize node.
2095 }
2096 ```
2097
2098When initializing a custom node, as in the code above, you'll normally just place your vtable in
2099static space. The number of input and output buses are specified as part of the vtable. If you need
2100a variable number of buses on a per-node bases, the vtable should have the relevant bus count set
2101to `MA_NODE_BUS_COUNT_UNKNOWN`. In this case, the bus count should be set in the node config:
2102
2103 ```c
2104 static ma_node_vtable my_custom_node_vtable =
2105 {
2106 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.
2107 NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames.
2108 MA_NODE_BUS_COUNT_UNKNOWN, // The number of input buses is determined on a per-node basis.
2109 1, // 1 output bus.
2110 0 // Default flags.
2111 };
2112
2113 ...
2114
2115 ma_node_config nodeConfig = ma_node_config_init();
2116 nodeConfig.vtable = &my_custom_node_vtable;
2117 nodeConfig.inputBusCount = myBusCount; // <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here.
2118 nodeConfig.pInputChannels = inputChannels; // <-- Make sure there are nodeConfig.inputBusCount elements in this array.
2119 nodeConfig.pOutputChannels = outputChannels; // <-- The vtable specifies 1 output bus, so there must be 1 element in this array.
2120 ```
2121
2122In the above example it's important to never set the `inputBusCount` and `outputBusCount` members
2123to anything other than their defaults if the vtable specifies an explicit count. They can only be
2124set if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count.
2125
2126Most often you'll want to create a structure to encapsulate your node with some extra data. You
2127need to make sure the `ma_node_base` object is your first member of the structure:
2128
2129 ```c
2130 typedef struct
2131 {
2132 ma_node_base base; // <-- Make sure this is always the first member.
2133 float someCustomData;
2134 } my_custom_node;
2135 ```
2136
2137By doing this, your object will be compatible with all `ma_node` APIs and you can attach it to the
2138graph just like any other node.
2139
2140In the custom processing callback (`my_custom_node_process_pcm_frames()` in the example above), the
2141number of channels for each bus is what was specified by the config when the node was initialized
2142with `ma_node_init()`. In addition, all attachments to each of the input buses will have been
2143pre-mixed by miniaudio. The config allows you to specify different channel counts for each
2144individual input and output bus. It's up to the effect to handle it appropriate, and if it can't,
2145return an error in it's initialization routine.
2146
2147Custom nodes can be assigned some flags to describe their behaviour. These are set via the vtable
2148and include the following:
2149
2150 +-----------------------------------------+---------------------------------------------------+
2151 | Flag Name | Description |
2152 +-----------------------------------------+---------------------------------------------------+
2153 | MA_NODE_FLAG_PASSTHROUGH | Useful for nodes that do not do any kind of audio |
2154 | | processing, but are instead used for tracking |
2155 | | time, handling events, etc. Also used by the |
2156 | | internal endpoint node. It reads directly from |
2157 | | the input bus to the output bus. Nodes with this |
2158 | | flag must have exactly 1 input bus and 1 output |
2159 | | bus, and both buses must have the same channel |
2160 | | counts. |
2161 +-----------------------------------------+---------------------------------------------------+
2162 | MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even |
2163 | | when no data is available to be read from input |
2164 | | attachments. This is useful for effects like |
2165 | | echos where there will be a tail of audio data |
2166 | | that still needs to be processed even when the |
2167 | | original data sources have reached their ends. |
2168 +-----------------------------------------+---------------------------------------------------+
2169 | MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with |
2170 | | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this |
2171 | | is set, the `ppFramesIn` parameter of the |
2172 | | processing callback will be set to NULL when |
2173 | | there are no input frames are available. When |
2174 | | this is unset, silence will be posted to the |
2175 | | processing callback. |
2176 +-----------------------------------------+---------------------------------------------------+
2177 | MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES | Used to tell miniaudio that input and output |
2178 | | frames are processed at different rates. You |
2179 | | should set this for any nodes that perform |
2180 | | resampling. |
2181 +-----------------------------------------+---------------------------------------------------+
2182 | MA_NODE_FLAG_SILENT_OUTPUT | Used to tell miniaudio that a node produces only |
2183 | | silent output. This is useful for nodes where you |
2184 | | don't want the output to contribute to the final |
2185 | | mix. An example might be if you want split your |
2186 | | stream and have one branch be output to a file. |
2187 | | When using this flag, you should avoid writing to |
2188 | | the output buffer of the node's processing |
2189 | | callback because miniaudio will ignore it anyway. |
2190 +-----------------------------------------+---------------------------------------------------+
2191
2192
2193If you need to make a copy of an audio stream for effect processing you can use a splitter node
2194called `ma_splitter_node`. This takes has 1 input bus and splits the stream into 2 output buses.
2195You can use it like this:
2196
2197 ```c
2198 ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channelsIn, channelsOut);
2199
2200 ma_splitter_node splitterNode;
2201 result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode);
2202 if (result != MA_SUCCESS) {
2203 // Failed to create node.
2204 }
2205
2206 // Attach your output buses to two different input buses (can be on two different nodes).
2207 ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint.
2208 ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); // Attach to input bus 0 of some effect node.
2209 ```
2210
2211The volume of an output bus can be configured on a per-bus basis:
2212
2213 ```c
2214 ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f);
2215 ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f);
2216 ```
2217
2218In the code above we're using the splitter node from before and changing the volume of each of the
2219copied streams.
2220
2221You can start and stop a node with the following:
2222
2223 ```c
2224 ma_node_set_state(&splitterNode, ma_node_state_started); // The default state.
2225 ma_node_set_state(&splitterNode, ma_node_state_stopped);
2226 ```
2227
2228By default the node is in a started state, but since it won't be connected to anything won't
2229actually be invoked by the node graph until it's connected. When you stop a node, data will not be
2230read from any of it's input connections. You can use this property to stop a group of sounds
2231atomically.
2232
2233You can configure the initial state of a node in it's config:
2234
2235 ```c
2236 nodeConfig.initialState = ma_node_state_stopped;
2237 ```
2238
2239Note that for the stock specialized nodes, all of their configs will have a `nodeConfig` member
2240which is the config to use with the base node. This is where the initial state can be configured
2241for specialized nodes:
2242
2243 ```c
2244 dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped;
2245 ```
2246
2247When using a specialized node like `ma_data_source_node` or `ma_splitter_node`, be sure to not
2248modify the `vtable` member of the `nodeConfig` object.
2249
2250
22517.1. Timing
2252-----------
2253The node graph supports starting and stopping nodes at scheduled times. This is especially useful
2254for data source nodes where you want to get the node set up, but only start playback at a specific
2255time. There are two clocks: local and global.
2256
2257A local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can
2258only be done based on the global clock because the local clock will not be running while the node
2259is stopped. The global clocks advances whenever `ma_node_graph_read_pcm_frames()` is called. On the
2260other hand, the local clock only advances when the node's processing callback is fired, and is
2261advanced based on the output frame count.
2262
2263To retrieve the global time, use `ma_node_graph_get_time()`. The global time can be set with
2264`ma_node_graph_set_time()` which might be useful if you want to do seeking on a global timeline.
2265Getting and setting the local time is similar. Use `ma_node_get_time()` to retrieve the local time,
2266and `ma_node_set_time()` to set the local time. The global and local times will be advanced by the
2267audio thread, so care should be taken to avoid data races. Ideally you should avoid calling these
2268outside of the node processing callbacks which are always run on the audio thread.
2269
2270There is basic support for scheduling the starting and stopping of nodes. You can only schedule one
2271start and one stop at a time. This is mainly intended for putting nodes into a started or stopped
2272state in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited
2273to the resolution of a call to `ma_node_graph_read_pcm_frames()` which would typically be in blocks
2274of several milliseconds. The following APIs can be used for scheduling node states:
2275
2276 ```c
2277 ma_node_set_state_time()
2278 ma_node_get_state_time()
2279 ```
2280
2281The time is absolute and must be based on the global clock. An example is below:
2282
2283 ```c
2284 ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1); // Delay starting to 1 second.
2285 ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5); // Delay stopping to 5 seconds.
2286 ```
2287
2288An example for changing the state using a relative time.
2289
2290 ```c
2291 ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph));
2292 ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph));
2293 ```
2294
2295Note that due to the nature of multi-threading the times may not be 100% exact. If this is an
2296issue, consider scheduling state changes from within a processing callback. An idea might be to
2297have some kind of passthrough trigger node that is used specifically for tracking time and handling
2298events.
2299
2300
2301
23027.2. Thread Safety and Locking
2303------------------------------
2304When processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's
2305expected that `ma_node_graph_read_pcm_frames()` would be run on the audio thread, it does so
2306without the use of any locks. This section discusses the implementation used by miniaudio and goes
2307over some of the compromises employed by miniaudio to achieve this goal. Note that the current
2308implementation may not be ideal - feedback and critiques are most welcome.
2309
2310The node graph API is not *entirely* lock-free. Only `ma_node_graph_read_pcm_frames()` is expected
2311to be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the
2312implementation, but are crafted in a way such that such locking is not required when reading audio
2313data from the graph. Locking in these areas are achieved by means of spinlocks.
2314
2315The main complication with keeping `ma_node_graph_read_pcm_frames()` lock-free stems from the fact
2316that a node can be uninitialized, and it's memory potentially freed, while in the middle of being
2317processed on the audio thread. There are times when the audio thread will be referencing a node,
2318which means the uninitialization process of a node needs to make sure it delays returning until the
2319audio thread is finished so that control is not handed back to the caller thereby giving them a
2320chance to free the node's memory.
2321
2322When the audio thread is processing a node, it does so by reading from each of the output buses of
2323the node. In order for a node to process data for one of it's output buses, it needs to read from
2324each of it's input buses, and so on an so forth. It follows that once all output buses of a node
2325are detached, the node as a whole will be disconnected and no further processing will occur unless
2326it's output buses are reattached, which won't be happening when the node is being uninitialized.
2327By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can
2328simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By
2329doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output
2330nodes, followed by each of the attachments to each of it's input nodes, and then do any final clean
2331up.
2332
2333With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as
2334it takes to process the output bus being detached. This will happen if it's called at just the
2335wrong moment where the audio thread has just iterated it and has just started processing. The
2336caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which
2337includes the cost of recursively processing it's inputs. This is the biggest compromise made with
2338the approach taken by miniaudio for it's lock-free processing system. The cost of detaching nodes
2339earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching
2340higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass
2341detachments, detach starting from the lowest level nodes and work your way towards the final
2342endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not
2343running, detachment will be fast and detachment in any order will be the same. The reason nodes
2344need to wait for their input attachments to complete is due to the potential for desyncs between
2345data sources. If the node was to terminate processing mid way through processing it's inputs,
2346there's a chance that some of the underlying data sources will have been read, but then others not.
2347That will then result in a potential desynchronization when detaching and reattaching higher-level
2348nodes. A possible solution to this is to have an option when detaching to terminate processing
2349before processing all input attachments which should be fairly simple.
2350
2351Another compromise, albeit less significant, is locking when attaching and detaching nodes. This
2352locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present
2353for each input bus and output bus. When an output bus is connected to an input bus, both the output
2354bus and input bus is locked. This locking is specifically for attaching and detaching across
2355different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and
2356unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when
2357considering that iterating over attachments must not break as a result of attaching or detaching a
2358node while iteration is occuring.
2359
2360Attaching and detaching are both quite simple. When an output bus of a node is attached to an input
2361bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where
2362each item in the list is and output bus. We have some intentional (and convenient) restrictions on
2363what can done with the linked list in order to simplify the implementation. First of all, whenever
2364something needs to iterate over the list, it must do so in a forward direction. Backwards iteration
2365is not supported. Also, items can only be added to the start of the list.
2366
2367The linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer
2368to the next item in the list, and another to the previous item. A pointer to the previous item is
2369only required for fast detachment of the node - it is never used in iteration. This is an
2370important property because it means from the perspective of iteration, attaching and detaching of
2371an item can be done with a single atomic assignment. This is exploited by both the attachment and
2372detachment process. When attaching the node, the first thing that is done is the setting of the
2373local "next" and "previous" pointers of the node. After that, the item is "attached" to the list
2374by simply performing an atomic exchange with the head pointer. After that, the node is "attached"
2375to the list from the perspective of iteration. Even though the "previous" pointer of the next item
2376hasn't yet been set, from the perspective of iteration it's been attached because iteration will
2377only be happening in a forward direction which means the "previous" pointer won't actually ever get
2378used. The same general process applies to detachment. See `ma_node_attach_output_bus()` and
2379`ma_node_detach_output_bus()` for the implementation of this mechanism.
2380
2381
2382
23838. Decoding
2384===========
2385The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from
2386devices and can be used independently. The following formats are supported:
2387
2388 +---------+------------------+----------+
2389 | Format | Decoding Backend | Built-In |
2390 +---------+------------------+----------+
2391 | WAV | dr_wav | Yes |
2392 | MP3 | dr_mp3 | Yes |
2393 | FLAC | dr_flac | Yes |
2394 | Vorbis | stb_vorbis | No |
2395 +---------+------------------+----------+
2396
2397Vorbis is supported via stb_vorbis which can be enabled by including the header section before the
2398implementation of miniaudio, like the following:
2399
2400 ```c
2401 #define STB_VORBIS_HEADER_ONLY
2402 #include "extras/stb_vorbis.c" // Enables Vorbis decoding.
2403
2404 #define MINIAUDIO_IMPLEMENTATION
2405 #include "miniaudio.h"
2406
2407 // The stb_vorbis implementation must come after the implementation of miniaudio.
2408 #undef STB_VORBIS_HEADER_ONLY
2409 #include "extras/stb_vorbis.c"
2410 ```
2411
2412A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/mackron/miniaudio).
2413
2414Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the
2415built-in decoders by specifying one or more of the following options before the miniaudio
2416implementation:
2417
2418 ```c
2419 #define MA_NO_WAV
2420 #define MA_NO_MP3
2421 #define MA_NO_FLAC
2422 ```
2423
2424Disabling built-in decoding libraries is useful if you use these libraries independantly of the
2425`ma_decoder` API.
2426
2427A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with
2428`ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is
2429an example for loading a decoder from a file:
2430
2431 ```c
2432 ma_decoder decoder;
2433 ma_result result = ma_decoder_init_file("MySong.mp3", NULL, &decoder);
2434 if (result != MA_SUCCESS) {
2435 return false; // An error occurred.
2436 }
2437
2438 ...
2439
2440 ma_decoder_uninit(&decoder);
2441 ```
2442
2443When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object
2444(the `NULL` argument in the example above) which allows you to configure the output format, channel
2445count, sample rate and channel map:
2446
2447 ```c
2448 ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000);
2449 ```
2450
2451When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the
2452same as that defined by the decoding backend.
2453
2454Data is read from the decoder as PCM frames. This will output the number of PCM frames actually
2455read. If this is less than the requested number of PCM frames it means you've reached the end. The
2456return value will be `MA_AT_END` if no samples have been read and the end has been reached.
2457
2458 ```c
2459 ma_result result = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead, &framesRead);
2460 if (framesRead < framesToRead) {
2461 // Reached the end.
2462 }
2463 ```
2464
2465You can also seek to a specific frame like so:
2466
2467 ```c
2468 ma_result result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame);
2469 if (result != MA_SUCCESS) {
2470 return false; // An error occurred.
2471 }
2472 ```
2473
2474If you want to loop back to the start, you can simply seek back to the first PCM frame:
2475
2476 ```c
2477 ma_decoder_seek_to_pcm_frame(pDecoder, 0);
2478 ```
2479
2480When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding
2481backend. This can be unnecessarily inefficient if the type is already known. In this case you can
2482use `encodingFormat` variable in the device config to specify a specific encoding format you want
2483to decode:
2484
2485 ```c
2486 decoderConfig.encodingFormat = ma_encoding_format_wav;
2487 ```
2488
2489See the `ma_encoding_format` enum for possible encoding formats.
2490
2491The `ma_decoder_init_file()` API will try using the file extension to determine which decoding
2492backend to prefer.
2493
2494
24958.1. Custom Decoders
2496--------------------
2497It's possible to implement a custom decoder and plug it into miniaudio. This is extremely useful
2498when you want to use the `ma_decoder` API, but need to support an encoding format that's not one of
2499the stock formats supported by miniaudio. This can be put to particularly good use when using the
2500`ma_engine` and/or `ma_resource_manager` APIs because they use `ma_decoder` internally. If, for
2501example, you wanted to support Opus, you can do so with a custom decoder (there if a reference
2502Opus decoder in the "extras" folder of the miniaudio repository which uses libopus + libopusfile).
2503
2504A custom decoder must implement a data source. A vtable called `ma_decoding_backend_vtable` needs
2505to be implemented which is then passed into the decoder config:
2506
2507 ```c
2508 ma_decoding_backend_vtable* pCustomBackendVTables[] =
2509 {
2510 &g_ma_decoding_backend_vtable_libvorbis,
2511 &g_ma_decoding_backend_vtable_libopus
2512 };
2513
2514 ...
2515
2516 decoderConfig = ma_decoder_config_init_default();
2517 decoderConfig.pCustomBackendUserData = NULL;
2518 decoderConfig.ppCustomBackendVTables = pCustomBackendVTables;
2519 decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);
2520 ```
2521
2522The `ma_decoding_backend_vtable` vtable has the following functions:
2523
2524 ```
2525 onInit
2526 onInitFile
2527 onInitFileW
2528 onInitMemory
2529 onUninit
2530 ```
2531
2532There are only two functions that must be implemented - `onInit` and `onUninit`. The other
2533functions can be implemented for a small optimization for loading from a file path or memory. If
2534these are not specified, miniaudio will deal with it for you via a generic implementation.
2535
2536When you initialize a custom data source (by implementing the `onInit` function in the vtable) you
2537will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the
2538section about data sources for details on how to implemen this. Alternatively, see the
2539"custom_decoders" example in the miniaudio repository.
2540
2541The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data
2542from some abitrary source. You'll use these functions to read from the raw data and perform the
2543decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant
2544parameter.
2545
2546The `pConfig` parameter in `onInit` can be used to configure the backend if appropriate. It's only
2547used as a hint and can be ignored. However, if any of the properties are relevant to your decoder,
2548an optimal implementation will handle the relevant properties appropriately.
2549
2550If memory allocation is required, it should be done so via the specified allocation callbacks if
2551possible (the `pAllocationCallbacks` parameter).
2552
2553If an error occurs when initializing the decoder, you should leave `ppBackend` unset, or set to
2554NULL, and make sure everything is cleaned up appropriately and an appropriate result code returned.
2555When multiple custom backends are specified, miniaudio will cycle through the vtables in the order
2556they're listed in the array that's passed into the decoder config so it's important that your
2557initialization routine is clean.
2558
2559When a decoder is uninitialized, the `onUninit` callback will be fired which will give you an
2560opportunity to clean up and internal data.
2561
2562
2563
25649. Encoding
2565===========
2566The `ma_encoding` API is used for writing audio files. The only supported output format is WAV
2567which is achieved via dr_wav which is amalgamated into the implementation section of miniaudio.
2568This can be disabled by specifying the following option before the implementation of miniaudio:
2569
2570 ```c
2571 #define MA_NO_WAV
2572 ```
2573
2574An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data
2575delivered via callbacks with `ma_encoder_init()`. Below is an example for initializing an encoder
2576to output to a file.
2577
2578 ```c
2579 ma_encoder_config config = ma_encoder_config_init(ma_encoding_format_wav, FORMAT, CHANNELS, SAMPLE_RATE);
2580 ma_encoder encoder;
2581 ma_result result = ma_encoder_init_file("my_file.wav", &config, &encoder);
2582 if (result != MA_SUCCESS) {
2583 // Error
2584 }
2585
2586 ...
2587
2588 ma_encoder_uninit(&encoder);
2589 ```
2590
2591When initializing an encoder you must specify a config which is initialized with
2592`ma_encoder_config_init()`. Here you must specify the file type, the output sample format, output
2593channel count and output sample rate. The following file types are supported:
2594
2595 +------------------------+-------------+
2596 | Enum | Description |
2597 +------------------------+-------------+
2598 | ma_encoding_format_wav | WAV |
2599 +------------------------+-------------+
2600
2601If the format, channel count or sample rate is not supported by the output file type an error will
2602be returned. The encoder will not perform data conversion so you will need to convert it before
2603outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the
2604example below:
2605
2606 ```c
2607 framesWritten = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite);
2608 ```
2609
2610Encoders must be uninitialized with `ma_encoder_uninit()`.
2611
2612
2613
261410. Data Conversion
2615===================
2616A data conversion API is included with miniaudio which supports the majority of data conversion
2617requirements. This supports conversion between sample formats, channel counts (with channel
2618mapping) and sample rates.
2619
2620
262110.1. Sample Format Conversion
2622------------------------------
2623Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and
2624`ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()` to convert between two specific
2625formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use
2626`ma_convert_pcm_frames_format()` to convert PCM frames where you want to specify the frame count
2627and channel count as a variable instead of the total sample count.
2628
2629
263010.1.1. Dithering
2631-----------------
2632Dithering can be set using the ditherMode parameter.
2633
2634The different dithering modes include the following, in order of efficiency:
2635
2636 +-----------+--------------------------+
2637 | Type | Enum Token |
2638 +-----------+--------------------------+
2639 | None | ma_dither_mode_none |
2640 | Rectangle | ma_dither_mode_rectangle |
2641 | Triangle | ma_dither_mode_triangle |
2642 +-----------+--------------------------+
2643
2644Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be
2645ignored for conversions where dithering is not needed. Dithering is available for the following
2646conversions:
2647
2648 ```
2649 s16 -> u8
2650 s24 -> u8
2651 s32 -> u8
2652 f32 -> u8
2653 s24 -> s16
2654 s32 -> s16
2655 f32 -> s16
2656 ```
2657
2658Note that it is not an error to pass something other than ma_dither_mode_none for conversions where
2659dither is not used. It will just be ignored.
2660
2661
2662
266310.2. Channel Conversion
2664------------------------
2665Channel conversion is used for channel rearrangement and conversion from one channel count to
2666another. The `ma_channel_converter` API is used for channel conversion. Below is an example of
2667initializing a simple channel converter which converts from mono to stereo.
2668
2669 ```c
2670 ma_channel_converter_config config = ma_channel_converter_config_init(
2671 ma_format, // Sample format
2672 1, // Input channels
2673 NULL, // Input channel map
2674 2, // Output channels
2675 NULL, // Output channel map
2676 ma_channel_mix_mode_default); // The mixing algorithm to use when combining channels.
2677
2678 result = ma_channel_converter_init(&config, NULL, &converter);
2679 if (result != MA_SUCCESS) {
2680 // Error.
2681 }
2682 ```
2683
2684To perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so:
2685
2686 ```c
2687 ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount);
2688 if (result != MA_SUCCESS) {
2689 // Error.
2690 }
2691 ```
2692
2693It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM
2694frames.
2695
2696Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported.
2697
2698
269910.2.1. Channel Mapping
2700-----------------------
2701In addition to converting from one channel count to another, like the example above, the channel
2702converter can also be used to rearrange channels. When initializing the channel converter, you can
2703optionally pass in channel maps for both the input and output frames. If the channel counts are the
2704same, and each channel map contains the same channel positions with the exception that they're in
2705a different order, a simple shuffling of the channels will be performed. If, however, there is not
2706a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed
2707based on a mixing mode which is specified when initializing the `ma_channel_converter_config`
2708object.
2709
2710When converting from mono to multi-channel, the mono channel is simply copied to each output
2711channel. When going the other way around, the audio of each output channel is simply averaged and
2712copied to the mono channel.
2713
2714In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess
2715channels and silence extra channels. For example, converting from 4 to 2 channels, the 3rd and 4th
2716channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and
27174th channels.
2718
2719The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a
2720simple distribution between input and output. Imagine sitting in the middle of a room, with
2721speakers on the walls representing channel positions. The `MA_CHANNEL_FRONT_LEFT` position can be
2722thought of as being in the corner of the front and left walls.
2723
2724Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined
2725weights. Custom weights can be passed in as the last parameter of
2726`ma_channel_converter_config_init()`.
2727
2728Predefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a
2729`ma_standard_channel_map` enum as it's first parameter, which can be one of the following:
2730
2731 +-----------------------------------+-----------------------------------------------------------+
2732 | Name | Description |
2733 +-----------------------------------+-----------------------------------------------------------+
2734 | ma_standard_channel_map_default | Default channel map used by miniaudio. See below. |
2735 | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps. |
2736 | ma_standard_channel_map_alsa | Default ALSA channel map. |
2737 | ma_standard_channel_map_rfc3551 | RFC 3551. Based on AIFF. |
2738 | ma_standard_channel_map_flac | FLAC channel map. |
2739 | ma_standard_channel_map_vorbis | Vorbis channel map. |
2740 | ma_standard_channel_map_sound4 | FreeBSD's sound(4). |
2741 | ma_standard_channel_map_sndio | sndio channel map. http://www.sndio.org/tips.html. |
2742 | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering |
2743 +-----------------------------------+-----------------------------------------------------------+
2744
2745Below are the channel maps used by default in miniaudio (`ma_standard_channel_map_default`):
2746
2747 +---------------+---------------------------------+
2748 | Channel Count | Mapping |
2749 +---------------+---------------------------------+
2750 | 1 (Mono) | 0: MA_CHANNEL_MONO |
2751 +---------------+---------------------------------+
2752 | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT <br> |
2753 | | 1: MA_CHANNEL_FRONT_RIGHT |
2754 +---------------+---------------------------------+
2755 | 3 | 0: MA_CHANNEL_FRONT_LEFT <br> |
2756 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2757 | | 2: MA_CHANNEL_FRONT_CENTER |
2758 +---------------+---------------------------------+
2759 | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT <br> |
2760 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2761 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
2762 | | 3: MA_CHANNEL_BACK_CENTER |
2763 +---------------+---------------------------------+
2764 | 5 | 0: MA_CHANNEL_FRONT_LEFT <br> |
2765 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2766 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
2767 | | 3: MA_CHANNEL_BACK_LEFT <br> |
2768 | | 4: MA_CHANNEL_BACK_RIGHT |
2769 +---------------+---------------------------------+
2770 | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT <br> |
2771 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2772 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
2773 | | 3: MA_CHANNEL_LFE <br> |
2774 | | 4: MA_CHANNEL_SIDE_LEFT <br> |
2775 | | 5: MA_CHANNEL_SIDE_RIGHT |
2776 +---------------+---------------------------------+
2777 | 7 | 0: MA_CHANNEL_FRONT_LEFT <br> |
2778 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2779 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
2780 | | 3: MA_CHANNEL_LFE <br> |
2781 | | 4: MA_CHANNEL_BACK_CENTER <br> |
2782 | | 4: MA_CHANNEL_SIDE_LEFT <br> |
2783 | | 5: MA_CHANNEL_SIDE_RIGHT |
2784 +---------------+---------------------------------+
2785 | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT <br> |
2786 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2787 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
2788 | | 3: MA_CHANNEL_LFE <br> |
2789 | | 4: MA_CHANNEL_BACK_LEFT <br> |
2790 | | 5: MA_CHANNEL_BACK_RIGHT <br> |
2791 | | 6: MA_CHANNEL_SIDE_LEFT <br> |
2792 | | 7: MA_CHANNEL_SIDE_RIGHT |
2793 +---------------+---------------------------------+
2794 | Other | All channels set to 0. This |
2795 | | is equivalent to the same |
2796 | | mapping as the device. |
2797 +---------------+---------------------------------+
2798
2799
2800
280110.3. Resampling
2802----------------
2803Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something
2804like the following:
2805
2806 ```c
2807 ma_resampler_config config = ma_resampler_config_init(
2808 ma_format_s16,
2809 channels,
2810 sampleRateIn,
2811 sampleRateOut,
2812 ma_resample_algorithm_linear);
2813
2814 ma_resampler resampler;
2815 ma_result result = ma_resampler_init(&config, &resampler);
2816 if (result != MA_SUCCESS) {
2817 // An error occurred...
2818 }
2819 ```
2820
2821Do the following to uninitialize the resampler:
2822
2823 ```c
2824 ma_resampler_uninit(&resampler);
2825 ```
2826
2827The following example shows how data can be processed
2828
2829 ```c
2830 ma_uint64 frameCountIn = 1000;
2831 ma_uint64 frameCountOut = 2000;
2832 ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
2833 if (result != MA_SUCCESS) {
2834 // An error occurred...
2835 }
2836
2837 // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the
2838 // number of output frames written.
2839 ```
2840
2841To initialize the resampler you first need to set up a config (`ma_resampler_config`) with
2842`ma_resampler_config_init()`. You need to specify the sample format you want to use, the number of
2843channels, the input and output sample rate, and the algorithm.
2844
2845The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format
2846you will need to perform pre- and post-conversions yourself where necessary. Note that the format
2847is the same for both input and output. The format cannot be changed after initialization.
2848
2849The resampler supports multiple channels and is always interleaved (both input and output). The
2850channel count cannot be changed after initialization.
2851
2852The sample rates can be anything other than zero, and are always specified in hertz. They should be
2853set to something like 44100, etc. The sample rate is the only configuration property that can be
2854changed after initialization.
2855
2856The miniaudio resampler has built-in support for the following algorithms:
2857
2858 +-----------+------------------------------+
2859 | Algorithm | Enum Token |
2860 +-----------+------------------------------+
2861 | Linear | ma_resample_algorithm_linear |
2862 | Custom | ma_resample_algorithm_custom |
2863 +-----------+------------------------------+
2864
2865The algorithm cannot be changed after initialization.
2866
2867Processing always happens on a per PCM frame basis and always assumes interleaved input and output.
2868De-interleaved processing is not supported. To process frames, use
2869`ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you
2870can fit in the output buffer and the number of input frames contained in the input buffer. On
2871output these variables contain the number of output frames that were written to the output buffer
2872and the number of input frames that were consumed in the process. You can pass in NULL for the
2873input buffer in which case it will be treated as an infinitely large buffer of zeros. The output
2874buffer can also be NULL, in which case the processing will be treated as seek.
2875
2876The sample rate can be changed dynamically on the fly. You can change this with explicit sample
2877rates with `ma_resampler_set_rate()` and also with a decimal ratio with
2878`ma_resampler_set_rate_ratio()`. The ratio is in/out.
2879
2880Sometimes it's useful to know exactly how many input frames will be required to output a specific
2881number of frames. You can calculate this with `ma_resampler_get_required_input_frame_count()`.
2882Likewise, it's sometimes useful to know exactly how many frames would be output given a certain
2883number of input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`.
2884
2885Due to the nature of how resampling works, the resampler introduces some latency. This can be
2886retrieved in terms of both the input rate and the output rate with
2887`ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`.
2888
2889
289010.3.1. Resampling Algorithms
2891-----------------------------
2892The choice of resampling algorithm depends on your situation and requirements.
2893
2894
289510.3.1.1. Linear Resampling
2896---------------------------
2897The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however,
2898some control over the quality of the linear resampler which may make it a suitable option depending
2899on your requirements.
2900
2901The linear resampler performs low-pass filtering before or after downsampling or upsampling,
2902depending on the sample rates you're converting between. When decreasing the sample rate, the
2903low-pass filter will be applied before downsampling. When increasing the rate it will be performed
2904after upsampling. By default a fourth order low-pass filter will be applied. This can be configured
2905via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering.
2906
2907The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of
2908the input and output sample rates (Nyquist Frequency).
2909
2910The API for the linear resampler is the same as the main resampler API, only it's called
2911`ma_linear_resampler`.
2912
2913
291410.3.2. Custom Resamplers
2915-------------------------
2916You can implement a custom resampler by using the `ma_resample_algorithm_custom` resampling
2917algorithm and setting a vtable in the resampler config:
2918
2919 ```c
2920 ma_resampler_config config = ma_resampler_config_init(..., ma_resample_algorithm_custom);
2921 config.pBackendVTable = &g_customResamplerVTable;
2922 ```
2923
2924Custom resamplers are useful if the stock algorithms are not appropriate for your use case. You
2925need to implement the required functions in `ma_resampling_backend_vtable`. Note that not all
2926functions in the vtable need to be implemented, but if it's possible to implement, they should be.
2927
2928You can use the `ma_linear_resampler` object for an example on how to implement the vtable. The
2929`onGetHeapSize` callback is used to calculate the size of any internal heap allocation the custom
2930resampler will need to make given the supplied config. When you initialize the resampler via the
2931`onInit` callback, you'll be given a pointer to a heap allocation which is where you should store
2932the heap allocated data. You should not free this data in `onUninit` because miniaudio will manage
2933it for you.
2934
2935The `onProcess` callback is where the actual resampling takes place. On input, `pFrameCountIn`
2936points to a variable containing the number of frames in the `pFramesIn` buffer and
2937`pFrameCountOut` points to a variable containing the capacity in frames of the `pFramesOut` buffer.
2938On output, `pFrameCountIn` should be set to the number of input frames that were fully consumed,
2939whereas `pFrameCountOut` should be set to the number of frames that were written to `pFramesOut`.
2940
2941The `onSetRate` callback is optional and is used for dynamically changing the sample rate. If
2942dynamic rate changes are not supported, you can set this callback to NULL.
2943
2944The `onGetInputLatency` and `onGetOutputLatency` functions are used for retrieving the latency in
2945input and output rates respectively. These can be NULL in which case latency calculations will be
2946assumed to be NULL.
2947
2948The `onGetRequiredInputFrameCount` callback is used to give miniaudio a hint as to how many input
2949frames are required to be available to produce the given number of output frames. Likewise, the
2950`onGetExpectedOutputFrameCount` callback is used to determine how many output frames will be
2951produced given the specified number of input frames. miniaudio will use these as a hint, but they
2952are optional and can be set to NULL if you're unable to implement them.
2953
2954
2955
295610.4. General Data Conversion
2957-----------------------------
2958The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and
2959resampling into one operation. This is what miniaudio uses internally to convert between the format
2960requested when the device was initialized and the format of the backend's native device. The API
2961for general data conversion is very similar to the resampling API. Create a `ma_data_converter`
2962object like this:
2963
2964 ```c
2965 ma_data_converter_config config = ma_data_converter_config_init(
2966 inputFormat,
2967 outputFormat,
2968 inputChannels,
2969 outputChannels,
2970 inputSampleRate,
2971 outputSampleRate
2972 );
2973
2974 ma_data_converter converter;
2975 ma_result result = ma_data_converter_init(&config, NULL, &converter);
2976 if (result != MA_SUCCESS) {
2977 // An error occurred...
2978 }
2979 ```
2980
2981In the example above we use `ma_data_converter_config_init()` to initialize the config, however
2982there's many more properties that can be configured, such as channel maps and resampling quality.
2983Something like the following may be more suitable depending on your requirements:
2984
2985 ```c
2986 ma_data_converter_config config = ma_data_converter_config_init_default();
2987 config.formatIn = inputFormat;
2988 config.formatOut = outputFormat;
2989 config.channelsIn = inputChannels;
2990 config.channelsOut = outputChannels;
2991 config.sampleRateIn = inputSampleRate;
2992 config.sampleRateOut = outputSampleRate;
2993 ma_channel_map_init_standard(ma_standard_channel_map_flac, config.channelMapIn, sizeof(config.channelMapIn)/sizeof(config.channelMapIn[0]), config.channelCountIn);
2994 config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER;
2995 ```
2996
2997Do the following to uninitialize the data converter:
2998
2999 ```c
3000 ma_data_converter_uninit(&converter, NULL);
3001 ```
3002
3003The following example shows how data can be processed
3004
3005 ```c
3006 ma_uint64 frameCountIn = 1000;
3007 ma_uint64 frameCountOut = 2000;
3008 ma_result result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
3009 if (result != MA_SUCCESS) {
3010 // An error occurred...
3011 }
3012
3013 // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number
3014 // of output frames written.
3015 ```
3016
3017The data converter supports multiple channels and is always interleaved (both input and output).
3018The channel count cannot be changed after initialization.
3019
3020Sample rates can be anything other than zero, and are always specified in hertz. They should be set
3021to something like 44100, etc. The sample rate is the only configuration property that can be
3022changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of
3023`ma_data_converter_config` is set to `MA_TRUE`. To change the sample rate, use
3024`ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out.
3025The resampling algorithm cannot be changed after initialization.
3026
3027Processing always happens on a per PCM frame basis and always assumes interleaved input and output.
3028De-interleaved processing is not supported. To process frames, use
3029`ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames
3030you can fit in the output buffer and the number of input frames contained in the input buffer. On
3031output these variables contain the number of output frames that were written to the output buffer
3032and the number of input frames that were consumed in the process. You can pass in NULL for the
3033input buffer in which case it will be treated as an infinitely large
3034buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated
3035as seek.
3036
3037Sometimes it's useful to know exactly how many input frames will be required to output a specific
3038number of frames. You can calculate this with `ma_data_converter_get_required_input_frame_count()`.
3039Likewise, it's sometimes useful to know exactly how many frames would be output given a certain
3040number of input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`.
3041
3042Due to the nature of how resampling works, the data converter introduces some latency if resampling
3043is required. This can be retrieved in terms of both the input rate and the output rate with
3044`ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`.
3045
3046
3047
304811. Filtering
3049=============
3050
305111.1. Biquad Filtering
3052----------------------
3053Biquad filtering is achieved with the `ma_biquad` API. Example:
3054
3055 ```c
3056 ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);
3057 ma_result result = ma_biquad_init(&config, &biquad);
3058 if (result != MA_SUCCESS) {
3059 // Error.
3060 }
3061
3062 ...
3063
3064 ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount);
3065 ```
3066
3067Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0,
3068b1 and b2, and the denominator coefficients are a0, a1 and a2. The a0 coefficient is required and
3069coefficients must not be pre-normalized.
3070
3071Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format
3072you need to convert it yourself beforehand. When using `ma_format_s16` the biquad filter will use
3073fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used.
3074
3075Input and output frames are always interleaved.
3076
3077Filtering can be applied in-place by passing in the same pointer for both the input and output
3078buffers, like so:
3079
3080 ```c
3081 ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount);
3082 ```
3083
3084If you need to change the values of the coefficients, but maintain the values in the registers you
3085can do so with `ma_biquad_reinit()`. This is useful if you need to change the properties of the
3086filter while keeping the values of registers valid to avoid glitching. Do not use
3087`ma_biquad_init()` for this as it will do a full initialization which involves clearing the
3088registers to 0. Note that changing the format or channel count after initialization is invalid and
3089will result in an error.
3090
3091
309211.2. Low-Pass Filtering
3093------------------------
3094Low-pass filtering is achieved with the following APIs:
3095
3096 +---------+------------------------------------------+
3097 | API | Description |
3098 +---------+------------------------------------------+
3099 | ma_lpf1 | First order low-pass filter |
3100 | ma_lpf2 | Second order low-pass filter |
3101 | ma_lpf | High order low-pass filter (Butterworth) |
3102 +---------+------------------------------------------+
3103
3104Low-pass filter example:
3105
3106 ```c
3107 ma_lpf_config config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
3108 ma_result result = ma_lpf_init(&config, &lpf);
3109 if (result != MA_SUCCESS) {
3110 // Error.
3111 }
3112
3113 ...
3114
3115 ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount);
3116 ```
3117
3118Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format
3119you need to convert it yourself beforehand. Input and output frames are always interleaved.
3120
3121Filtering can be applied in-place by passing in the same pointer for both the input and output
3122buffers, like so:
3123
3124 ```c
3125 ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount);
3126 ```
3127
3128The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more,
3129you can chain first and second order filters together.
3130
3131 ```c
3132 for (iFilter = 0; iFilter < filterCount; iFilter += 1) {
3133 ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount);
3134 }
3135 ```
3136
3137If you need to change the configuration of the filter, but need to maintain the state of internal
3138registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample
3139rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the
3140format or channel count after initialization is invalid and will result in an error.
3141
3142The `ma_lpf` object supports a configurable order, but if you only need a first order filter you
3143may want to consider using `ma_lpf1`. Likewise, if you only need a second order filter you can use
3144`ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient.
3145
3146If an even filter order is specified, a series of second order filters will be processed in a
3147chain. If an odd filter order is specified, a first order filter will be applied, followed by a
3148series of second order filters in a chain.
3149
3150
315111.3. High-Pass Filtering
3152-------------------------
3153High-pass filtering is achieved with the following APIs:
3154
3155 +---------+-------------------------------------------+
3156 | API | Description |
3157 +---------+-------------------------------------------+
3158 | ma_hpf1 | First order high-pass filter |
3159 | ma_hpf2 | Second order high-pass filter |
3160 | ma_hpf | High order high-pass filter (Butterworth) |
3161 +---------+-------------------------------------------+
3162
3163High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`,
3164`ma_hpf2` and `ma_hpf`. See example code for low-pass filters for example usage.
3165
3166
316711.4. Band-Pass Filtering
3168-------------------------
3169Band-pass filtering is achieved with the following APIs:
3170
3171 +---------+-------------------------------+
3172 | API | Description |
3173 +---------+-------------------------------+
3174 | ma_bpf2 | Second order band-pass filter |
3175 | ma_bpf | High order band-pass filter |
3176 +---------+-------------------------------+
3177
3178Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and
3179`ma_hpf`. See example code for low-pass filters for example usage. Note that the order for
3180band-pass filters must be an even number which means there is no first order band-pass filter,
3181unlike low-pass and high-pass filters.
3182
3183
318411.5. Notch Filtering
3185---------------------
3186Notch filtering is achieved with the following APIs:
3187
3188 +-----------+------------------------------------------+
3189 | API | Description |
3190 +-----------+------------------------------------------+
3191 | ma_notch2 | Second order notching filter |
3192 +-----------+------------------------------------------+
3193
3194
319511.6. Peaking EQ Filtering
3196-------------------------
3197Peaking filtering is achieved with the following APIs:
3198
3199 +----------+------------------------------------------+
3200 | API | Description |
3201 +----------+------------------------------------------+
3202 | ma_peak2 | Second order peaking filter |
3203 +----------+------------------------------------------+
3204
3205
320611.7. Low Shelf Filtering
3207-------------------------
3208Low shelf filtering is achieved with the following APIs:
3209
3210 +-------------+------------------------------------------+
3211 | API | Description |
3212 +-------------+------------------------------------------+
3213 | ma_loshelf2 | Second order low shelf filter |
3214 +-------------+------------------------------------------+
3215
3216Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to
3217just turn them down rather than eliminate them entirely.
3218
3219
322011.8. High Shelf Filtering
3221--------------------------
3222High shelf filtering is achieved with the following APIs:
3223
3224 +-------------+------------------------------------------+
3225 | API | Description |
3226 +-------------+------------------------------------------+
3227 | ma_hishelf2 | Second order high shelf filter |
3228 +-------------+------------------------------------------+
3229
3230The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf`
3231instead of `ma_loshelf`. Where a low shelf filter is used to adjust the volume of low frequencies,
3232the high shelf filter does the same thing for high frequencies.
3233
3234
3235
3236
323712. Waveform and Noise Generation
3238=================================
3239
324012.1. Waveforms
3241---------------
3242miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved
3243with the `ma_waveform` API. Example:
3244
3245 ```c
3246 ma_waveform_config config = ma_waveform_config_init(
3247 FORMAT,
3248 CHANNELS,
3249 SAMPLE_RATE,
3250 ma_waveform_type_sine,
3251 amplitude,
3252 frequency);
3253
3254 ma_waveform waveform;
3255 ma_result result = ma_waveform_init(&config, &waveform);
3256 if (result != MA_SUCCESS) {
3257 // Error.
3258 }
3259
3260 ...
3261
3262 ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount);
3263 ```
3264
3265The amplitude, frequency, type, and sample rate can be changed dynamically with
3266`ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, `ma_waveform_set_type()`, and
3267`ma_waveform_set_sample_rate()` respectively.
3268
3269You can invert the waveform by setting the amplitude to a negative value. You can use this to
3270control whether or not a sawtooth has a positive or negative ramp, for example.
3271
3272Below are the supported waveform types:
3273
3274 +---------------------------+
3275 | Enum Name |
3276 +---------------------------+
3277 | ma_waveform_type_sine |
3278 | ma_waveform_type_square |
3279 | ma_waveform_type_triangle |
3280 | ma_waveform_type_sawtooth |
3281 +---------------------------+
3282
3283
3284
328512.2. Noise
3286-----------
3287miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example:
3288
3289 ```c
3290 ma_noise_config config = ma_noise_config_init(
3291 FORMAT,
3292 CHANNELS,
3293 ma_noise_type_white,
3294 SEED,
3295 amplitude);
3296
3297 ma_noise noise;
3298 ma_result result = ma_noise_init(&config, &noise);
3299 if (result != MA_SUCCESS) {
3300 // Error.
3301 }
3302
3303 ...
3304
3305 ma_noise_read_pcm_frames(&noise, pOutput, frameCount);
3306 ```
3307
3308The noise API uses simple LCG random number generation. It supports a custom seed which is useful
3309for things like automated testing requiring reproducibility. Setting the seed to zero will default
3310to `MA_DEFAULT_LCG_SEED`.
3311
3312The amplitude, seed, and type can be changed dynamically with `ma_noise_set_amplitude()`,
3313`ma_noise_set_seed()`, and `ma_noise_set_type()` respectively.
3314
3315By default, the noise API will use different values for different channels. So, for example, the
3316left side in a stereo stream will be different to the right side. To instead have each channel use
3317the same random value, set the `duplicateChannels` member of the noise config to true, like so:
3318
3319 ```c
3320 config.duplicateChannels = MA_TRUE;
3321 ```
3322
3323Below are the supported noise types.
3324
3325 +------------------------+
3326 | Enum Name |
3327 +------------------------+
3328 | ma_noise_type_white |
3329 | ma_noise_type_pink |
3330 | ma_noise_type_brownian |
3331 +------------------------+
3332
3333
3334
333513. Audio Buffers
3336=================
3337miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can
3338read from memory that's managed by the application, but can also handle the memory management for
3339you internally. Memory management is flexible and should support most use cases.
3340
3341Audio buffers are initialised using the standard configuration system used everywhere in miniaudio:
3342
3343 ```c
3344 ma_audio_buffer_config config = ma_audio_buffer_config_init(
3345 format,
3346 channels,
3347 sizeInFrames,
3348 pExistingData,
3349 &allocationCallbacks);
3350
3351 ma_audio_buffer buffer;
3352 result = ma_audio_buffer_init(&config, &buffer);
3353 if (result != MA_SUCCESS) {
3354 // Error.
3355 }
3356
3357 ...
3358
3359 ma_audio_buffer_uninit(&buffer);
3360 ```
3361
3362In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an
3363application can do self-managed memory allocation. If you would rather make a copy of the data, use
3364`ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`.
3365
3366Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the
3367raw audio data in a contiguous block of memory. That is, the raw audio data will be located
3368immediately after the `ma_audio_buffer` structure. To do this, use
3369`ma_audio_buffer_alloc_and_init()`:
3370
3371 ```c
3372 ma_audio_buffer_config config = ma_audio_buffer_config_init(
3373 format,
3374 channels,
3375 sizeInFrames,
3376 pExistingData,
3377 &allocationCallbacks);
3378
3379 ma_audio_buffer* pBuffer
3380 result = ma_audio_buffer_alloc_and_init(&config, &pBuffer);
3381 if (result != MA_SUCCESS) {
3382 // Error
3383 }
3384
3385 ...
3386
3387 ma_audio_buffer_uninit_and_free(&buffer);
3388 ```
3389
3390If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it
3391with `ma_audio_buffer_uninit_and_free()`. In the example above, the memory pointed to by
3392`pExistingData` will be copied into the buffer, which is contrary to the behavior of
3393`ma_audio_buffer_init()`.
3394
3395An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the
3396cursor moves forward. The last parameter (`loop`) can be used to determine if the buffer should
3397loop. The return value is the number of frames actually read. If this is less than the number of
3398frames requested it means the end has been reached. This should never happen if the `loop`
3399parameter is set to true. If you want to manually loop back to the start, you can do so with with
3400`ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an
3401audio buffer.
3402
3403 ```c
3404 ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping);
3405 if (framesRead < desiredFrameCount) {
3406 // If not looping, this means the end has been reached. This should never happen in looping mode with valid input.
3407 }
3408 ```
3409
3410Sometimes you may want to avoid the cost of data movement between the internal buffer and the
3411output buffer. Instead you can use memory mapping to retrieve a pointer to a segment of data:
3412
3413 ```c
3414 void* pMappedFrames;
3415 ma_uint64 frameCount = frameCountToTryMapping;
3416 ma_result result = ma_audio_buffer_map(pAudioBuffer, &pMappedFrames, &frameCount);
3417 if (result == MA_SUCCESS) {
3418 // Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be
3419 // less due to the end of the buffer being reached.
3420 ma_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels);
3421
3422 // You must unmap the buffer.
3423 ma_audio_buffer_unmap(pAudioBuffer, frameCount);
3424 }
3425 ```
3426
3427When you use memory mapping, the read cursor is increment by the frame count passed in to
3428`ma_audio_buffer_unmap()`. If you decide not to process every frame you can pass in a value smaller
3429than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is
3430that it does not handle looping for you. You can determine if the buffer is at the end for the
3431purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of
3432`ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END`
3433as an error when returned by `ma_audio_buffer_unmap()`.
3434
3435
3436
343714. Ring Buffers
3438================
3439miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via
3440the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates on bytes, whereas the `ma_pcm_rb`
3441operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around
3442`ma_rb`.
3443
3444Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved
3445streams. The caller can also allocate their own backing memory for the ring buffer to use
3446internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for
3447you.
3448
3449The examples below use the PCM frame variant of the ring buffer since that's most likely the one
3450you will want to use. To initialize a ring buffer, do something like the following:
3451
3452 ```c
3453 ma_pcm_rb rb;
3454 ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb);
3455 if (result != MA_SUCCESS) {
3456 // Error
3457 }
3458 ```
3459
3460The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because
3461it's the PCM varient of the ring buffer API. For the regular ring buffer that operates on bytes you
3462would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes
3463instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter
3464is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines.
3465Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used.
3466
3467Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is
3468offset from each other based on the stride. To manage your sub-buffers you can use
3469`ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and
3470`ma_pcm_rb_get_subbuffer_ptr()`.
3471
3472Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section
3473of the ring buffer. You specify the number of frames you need, and on output it will set to what
3474was actually acquired. If the read or write pointer is positioned such that the number of frames
3475requested will require a loop, it will be clamped to the end of the buffer. Therefore, the number
3476of frames you're given may be less than the number you requested.
3477
3478After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the
3479buffer and then "commit" it with `ma_pcm_rb_commit_read()` or `ma_pcm_rb_commit_write()`. This is
3480where the read/write pointers are updated. When you commit you need to pass in the buffer that was
3481returned by the earlier call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is
3482only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and
3483`ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was
3484originally requested.
3485
3486If you want to correct for drift between the write pointer and the read pointer you can use a
3487combination of `ma_pcm_rb_pointer_distance()`, `ma_pcm_rb_seek_read()` and
3488`ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only
3489move the read pointer forward via the consumer thread, and the write pointer forward by the
3490producer thread. If there is too much space between the pointers, move the read pointer forward. If
3491there is too little space between the pointers, move the write pointer forward.
3492
3493You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb`
3494API. This is exactly the same, only you will use the `ma_rb` functions instead of `ma_pcm_rb` and
3495instead of frame counts you will pass around byte counts.
3496
3497The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most
3498significant bit being used to encode a loop flag and the internally managed buffers always being
3499aligned to `MA_SIMD_ALIGNMENT`.
3500
3501Note that the ring buffer is only thread safe when used by a single consumer thread and single
3502producer thread.
3503
3504
3505
350615. Backends
3507============
3508The following backends are supported by miniaudio.
3509
3510 +-------------+-----------------------+--------------------------------------------------------+
3511 | Name | Enum Name | Supported Operating Systems |
3512 +-------------+-----------------------+--------------------------------------------------------+
3513 | WASAPI | ma_backend_wasapi | Windows Vista+ |
3514 | DirectSound | ma_backend_dsound | Windows XP+ |
3515 | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) |
3516 | Core Audio | ma_backend_coreaudio | macOS, iOS |
3517 | ALSA | ma_backend_alsa | Linux |
3518 | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
3519 | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
3520 | sndio | ma_backend_sndio | OpenBSD |
3521 | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
3522 | OSS | ma_backend_oss | FreeBSD |
3523 | AAudio | ma_backend_aaudio | Android 8+ |
3524 | OpenSL ES | ma_backend_opensl | Android (API level 16+) |
3525 | Web Audio | ma_backend_webaudio | Web (via Emscripten) |
3526 | Custom | ma_backend_custom | Cross Platform |
3527 | Null | ma_backend_null | Cross Platform (not used on Web) |
3528 +-------------+-----------------------+--------------------------------------------------------+
3529
3530Some backends have some nuance details you may want to be aware of.
3531
353215.1. WASAPI
3533------------
3534- Low-latency shared mode will be disabled when using an application-defined sample rate which is
3535 different to the device's native sample rate. To work around this, set `wasapi.noAutoConvertSRC`
3536 to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing
3537 when the `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC
3538 will result in miniaudio's internal resampler being used instead which will in turn enable the
3539 use of low-latency shared mode.
3540
354115.2. PulseAudio
3542----------------
3543- If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki:
3544 https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling.
3545 Alternatively, consider using a different backend such as ALSA.
3546
354715.3. Android
3548-------------
3549- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest:
3550 `<uses-permission android:name="android.permission.RECORD_AUDIO" />`
3551- With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a
3552 limitation with OpenSL|ES.
3553- With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration
3554 API (devices are enumerated through Java). You can however perform your own device enumeration
3555 through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it
3556 to ma_device_init().
3557- The backend API will perform resampling where possible. The reason for this as opposed to using
3558 miniaudio's built-in resampler is to take advantage of any potential device-specific
3559 optimizations the driver may implement.
3560
356115.4. UWP
3562---------
3563- UWP only supports default playback and capture devices.
3564- UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest):
3565
3566 ```
3567 <Package ...>
3568 ...
3569 <Capabilities>
3570 <DeviceCapability Name="microphone" />
3571 </Capabilities>
3572 </Package>
3573 ```
3574
357515.5. Web Audio / Emscripten
3576----------------------------
3577- You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build.
3578- The first time a context is initialized it will create a global object called "miniaudio" whose
3579 primary purpose is to act as a factory for device objects.
3580- Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as
3581 they've been deprecated.
3582- Google has implemented a policy in their browsers that prevent automatic media output without
3583 first receiving some kind of user input. The following web page has additional details:
3584 https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device
3585 may fail if you try to start playback without first handling some kind of user input.
3586
3587
3588
358916. Optimization Tips
3590=====================
3591
359216.1. High Level API
3593--------------------
3594- If a sound does not require doppler or pitch shifting, consider disabling pitching by
3595 initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag.
3596- If a sound does not require spatialization, disable it by initialzing the sound with the
3597 `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be renabled again post-initialization with
3598 `ma_sound_set_spatialization_enabled()`.
3599
3600
3601
360217. Miscellaneous Notes
3603=======================
3604- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for
3605 WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though
3606 not all have been tested.
3607- The contents of the output buffer passed into the data callback will always be pre-initialized to
3608 silence unless the `noPreSilencedOutputBuffer` config variable in `ma_device_config` is set to
3609 true, in which case it'll be undefined which will require you to write something to the entire
3610 buffer.
3611- By default miniaudio will automatically clip samples. This only applies when the playback sample
3612 format is configured as `ma_format_f32`. If you are doing clipping yourself, you can disable this
3613 overhead by setting `noClip` to true in the device config.
3614- Note that GCC and Clang requires `-msse2`, `-mavx2`, etc. for SIMD optimizations.
3615- The sndio backend is currently only enabled on OpenBSD builds.
3616- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can
3617 use it.
3618- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This
3619 is due to 64-bit file APIs not being available.
3620*/
3621
3622#ifndef miniaudio_h
3623#define miniaudio_h
3624
3625#ifdef __cplusplus
3626extern "C" {
3627#endif
3628
3629#define MA_STRINGIFY(x) #x
3630#define MA_XSTRINGIFY(x) MA_STRINGIFY(x)
3631
3632#define MA_VERSION_MAJOR 0
3633#define MA_VERSION_MINOR 11
3634#define MA_VERSION_REVISION 8
3635#define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION)
3636
3637#if defined(_MSC_VER) && !defined(__clang__)
3638 #pragma warning(push)
3639 #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
3640 #pragma warning(disable:4214) /* nonstandard extension used: bit field types other than int */
3641 #pragma warning(disable:4324) /* structure was padded due to alignment specifier */
3642#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
3643 #pragma GCC diagnostic push
3644 #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
3645 #if defined(__clang__)
3646 #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */
3647 #endif
3648#endif
3649
3650
3651
3652#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__)
3653 #define MA_SIZEOF_PTR 8
3654#else
3655 #define MA_SIZEOF_PTR 4
3656#endif
3657
3658#include <stddef.h> /* For size_t. */
3659
3660/* Sized types. */
3661#if defined(MA_USE_STDINT)
3662 #include <stdint.h>
3663 typedef int8_t ma_int8;
3664 typedef uint8_t ma_uint8;
3665 typedef int16_t ma_int16;
3666 typedef uint16_t ma_uint16;
3667 typedef int32_t ma_int32;
3668 typedef uint32_t ma_uint32;
3669 typedef int64_t ma_int64;
3670 typedef uint64_t ma_uint64;
3671#else
3672 typedef signed char ma_int8;
3673 typedef unsigned char ma_uint8;
3674 typedef signed short ma_int16;
3675 typedef unsigned short ma_uint16;
3676 typedef signed int ma_int32;
3677 typedef unsigned int ma_uint32;
3678 #if defined(_MSC_VER) && !defined(__clang__)
3679 typedef signed __int64 ma_int64;
3680 typedef unsigned __int64 ma_uint64;
3681 #else
3682 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
3683 #pragma GCC diagnostic push
3684 #pragma GCC diagnostic ignored "-Wlong-long"
3685 #if defined(__clang__)
3686 #pragma GCC diagnostic ignored "-Wc++11-long-long"
3687 #endif
3688 #endif
3689 typedef signed long long ma_int64;
3690 typedef unsigned long long ma_uint64;
3691 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
3692 #pragma GCC diagnostic pop
3693 #endif
3694 #endif
3695#endif /* MA_USE_STDINT */
3696
3697#if MA_SIZEOF_PTR == 8
3698 typedef ma_uint64 ma_uintptr;
3699#else
3701#endif
3702
3705#define MA_TRUE 1
3706#define MA_FALSE 0
3707
3708typedef void* ma_handle;
3709typedef void* ma_ptr;
3710typedef void (* ma_proc)(void);
3711
3712#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED)
3713typedef ma_uint16 wchar_t;
3714#endif
3715
3716/* Define NULL for some compilers. */
3717#ifndef NULL
3718#define NULL 0
3719#endif
3720
3721#if defined(SIZE_MAX)
3722 #define MA_SIZE_MAX SIZE_MAX
3723#else
3724 #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */
3725#endif
3726
3727
3728/* Platform/backend detection. */
3729#ifdef _WIN32
3730 #define MA_WIN32
3731 #if 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))
3732 #define MA_WIN32_UWP
3733 #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES)
3734 #define MA_WIN32_GDK
3735 #else
3736 #define MA_WIN32_DESKTOP
3737 #endif
3738#else
3739 #define MA_POSIX
3740
3741 /*
3742 Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented.
3743 You can use this to avoid including pthread.h in the header section. The downside is that it
3744 results in some fixed sized structures being declared for the various types that are used in
3745 miniaudio. The risk here is that these types might be too small for a given platform. This
3746 risk is yours to take and no support will be offered if you enable this option.
3747 */
3748 #ifndef MA_NO_PTHREAD_IN_HEADER
3749 #include <pthread.h> /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */
3750 typedef pthread_t ma_pthread_t;
3751 typedef pthread_mutex_t ma_pthread_mutex_t;
3752 typedef pthread_cond_t ma_pthread_cond_t;
3753 #else
3754 typedef ma_uintptr ma_pthread_t;
3755 typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t;
3756 typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t;
3757 #endif
3758
3759 #ifdef __unix__
3760 #define MA_UNIX
3761 #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
3762 #define MA_BSD
3763 #endif
3764 #endif
3765 #ifdef __linux__
3766 #define MA_LINUX
3767 #endif
3768 #ifdef __APPLE__
3769 #define MA_APPLE
3770 #endif
3771 #ifdef __ANDROID__
3772 #define MA_ANDROID
3773 #endif
3774 #ifdef __EMSCRIPTEN__
3775 #define MA_EMSCRIPTEN
3776 #endif
3777#endif
3778
3779
3780#ifdef _MSC_VER
3781 #define MA_INLINE __forceinline
3782#elif defined(__GNUC__)
3783 /*
3784 I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
3785 the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
3786 case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
3787 command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
3788 I am using "__inline__" only when we're compiling in strict ANSI mode.
3789 */
3790 #if defined(__STRICT_ANSI__)
3791 #define MA_INLINE __inline__ __attribute__((always_inline))
3792 #else
3793 #define MA_INLINE inline __attribute__((always_inline))
3794 #endif
3795#elif defined(__WATCOMC__)
3796 #define MA_INLINE __inline
3797#else
3798 #define MA_INLINE
3799#endif
3800
3801#if !defined(MA_API)
3802 #if defined(MA_DLL)
3803 #if defined(_WIN32)
3804 #define MA_DLL_IMPORT __declspec(dllimport)
3805 #define MA_DLL_EXPORT __declspec(dllexport)
3806 #define MA_DLL_PRIVATE static
3807 #else
3808 #if defined(__GNUC__) && __GNUC__ >= 4
3809 #define MA_DLL_IMPORT __attribute__((visibility("default")))
3810 #define MA_DLL_EXPORT __attribute__((visibility("default")))
3811 #define MA_DLL_PRIVATE __attribute__((visibility("hidden")))
3812 #else
3813 #define MA_DLL_IMPORT
3814 #define MA_DLL_EXPORT
3815 #define MA_DLL_PRIVATE static
3816 #endif
3817 #endif
3818
3819 #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
3820 #define MA_API MA_DLL_EXPORT
3821 #else
3822 #define MA_API MA_DLL_IMPORT
3823 #endif
3824 #define MA_PRIVATE MA_DLL_PRIVATE
3825 #else
3826 #define MA_API extern
3827 #define MA_PRIVATE static
3828 #endif
3829#endif
3830
3831/* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */
3832#define MA_SIMD_ALIGNMENT 32
3833
3834
3835/*
3836Logging Levels
3837==============
3838Log levels are only used to give logging callbacks some context as to the severity of a log message
3839so they can do filtering. All log levels will be posted to registered logging callbacks. If you
3840don't want to output a certain log level you can discriminate against the log level in the callback.
3841
3842MA_LOG_LEVEL_DEBUG
3843 Used for debugging. Useful for debug and test builds, but should be disabled in release builds.
3844
3845MA_LOG_LEVEL_INFO
3846 Informational logging. Useful for debugging. This will never be called from within the data
3847 callback.
3848
3849MA_LOG_LEVEL_WARNING
3850 Warnings. You should enable this in you development builds and action them when encounted. These
3851 logs usually indicate a potential problem or misconfiguration, but still allow you to keep
3852 running. This will never be called from within the data callback.
3853
3854MA_LOG_LEVEL_ERROR
3855 Error logging. This will be fired when an operation fails and is subsequently aborted. This can
3856 be fired from within the data callback, in which case the device will be stopped. You should
3857 always have this log level enabled.
3858*/
3859typedef enum
3860{
3866
3867/*
3868Variables needing to be accessed atomically should be declared with this macro for two reasons:
3869
3870 1) It allows people who read the code to identify a variable as such; and
3871 2) It forces alignment on platforms where it's required or optimal.
3872
3873Note that for x86/64, alignment is not strictly necessary, but does have some performance
3874implications. Where supported by the compiler, alignment will be used, but otherwise if the CPU
3875architecture does not require it, it will simply leave it unaligned. This is the case with old
3876versions of Visual Studio, which I've confirmed with at least VC6.
3877*/
3878#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
3879 #include <stdalign.h>
3880 #define MA_ATOMIC(alignment, type) alignas(alignment) type
3881#else
3882 #if defined(__GNUC__)
3883 /* GCC-style compilers. */
3884 #define MA_ATOMIC(alignment, type) type __attribute__((aligned(alignment)))
3885 #elif defined(_MSC_VER) && _MSC_VER > 1200 /* 1200 = VC6. Alignment not supported, but not necessary because x86 is the only supported target. */
3886 /* MSVC. */
3887 #define MA_ATOMIC(alignment, type) __declspec(align(alignment)) type
3888 #else
3889 /* Other compilers. */
3890 #define MA_ATOMIC(alignment, type) type
3891 #endif
3892#endif
3893
3894typedef struct ma_context ma_context;
3895typedef struct ma_device ma_device;
3896
3898typedef enum
3899{
3955} _ma_channel_position; /* Do not use `_ma_channel_position` directly. Use `ma_channel` instead. */
3956
3957typedef enum
3958{
3960 MA_ERROR = -1, /* A generic error. */
3978 MA_BUSY = -19,
4012
4013 /* General miniaudio-specific errors. */
4021 MA_LOOP = -107,
4022
4023 /* State errors. */
4028
4029 /* Operation errors. */
4035
4036
4037#define MA_MIN_CHANNELS 1
4038#ifndef MA_MAX_CHANNELS
4039#define MA_MAX_CHANNELS 254
4040#endif
4041
4042#ifndef MA_MAX_FILTER_ORDER
4043#define MA_MAX_FILTER_ORDER 8
4044#endif
4045
4046typedef enum
4047{
4050
4051typedef enum
4052{
4056
4057typedef enum
4058{
4063
4064typedef enum
4065{
4066 /*
4067 I like to keep these explicitly defined because they're used as a key into a lookup table. When items are
4068 added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample().
4069 */
4070 ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */
4072 ma_format_s16 = 2, /* Seems to be the most widely supported format. */
4073 ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */
4078
4079typedef enum
4080{
4081 /* Standard rates need to be in priority order. */
4082 ma_standard_sample_rate_48000 = 48000, /* Most common */
4084
4088
4093
4094 ma_standard_sample_rate_16000 = 16000, /* Extreme lows */
4097
4098 ma_standard_sample_rate_352800 = 352800, /* Extreme highs */
4100
4103 ma_standard_sample_rate_count = 14 /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */
4105
4106
4107typedef enum
4108{
4109 ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */
4110 ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */
4111 ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_router_config. */
4114
4115typedef enum
4116{
4119 ma_standard_channel_map_rfc3551, /* Based off AIFF. */
4122 ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */
4123 ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */
4124 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. */
4127
4128typedef enum
4129{
4133
4134
4135typedef struct
4136{
4138 void* (* onMalloc)(size_t sz, void* pUserData);
4139 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
4140 void (* onFree)(void* p, void* pUserData);
4142
4143typedef struct
4144{
4146} ma_lcg;
4147
4148
4149/* Spinlocks are 32-bit for compatibility reasons. */
4151
4152#ifndef MA_NO_THREADING
4153/* Thread priorities should be ordered such that the default priority of the worker thread is 0. */
4154typedef enum
4155{
4165
4166#if defined(MA_WIN32)
4167typedef ma_handle ma_thread;
4168#endif
4169#if defined(MA_POSIX)
4171#endif
4172
4173#if defined(MA_WIN32)
4174typedef ma_handle ma_mutex;
4175#endif
4176#if defined(MA_POSIX)
4178#endif
4179
4180#if defined(MA_WIN32)
4181typedef ma_handle ma_event;
4182#endif
4183#if defined(MA_POSIX)
4184typedef struct
4185{
4189} ma_event;
4190#endif /* MA_POSIX */
4191
4192#if defined(MA_WIN32)
4193typedef ma_handle ma_semaphore;
4194#endif
4195#if defined(MA_POSIX)
4196typedef struct
4197{
4201} ma_semaphore;
4202#endif /* MA_POSIX */
4203#else
4204/* 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. */
4205#ifndef MA_NO_DEVICE_IO
4206#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO";
4207#endif
4208#endif /* MA_NO_THREADING */
4209
4210
4211/*
4212Retrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required.
4213*/
4214MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);
4215
4216/*
4217Retrieves the version of miniaudio as a string which can be useful for logging purposes.
4218*/
4219MA_API const char* ma_version_string(void);
4220
4221
4222
4227#include <stdarg.h> /* For va_list. */
4228
4229#if defined(__has_attribute)
4230 #if __has_attribute(format)
4231 #define MA_ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va)))
4232 #endif
4233#endif
4234#ifndef MA_ATTRIBUTE_FORMAT
4235#define MA_ATTRIBUTE_FORMAT(fmt,va)
4236#endif
4237
4238#ifndef MA_MAX_LOG_CALLBACKS
4239#define MA_MAX_LOG_CALLBACKS 4
4240#endif
4241
4242
4243/*
4244The callback for handling log messages.
4245
4246
4247Parameters
4248----------
4249pUserData (in)
4250 The user data pointer that was passed into ma_log_register_callback().
4251
4252logLevel (in)
4253 The log level. This can be one of the following:
4254
4255 +----------------------+
4256 | Log Level |
4257 +----------------------+
4258 | MA_LOG_LEVEL_DEBUG |
4259 | MA_LOG_LEVEL_INFO |
4260 | MA_LOG_LEVEL_WARNING |
4261 | MA_LOG_LEVEL_ERROR |
4262 +----------------------+
4263
4264pMessage (in)
4265 The log message.
4266
4267
4268Remarks
4269-------
4270Do not modify the state of the device from inside the callback.
4271*/
4272typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage);
4273
4274typedef struct
4275{
4279
4281
4282
4283typedef struct
4284{
4287 ma_allocation_callbacks allocationCallbacks; /* Need to store these persistently because ma_log_postv() might need to allocate a buffer on the heap. */
4288#ifndef MA_NO_THREADING
4289 ma_mutex lock; /* For thread safety just to make it easier and safer for the logging implementation. */
4290#endif
4291} ma_log;
4292
4293MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog);
4297MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage);
4298MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args);
4299MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) MA_ATTRIBUTE_FORMAT(3, 4);
4300
4301
4302
4307typedef union
4308{
4309 float f32;
4312
4313typedef struct
4314{
4317 double b0;
4318 double b1;
4319 double b2;
4320 double a0;
4321 double a1;
4322 double a2;
4324
4325MA_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);
4326
4327typedef struct
4328{
4338
4339 /* Memory management. */
4340 void* _pHeap;
4342} ma_biquad;
4343
4344MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes);
4346MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ);
4347MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks);
4349MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4351
4352
4353
4358typedef struct
4359{
4364 double q;
4366
4369
4370typedef struct
4371{
4376
4377 /* Memory management. */
4378 void* _pHeap;
4380} ma_lpf1;
4381
4382MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes);
4384MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF);
4385MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);
4387MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4389
4390typedef struct
4391{
4392 ma_biquad bq; /* The second order low-pass filter is implemented as a biquad filter. */
4393} ma_lpf2;
4394
4395MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes);
4397MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF);
4398MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);
4400MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4402
4403
4404typedef struct
4405{
4410 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
4412
4414
4415typedef struct
4416{
4424
4425 /* Memory management. */
4426 void* _pHeap;
4428} ma_lpf;
4429
4430MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes);
4432MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF);
4433MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);
4435MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4437
4438
4439
4444typedef struct
4445{
4450 double q;
4452
4455
4456typedef struct
4457{
4462
4463 /* Memory management. */
4464 void* _pHeap;
4466} ma_hpf1;
4467
4468MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes);
4470MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pHPF);
4471MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks);
4473MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4475
4476typedef struct
4477{
4478 ma_biquad bq; /* The second order high-pass filter is implemented as a biquad filter. */
4479} ma_hpf2;
4480
4481MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes);
4483MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF);
4484MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks);
4486MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4488
4489
4490typedef struct
4491{
4496 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
4498
4500
4501typedef struct
4502{
4510
4511 /* Memory management. */
4512 void* _pHeap;
4514} ma_hpf;
4515
4516MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes);
4518MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF);
4519MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks);
4521MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4523
4524
4525
4530typedef struct
4531{
4536 double q;
4538
4540
4541typedef struct
4542{
4543 ma_biquad bq; /* The second order band-pass filter is implemented as a biquad filter. */
4544} ma_bpf2;
4545
4546MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes);
4548MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF);
4549MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks);
4551MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4553
4554
4555typedef struct
4556{
4561 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
4563
4565
4566typedef struct
4567{
4572
4573 /* Memory management. */
4574 void* _pHeap;
4576} ma_bpf;
4577
4578MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes);
4580MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF);
4581MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks);
4583MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4585
4586
4587
4592typedef struct
4593{
4597 double q;
4600
4602
4603typedef struct
4604{
4606} ma_notch2;
4607
4608MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes);
4610MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter);
4611MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);
4613MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4615
4616
4617
4622typedef struct
4623{
4627 double gainDB;
4628 double q;
4631
4633
4634typedef struct
4635{
4637} ma_peak2;
4638
4639MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes);
4641MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter);
4642MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);
4644MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4646
4647
4648
4653typedef struct
4654{
4658 double gainDB;
4662
4664
4665typedef struct
4666{
4668} ma_loshelf2;
4669
4670MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes);
4672MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter);
4673MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);
4675MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4677
4678
4679
4684typedef struct
4685{
4689 double gainDB;
4693
4695
4696typedef struct
4697{
4699} ma_hishelf2;
4700
4701MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes);
4703MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter);
4704MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);
4706MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4708
4709
4710
4711/*
4712Delay
4713*/
4714typedef struct
4715{
4719 ma_bool32 delayStart; /* Set to true to delay the start of the output; false otherwise. */
4720 float wet; /* 0..1. Default = 1. */
4721 float dry; /* 0..1. Default = 1. */
4722 float decay; /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */
4724
4726
4727
4728typedef struct
4729{
4731 ma_uint32 cursor; /* Feedback is written to this cursor. Always equal or in front of the read cursor. */
4732 ma_uint32 bufferSizeInFrames; /* The maximum of config.startDelayInFrames and config.feedbackDelayInFrames. */
4733 float* pBuffer;
4734} ma_delay;
4735
4736MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay);
4737MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks);
4738MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount);
4739MA_API void ma_delay_set_wet(ma_delay* pDelay, float value);
4740MA_API float ma_delay_get_wet(const ma_delay* pDelay);
4741MA_API void ma_delay_set_dry(ma_delay* pDelay, float value);
4742MA_API float ma_delay_get_dry(const ma_delay* pDelay);
4743MA_API void ma_delay_set_decay(ma_delay* pDelay, float value);
4745
4746
4747/* Gainer for smooth volume changes. */
4748typedef struct
4749{
4753
4755
4756
4757typedef struct
4758{
4763
4764 /* Memory management. */
4765 void* _pHeap;
4767} ma_gainer;
4768
4769MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes);
4771MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer);
4772MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks);
4773MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4775MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains);
4776
4777
4778
4779/* Stereo panner. */
4780typedef enum
4781{
4782 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. */
4783 ma_pan_mode_pan /* A true pan. The sound from one side will "move" to the other side and blend with it. */
4785
4786typedef struct
4787{
4791 float pan;
4793
4795
4796
4797typedef struct
4798{
4802 float pan; /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */
4803} ma_panner;
4804
4806MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4809MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan);
4810MA_API float ma_panner_get_pan(const ma_panner* pPanner);
4811
4812
4813
4814/* Fader. */
4815typedef struct
4816{
4821
4823
4824typedef struct
4825{
4827 float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */
4829 ma_uint64 lengthInFrames; /* The total length of the fade. */
4830 ma_uint64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). */
4831} ma_fader;
4832
4834MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4835MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
4836MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames);
4838
4839
4840
4841/* Spatializer. */
4842typedef struct
4843{
4844 float x;
4845 float y;
4846 float z;
4847} ma_vec3f;
4848
4849typedef enum
4850{
4851 ma_attenuation_model_none, /* No distance attenuation and no spatialization. */
4852 ma_attenuation_model_inverse, /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */
4853 ma_attenuation_model_linear, /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */
4854 ma_attenuation_model_exponential /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */
4856
4857typedef enum
4858{
4862
4863typedef enum
4864{
4868
4869
4870typedef struct
4871{
4874 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. */
4881
4883
4884
4885typedef struct
4886{
4888 ma_vec3f position; /* The absolute position of the listener. */
4889 ma_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */
4892
4893 /* Memory management. */
4895 void* _pHeap;
4897
4903MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
4904MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
4905MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z);
4909MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z);
4913MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z);
4917
4918
4919typedef struct
4920{
4926 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. */
4927 float minGain;
4928 float maxGain;
4931 float rolloff;
4935 float dopplerFactor; /* Set to 0 to disable doppler effect. */
4936 float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */
4937 ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */
4939
4941
4942
4943typedef struct
4944{
4950 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. */
4951 float minGain;
4952 float maxGain;
4955 float rolloff;
4959 float dopplerFactor; /* Set to 0 to disable doppler effect. */
4960 float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */
4961 ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */
4964 ma_vec3f velocity; /* For doppler effect. */
4965 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. */
4966 ma_gainer gainer; /* For smooth gain transitions. */
4967 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. */
4968
4969 /* Memory management. */
4970 void* _pHeap;
4973
4974MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes);
4976MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer);
4977MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks);
4978MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4985MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff);
4987MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain);
4989MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain);
4991MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance);
4993MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance);
4995MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
4996MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
4997MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor);
4999MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor);
5001MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z);
5003MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z);
5005MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z);
5007MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir);
5008
5009
5010
5011
5022
5027typedef struct
5028{
5033 ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */
5034 double lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */
5036
5038
5039typedef struct
5040{
5046 union
5047 {
5048 float* f32;
5050 } x0; /* The previous input frame. */
5051 union
5052 {
5053 float* f32;
5054 ma_int16* s16;
5055 } x1; /* The next input frame. */
5057
5058 /* Memory management. */
5059 void* _pHeap;
5062
5067MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
5074
5075
5077
5079typedef struct
5080{
5081 ma_result (* onGetHeapSize )(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes);
5082 ma_result (* onInit )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend);
5083 void (* onUninit )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks);
5084 ma_result (* onProcess )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
5085 ma_result (* onSetRate )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); /* Optional. Rate changes will be disabled. */
5086 ma_uint64 (* onGetInputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */
5087 ma_uint64 (* onGetOutputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */
5088 ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); /* Optional. Latency mitigation will be disabled. */
5089 ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* Optional. Latency mitigation will be disabled. */
5091
5092typedef enum
5093{
5094 ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */
5097
5099{
5100 ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */
5104 ma_resample_algorithm algorithm; /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */
5107 struct
5108 {
5111};
5112
5114
5115typedef struct
5116{
5124 union
5125 {
5127 } state; /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */
5128
5129 /* Memory management. */
5130 void* _pHeap;
5132} ma_resampler;
5133
5134MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes);
5136
5137/*
5138Initializes a new resampler object from a config.
5139*/
5140MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler);
5141
5142/*
5143Uninitializes a resampler.
5144*/
5145MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks);
5146
5147/*
5148Converts the given input data.
5149
5150Both the input and output frames must be in the format specified in the config when the resampler was initilized.
5151
5152On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that
5153were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use
5154ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames.
5155
5156On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole
5157input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames
5158you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead.
5159
5160If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of
5161output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input
5162frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be
5163processed. In this case, any internal filter state will be updated as if zeroes were passed in.
5164
5165It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL.
5166
5167It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL.
5168*/
5169MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
5170
5171
5172/*
5173Sets the input and output sample sample rate.
5174*/
5175MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
5176
5177/*
5178Sets the input and output sample rate as a ratio.
5179
5180The ration is in/out.
5181*/
5183
5184/*
5185Retrieves the latency introduced by the resampler in input frames.
5186*/
5188
5189/*
5190Retrieves the latency introduced by the resampler in output frames.
5191*/
5193
5194/*
5195Calculates the number of whole input frames that would need to be read from the client in order to output the specified
5196number of output frames.
5197
5198The returned value does not include cached input frames. It only returns the number of extra frames that would need to be
5199read from the input buffer in order to output the specified number of output frames.
5200*/
5201MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);
5202
5203/*
5204Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of
5205input frames.
5206*/
5208
5209
5210
5215typedef enum
5216{
5219 ma_channel_conversion_path_mono_out, /* Converting to mono. */
5220 ma_channel_conversion_path_mono_in, /* Converting from mono. */
5221 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. */
5222 ma_channel_conversion_path_weights /* Blended based on weights. */
5224
5225typedef enum
5226{
5227 ma_mono_expansion_mode_duplicate = 0, /* The default. */
5228 ma_mono_expansion_mode_average, /* Average the mono channel across all channels. */
5229 ma_mono_expansion_mode_stereo_only, /* Duplicate to the left and right channels only and ignore the others. */
5232
5233typedef struct
5234{
5241 float** ppWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
5243
5244MA_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);
5245
5246typedef struct
5247{
5255 ma_uint8* pShuffleTable; /* Indexed by output channel index. */
5256 union
5257 {
5258 float** f32;
5260 } weights; /* [in][out] */
5261
5262 /* Memory management. */
5263 void* _pHeap;
5266
5271MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
5274
5275
5276
5281typedef struct
5282{
5293 float** ppChannelWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
5297
5299MA_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);
5300
5301
5302typedef enum
5303{
5308 ma_data_converter_execution_path_resample_first, /* All conversions, but resample as the first step. */
5309 ma_data_converter_execution_path_channels_first /* All conversions, but channels as the first step. */
5311
5312typedef struct
5313{
5321 ma_data_converter_execution_path executionPath; /* The execution path the data converter will follow when processing. */
5329
5330 /* Memory management. */
5332 void* _pHeap;
5334
5338MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks);
5339MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
5346MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);
5347MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);
5348
5349
5350
5355MA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5356MA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5357MA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5358MA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5359MA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5360MA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5361MA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5362MA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5363MA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5364MA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5365MA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5366MA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5367MA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5368MA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5369MA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5370MA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5371MA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5372MA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5373MA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5374MA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5375MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode);
5376MA_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);
5377
5378/*
5379Deinterleaves an interleaved buffer.
5380*/
5381MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);
5382
5383/*
5384Interleaves a group of deinterleaved buffers.
5385*/
5386MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);
5387
5388
5389
5394/*
5395This is used in the shuffle table to indicate that the channel index is undefined and should be ignored.
5396*/
5397#define MA_CHANNEL_INDEX_NULL 255
5398
5399/*
5400Retrieves the channel position of the specified channel in the given channel map.
5401
5402The pChannelMap parameter can be null, in which case miniaudio's default channel map will be assumed.
5403*/
5404MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex);
5405
5406/*
5407Initializes a blank channel map.
5408
5409When a blank channel map is specified anywhere it indicates that the native channel map should be used.
5410*/
5412
5413/*
5414Helper for retrieving a standard channel map.
5415
5416The output channel map buffer must have a capacity of at least `channelMapCap`.
5417*/
5418MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels);
5419
5420/*
5421Copies a channel map.
5422
5423Both input and output channel map buffers must have a capacity of at at least `channels`.
5424*/
5426
5427/*
5428Copies a channel map if one is specified, otherwise copies the default channel map.
5429
5430The 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`.
5431*/
5432MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels);
5433
5434
5435/*
5436Determines whether or not a channel map is valid.
5437
5438A blank channel map is valid (all channels set to MA_CHANNEL_NONE). The way a blank channel map is handled is context specific, but
5439is usually treated as a passthrough.
5440
5441Invalid channel maps:
5442 - A channel map with no channels
5443 - A channel map with more than one channel and a mono channel
5444
5445The channel map buffer must have a capacity of at least `channels`.
5446*/
5448
5449/*
5450Helper for comparing two channel maps for equality.
5451
5452This assumes the channel count is the same between the two.
5453
5454Both channels map buffers must have a capacity of at least `channels`.
5455*/
5456MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels);
5457
5458/*
5459Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE).
5460
5461The channel map buffer must have a capacity of at least `channels`.
5462*/
5464
5465/*
5466Helper for determining whether or not a channel is present in the given channel map.
5467
5468The channel map buffer must have a capacity of at least `channels`.
5469*/
5471
5472
5473
5479/*
5480High-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
5481determine the required size of the output buffer. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is
5482ignored.
5483
5484A return value of 0 indicates an error.
5485
5486This function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_data_converter APIs instead.
5487*/
5488MA_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);
5489MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig);
5490
5491
5492
5497typedef struct
5498{
5499 void* pBuffer;
5503 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. */
5504 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. */
5505 ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */
5506 ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */
5508} ma_rb;
5509
5510MA_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);
5511MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB);
5514MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
5515MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes);
5516MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
5517MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes);
5518MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes);
5519MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes);
5520MA_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. */
5525MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex);
5526MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer);
5527
5528
5529typedef struct
5530{
5534} ma_pcm_rb;
5535
5536MA_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);
5537MA_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);
5540MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
5542MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
5546MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */
5552MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer);
5553
5554
5555/*
5556The idea of the duplex ring buffer is to act as the intermediary buffer when running two asynchronous devices in a duplex set up. The
5557capture device writes to it, and then a playback device reads from it.
5558
5559At the moment this is just a simple naive implementation, but in the future I want to implement some dynamic resampling to seamlessly
5560handle desyncs. Note that the API is work in progress and may change at any time in any version.
5561
5562The 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
5563in frames. The internal sample rate of the capture device is also needed in order to calculate the size.
5564*/
5565typedef struct
5566{
5568} ma_duplex_rb;
5569
5570MA_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);
5572
5573
5574
5579/*
5580Retrieves a human readable description of the given result code.
5581*/
5583
5584/*
5585malloc()
5586*/
5587MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
5588
5589/*
5590calloc()
5591*/
5592MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
5593
5594/*
5595realloc()
5596*/
5597MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
5598
5599/*
5600free()
5601*/
5602MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
5603
5604/*
5605Performs an aligned malloc, with the assumption that the alignment is a power of 2.
5606*/
5607MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks);
5608
5609/*
5610Free's an aligned malloc'd buffer.
5611*/
5612MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
5613
5614/*
5615Retrieves a friendly name for a format.
5616*/
5618
5619/*
5620Blends two frames in floating point format.
5621*/
5622MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels);
5623
5624/*
5625Retrieves the size of a sample in bytes for the given format.
5626
5627This API is efficient and is implemented using a lookup table.
5628
5629Thread Safety: SAFE
5630 This API is pure.
5631*/
5633static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; }
5634
5635/*
5636Converts a log level to a string.
5637*/
5639
5640
5641
5642
5643
5648/*
5649Locks a spinlock.
5650*/
5652
5653/*
5654Locks a spinlock, but does not yield() when looping.
5655*/
5657
5658/*
5659Unlocks a spinlock.
5660*/
5662
5663
5664#ifndef MA_NO_THREADING
5665
5666/*
5667Creates a mutex.
5668
5669A mutex must be created from a valid context. A mutex is initially unlocked.
5670*/
5672
5673/*
5674Deletes a mutex.
5675*/
5677
5678/*
5679Locks a mutex with an infinite timeout.
5680*/
5682
5683/*
5684Unlocks a mutex.
5685*/
5687
5688
5689/*
5690Initializes an auto-reset event.
5691*/
5693
5694/*
5695Uninitializes an auto-reset event.
5696*/
5698
5699/*
5700Waits for the specified auto-reset event to become signalled.
5701*/
5703
5704/*
5705Signals the specified auto-reset event.
5706*/
5708#endif /* MA_NO_THREADING */
5709
5710
5711/*
5712Fence
5713=====
5714This locks while the counter is larger than 0. Counter can be incremented and decremented by any
5715thread, but care needs to be taken when waiting. It is possible for one thread to acquire the
5716fence just as another thread returns from ma_fence_wait().
5717
5718The idea behind a fence is to allow you to wait for a group of operations to complete. When an
5719operation starts, the counter is incremented which locks the fence. When the operation completes,
5720the fence will be released which decrements the counter. ma_fence_wait() will block until the
5721counter hits zero.
5722
5723If threading is disabled, ma_fence_wait() will spin on the counter.
5724*/
5725typedef struct
5726{
5727#ifndef MA_NO_THREADING
5729#endif
5731} ma_fence;
5732
5735MA_API ma_result ma_fence_acquire(ma_fence* pFence); /* Increment counter. */
5736MA_API ma_result ma_fence_release(ma_fence* pFence); /* Decrement counter. */
5737MA_API ma_result ma_fence_wait(ma_fence* pFence); /* Wait for counter to reach 0. */
5738
5739
5740
5741/*
5742Notification callback for asynchronous operations.
5743*/
5745
5746typedef struct
5747{
5748 void (* onSignal)(ma_async_notification* pNotification);
5750
5752
5753
5754/*
5755Simple polling notification.
5756
5757This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled()
5758*/
5759typedef struct
5760{
5764
5767
5768
5769/*
5770Event Notification
5771
5772This uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail.
5773*/
5774typedef struct
5775{
5777#ifndef MA_NO_THREADING
5779#endif
5781
5786
5787
5788
5789
5790
5796/*
5797Slot Allocator
5798--------------
5799The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocator an index that can be used
5800as the insertion point for an object.
5801
5802Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs.
5803
5804The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits:
5805
5806 +-----------------+-----------------+
5807 | 32 Bits | 32 Bits |
5808 +-----------------+-----------------+
5809 | Reference Count | Slot Index |
5810 +-----------------+-----------------+
5811*/
5812typedef struct
5813{
5814 ma_uint32 capacity; /* The number of slots to make available. */
5816
5818
5819
5820typedef struct
5821{
5822 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. */
5824
5825typedef struct
5826{
5827 ma_slot_allocator_group* pGroups; /* Slots are grouped in chunks of 32. */
5828 ma_uint32* pSlots; /* 32 bits for reference counting for ABA mitigation. */
5829 ma_uint32 count; /* Allocation count. */
5831
5832 /* Memory management. */
5834 void* _pHeap;
5836
5840MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks);
5843
5844
5845typedef struct ma_job ma_job;
5846
5847/*
5848Callback for processing a job. Each job type will have their own processing callback which will be
5849called by ma_job_process().
5850*/
5851typedef ma_result (* ma_job_proc)(ma_job* pJob);
5852
5853/* When a job type is added here an callback needs to be added go "g_jobVTable" in the implementation section. */
5854typedef enum
5855{
5856 /* Miscellaneous. */
5859
5860 /* Resource Manager. */
5870
5871 /* Device. */
5873
5874 /* Count. Must always be last. */
5877
5879{
5880 union
5881 {
5882 struct
5883 {
5884 ma_uint16 code; /* Job type. */
5885 ma_uint16 slot; /* Index into a ma_slot_allocator. */
5889 } toc; /* 8 bytes. We encode the job code into the slot allocation data to save space. */
5890 MA_ATOMIC(8, ma_uint64) next; /* refcount + slot for the next item. Does not include the job code. */
5891 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. */
5892
5893 union
5894 {
5895 /* Miscellaneous. */
5896 struct
5897 {
5902
5903 /* Resource Manager */
5904 union
5905 {
5906 struct
5907 {
5908 /*ma_resource_manager**/ void* pResourceManager;
5909 /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode;
5911 wchar_t* pFilePathW;
5912 ma_bool32 decode; /* When set to true, the data buffer will be decoded. Otherwise it'll be encoded and will use a decoder for the connector. */
5913 ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */
5914 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. */
5915 ma_fence* pInitFence; /* Released when initialization of the decoder is complete. */
5916 ma_fence* pDoneFence; /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */
5918 struct
5919 {
5920 /*ma_resource_manager**/ void* pResourceManager;
5921 /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode;
5925 struct
5926 {
5927 /*ma_resource_manager**/ void* pResourceManager;
5928 /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode;
5929 /*ma_decoder**/ void* pDecoder;
5930 ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */
5931 ma_fence* pDoneFence; /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */
5933
5934 struct
5935 {
5936 /*ma_resource_manager_data_buffer**/ void* pDataBuffer;
5937 ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */
5938 ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */
5939 ma_fence* pInitFence; /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */
5940 ma_fence* pDoneFence; /* Released when the data buffer has been fully decoded. */
5942 struct
5943 {
5944 /*ma_resource_manager_data_buffer**/ void* pDataBuffer;
5948
5949 struct
5950 {
5951 /*ma_resource_manager_data_stream**/ void* pDataStream;
5952 char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */
5953 wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */
5955 ma_async_notification* pInitNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */
5958 struct
5959 {
5960 /*ma_resource_manager_data_stream**/ void* pDataStream;
5964 struct
5965 {
5966 /*ma_resource_manager_data_stream**/ void* pDataStream;
5967 ma_uint32 pageIndex; /* The index of the page to decode into. */
5969 struct
5970 {
5971 /*ma_resource_manager_data_stream**/ void* pDataStream;
5975
5976 /* Device. */
5977 union
5978 {
5979 union
5980 {
5981 struct
5982 {
5983 /*ma_device**/ void* pDevice;
5984 /*ma_device_type*/ ma_uint32 deviceType;
5989};
5990
5993
5994
5995/*
5996When set, ma_job_queue_next() will not wait and no semaphore will be signaled in
5997ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available.
5998
5999This flag should always be used for platforms that do not support multithreading.
6000*/
6001typedef enum
6002{
6005
6006typedef struct
6007{
6009 ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. */
6011
6013
6014
6015typedef struct
6016{
6017 ma_uint32 flags; /* Flags passed in at initialization time. */
6018 ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */
6019 MA_ATOMIC(8, ma_uint64) head; /* The first item in the list. Required for removing from the top of the list. */
6020 MA_ATOMIC(8, ma_uint64) tail; /* The last item in the list. Required for appending to the end of the list. */
6021#ifndef MA_NO_THREADING
6022 ma_semaphore sem; /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */
6023#endif
6026#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
6028#endif
6029
6030 /* Memory management. */
6031 void* _pHeap;
6033} ma_job_queue;
6034
6035MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes);
6038MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks);
6040MA_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. */
6041
6042
6043
6044
6054#ifndef MA_NO_DEVICE_IO
6055/* Some backends are only supported on certain platforms. */
6056#if defined(MA_WIN32)
6057 #define MA_SUPPORT_WASAPI
6058 #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */
6059 #define MA_SUPPORT_DSOUND
6060 #define MA_SUPPORT_WINMM
6061 #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */
6062 #endif
6063#endif
6064#if defined(MA_UNIX)
6065 #if defined(MA_LINUX)
6066 #if !defined(MA_ANDROID) /* ALSA is not supported on Android. */
6067 #define MA_SUPPORT_ALSA
6068 #endif
6069 #endif
6070 #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN)
6071 #define MA_SUPPORT_PULSEAUDIO
6072 #define MA_SUPPORT_JACK
6073 #endif
6074 #if defined(MA_ANDROID)
6075 #define MA_SUPPORT_AAUDIO
6076 #define MA_SUPPORT_OPENSL
6077 #endif
6078 #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */
6079 #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */
6080 #endif
6081 #if defined(__NetBSD__) || defined(__OpenBSD__)
6082 #define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */
6083 #endif
6084 #if defined(__FreeBSD__) || defined(__DragonFly__)
6085 #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */
6086 #endif
6087#endif
6088#if defined(MA_APPLE)
6089 #define MA_SUPPORT_COREAUDIO
6090#endif
6091#if defined(MA_EMSCRIPTEN)
6092 #define MA_SUPPORT_WEBAUDIO
6093#endif
6094
6095/* All platforms should support custom backends. */
6096#define MA_SUPPORT_CUSTOM
6097
6098/* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */
6099#if !defined(MA_EMSCRIPTEN)
6100#define MA_SUPPORT_NULL
6101#endif
6102
6103
6104#if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI))
6105 #define MA_HAS_WASAPI
6106#endif
6107#if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND))
6108 #define MA_HAS_DSOUND
6109#endif
6110#if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM))
6111 #define MA_HAS_WINMM
6112#endif
6113#if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA))
6114 #define MA_HAS_ALSA
6115#endif
6116#if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO))
6117 #define MA_HAS_PULSEAUDIO
6118#endif
6119#if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK))
6120 #define MA_HAS_JACK
6121#endif
6122#if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO))
6123 #define MA_HAS_COREAUDIO
6124#endif
6125#if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO))
6126 #define MA_HAS_SNDIO
6127#endif
6128#if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4))
6129 #define MA_HAS_AUDIO4
6130#endif
6131#if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS))
6132 #define MA_HAS_OSS
6133#endif
6134#if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO))
6135 #define MA_HAS_AAUDIO
6136#endif
6137#if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL))
6138 #define MA_HAS_OPENSL
6139#endif
6140#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO))
6141 #define MA_HAS_WEBAUDIO
6142#endif
6143#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM))
6144 #define MA_HAS_CUSTOM
6145#endif
6146#if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL))
6147 #define MA_HAS_NULL
6148#endif
6149
6150typedef enum
6151{
6153 ma_device_state_stopped = 1, /* The device's default state after initialization. */
6154 ma_device_state_started = 2, /* The device is started and is requesting and/or delivering audio data. */
6155 ma_device_state_starting = 3, /* Transitioning from a stopped state to started. */
6156 ma_device_state_stopping = 4 /* Transitioning from a started state to stopped. */
6158
6159#ifdef MA_SUPPORT_WASAPI
6160/* We need a IMMNotificationClient object for WASAPI. */
6161typedef struct
6162{
6163 void* lpVtbl;
6164 ma_uint32 counter;
6165 ma_device* pDevice;
6166} ma_IMMNotificationClient;
6167#endif
6168
6169/* Backend enums must be in priority order. */
6170typedef enum
6171{
6185 ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */
6186 ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */
6188
6189#define MA_BACKEND_COUNT (ma_backend_null+1)
6190
6191
6192/*
6193Device job thread. This is used by backends that require asynchronous processing of certain
6194operations. It is not used by all backends.
6195
6196The device job thread is made up of a thread and a job queue. You can post a job to the thread with
6197ma_device_job_thread_post(). The thread will do the processing of the job.
6198*/
6199typedef struct
6200{
6201 ma_bool32 noThread; /* Set this to true if you want to process jobs yourself. */
6205
6207
6208typedef struct
6209{
6214
6219
6220
6221
6222/* Device notification types. */
6223typedef enum
6224{
6231
6232typedef struct
6233{
6236 union
6237 {
6238 struct
6239 {
6241 } started;
6242 struct
6243 {
6244 int _unused;
6245 } stopped;
6246 struct
6247 {
6248 int _unused;
6249 } rerouted;
6250 struct
6251 {
6252 int _unused;
6253 } interruption;
6254 } data;
6256
6257/*
6258The notification callback for when the application should be notified of a change to the device.
6259
6260This callback is used for notifying the application of changes such as when the device has started,
6261stopped, rerouted or an interruption has occurred. Note that not all backends will post all
6262notification types. For example, some backends will perform automatic stream routing without any
6263kind of notification to the host program which means miniaudio will never know about it and will
6264never be able to fire the rerouted notification. You should keep this in mind when designing your
6265program.
6266
6267The stopped notification will *not* get fired when a device is rerouted.
6268
6269
6270Parameters
6271----------
6272pNotification (in)
6273 A pointer to a structure containing information about the event. Use the `pDevice` member of
6274 this object to retrieve the relevant device. The `type` member can be used to discriminate
6275 against each of the notification types.
6276
6277
6278Remarks
6279-------
6280Do not restart or uninitialize the device from the callback.
6281
6282Not all notifications will be triggered by all backends, however the started and stopped events
6283should be reliable for all backends. Some backends do not have a good way to detect device
6284stoppages due to unplugging the device which may result in the stopped callback not getting
6285fired. This has been observed with at least one BSD variant.
6286
6287The rerouted notification is fired *after* the reroute has occurred. The stopped notification will
6288*not* get fired when a device is rerouted. The following backends are known to do automatic stream
6289rerouting, but do not have a way to be notified of the change:
6290
6291 * DirectSound
6292
6293The interruption notifications are used on mobile platforms for detecting when audio is interrupted
6294due to things like an incoming phone call. Currently this is only implemented on iOS. None of the
6295Android backends will report this notification.
6296*/
6297typedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification);
6298
6299
6300/*
6301The callback for processing audio data from the device.
6302
6303The 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
6304available. 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
6305callback will be fired with a consistent frame count.
6306
6307
6308Parameters
6309----------
6310pDevice (in)
6311 A pointer to the relevant device.
6312
6313pOutput (out)
6314 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
6315 full-duplex device and null for a capture and loopback device.
6316
6317pInput (in)
6318 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
6319 playback device.
6320
6321frameCount (in)
6322 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
6323 `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must
6324 not assume this will always be the same value each time the callback is fired.
6325
6326
6327Remarks
6328-------
6329You 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
6330callback. The following APIs cannot be called from inside the callback:
6331
6332 ma_device_init()
6333 ma_device_init_ex()
6334 ma_device_uninit()
6335 ma_device_start()
6336 ma_device_stop()
6337
6338The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread.
6339*/
6340typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
6341
6342
6343
6344
6345/*
6346DEPRECATED. Use ma_device_notification_proc instead.
6347
6348The callback for when the device has been stopped.
6349
6350This 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
6351such as being unplugged or an internal error occuring.
6352
6353
6354Parameters
6355----------
6356pDevice (in)
6357 A pointer to the device that has just stopped.
6358
6359
6360Remarks
6361-------
6362Do not restart or uninitialize the device from the callback.
6363*/
6364typedef void (* ma_stop_proc)(ma_device* pDevice); /* DEPRECATED. Use ma_device_notification_proc instead. */
6365
6366typedef enum
6367{
6373
6374typedef enum
6375{
6379
6380/* iOS/tvOS/watchOS session categories. */
6381typedef enum
6382{
6383 ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord with AVAudioSessionCategoryOptionDefaultToSpeaker. */
6384 ma_ios_session_category_none, /* Leave the session category unchanged. */
6385 ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */
6386 ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */
6387 ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */
6388 ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */
6389 ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */
6390 ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */
6392
6393/* iOS/tvOS/watchOS session category options */
6394typedef enum
6395{
6396 ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */
6397 ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */
6398 ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */
6399 ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */
6400 ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */
6401 ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */
6402 ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */
6404
6405/* OpenSL stream types. */
6406typedef enum
6407{
6408 ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */
6409 ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */
6410 ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */
6411 ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */
6412 ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */
6413 ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */
6414 ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */
6416
6417/* OpenSL recording presets. */
6418typedef enum
6419{
6420 ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */
6421 ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */
6422 ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */
6423 ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */
6424 ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */
6425 ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */
6427
6428/* AAudio usage types. */
6429typedef enum
6430{
6431 ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */
6432 ma_aaudio_usage_announcement, /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */
6433 ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */
6434 ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */
6435 ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */
6436 ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */
6437 ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */
6438 ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */
6439 ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */
6440 ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */
6441 ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */
6442 ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */
6443 ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */
6444 ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */
6445 ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */
6446 ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */
6447 ma_aaudio_usage_voice_communication_signalling /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */
6449
6450/* AAudio content types. */
6451typedef enum
6452{
6453 ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */
6454 ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */
6455 ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */
6456 ma_aaudio_content_type_sonification, /* AAUDIO_CONTENT_TYPE_SONIFICATION */
6457 ma_aaudio_content_type_speech /* AAUDIO_CONTENT_TYPE_SPEECH */
6459
6460/* AAudio input presets. */
6461typedef enum
6462{
6463 ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */
6464 ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */
6465 ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */
6466 ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */
6467 ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */
6468 ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */
6469 ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */
6471
6472
6473typedef union
6474{
6476 double counterD;
6477} ma_timer;
6478
6479typedef union
6480{
6481 wchar_t wasapi[64]; /* WASAPI uses a wchar_t string for identification. */
6482 ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */
6483 /*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. */
6484 char alsa[256]; /* ALSA uses a name string for identification. */
6485 char pulse[256]; /* PulseAudio uses a name string for identification. */
6486 int jack; /* JACK always uses default devices. */
6487 char coreaudio[256]; /* Core Audio uses a string for identification. */
6488 char sndio[256]; /* "snd/0", etc. */
6489 char audio4[256]; /* "/dev/audio", etc. */
6490 char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */
6491 ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */
6492 ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */
6493 char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */
6494 union
6495 {
6496 int i;
6497 char s[256];
6498 void* p;
6499 } custom; /* The custom backend could be anything. Give them a few options. */
6500 int nullbackend; /* The null backend uses an integer for device IDs. */
6501} ma_device_id;
6502
6503
6507
6508#define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */
6509
6510#ifndef MA_MAX_DEVICE_NAME_LENGTH
6511#define MA_MAX_DEVICE_NAME_LENGTH 255
6512#endif
6513
6514typedef struct
6515{
6516 /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */
6518 char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* +1 for null terminator. */
6520
6522 struct
6523 {
6524 ma_format format; /* Sample format. If set to ma_format_unknown, all sample formats are supported. */
6525 ma_uint32 channels; /* If set to 0, all channels are supported. */
6526 ma_uint32 sampleRate; /* If set to 0, all sample rates are supported. */
6527 ma_uint32 flags; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */
6528 } 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. */
6530
6532{
6539 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. */
6540 ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. Only applies when the playback sample format is f32. */
6541 ma_bool8 noDisableDenormals; /* Do not disable denormals when firing the data callback. */
6542 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. */
6548 struct
6549 {
6557 struct
6558 {
6559 const ma_device_id* pDeviceID;
6566
6567 struct
6568 {
6569 ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
6570 ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
6571 ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */
6572 ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */
6574 struct
6575 {
6576 ma_bool32 noMMap; /* Disables MMap mode. */
6577 ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */
6578 ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */
6579 ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */
6581 struct
6582 {
6586 struct
6587 {
6588 ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */
6590 struct
6591 {
6595 struct
6596 {
6602};
6603
6604
6605/*
6606The callback for handling device enumeration. This is fired from `ma_context_enumerated_devices()`.
6607
6608
6609Parameters
6610----------
6611pContext (in)
6612 A pointer to the context performing the enumeration.
6613
6614deviceType (in)
6615 The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`.
6616
6617pInfo (in)
6618 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,
6619 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
6620 is too inefficient.
6621
6622pUserData (in)
6623 The user data pointer passed into `ma_context_enumerate_devices()`.
6624*/
6625typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData);
6626
6627
6628/*
6629Describes some basic details about a playback or capture device.
6630*/
6631typedef struct
6632{
6643
6644/*
6645These are the callbacks required to be implemented for a backend. These callbacks are grouped into two parts: context and device. There is one context
6646to many devices. A device is created from a context.
6647
6648The general flow goes like this:
6649
6650 1) A context is created with `onContextInit()`
6651 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required.
6652 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required.
6653 2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was
6654 selected from device enumeration via `onContextEnumerateDevices()`.
6655 3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()`
6656 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
6657 to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by
6658 miniaudio internally.
6659
6660Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the
6661callbacks defined in this structure.
6662
6663Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which
6664physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the
6665given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration
6666needs to stop and the `onContextEnumerateDevices()` function returns with a success code.
6667
6668Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID,
6669and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the
6670case when the device ID is NULL, in which case information about the default device needs to be retrieved.
6671
6672Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created.
6673This is a little bit more complicated than initialization of the context due to it's more complicated configuration. When initializing a
6674device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input,
6675the 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
6676the requested format. The conversion between the format requested by the application and the device's native format will be handled
6677internally by miniaudio.
6678
6679On 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
6680supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for
6681sample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to
6682`MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should
6683inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period
6684size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the
6685sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_data_format`
6686object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set).
6687
6688Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses
6689asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented.
6690
6691The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit
6692easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and
6693`onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the
6694backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback.
6695This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback.
6696
6697If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback
6698which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional.
6699
6700The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been
6701encounted. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback.
6702
6703The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this
6704callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated
6705which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback,
6706look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to
6707wake up the audio thread.
6708
6709If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the
6710`onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient.
6711*/
6713{
6714 ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks);
6717 ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo);
6718 ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture);
6722 ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead);
6723 ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten);
6727};
6728
6730{
6736 struct
6737 {
6740 struct
6741 {
6742 const char* pApplicationName;
6743 const char* pServerName;
6744 ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */
6746 struct
6747 {
6750 ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */
6751 ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */
6753 struct
6754 {
6755 const char* pClientName;
6759};
6760
6761/* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */
6762typedef struct
6763{
6764 int code;
6765 ma_event* pEvent; /* This will be signalled when the event is complete. */
6766 union
6767 {
6768 struct
6769 {
6771 } quit;
6772 struct
6773 {
6777 ma_result* pResult; /* The result from creating the audio client service. */
6778 } createAudioClient;
6779 struct
6780 {
6782 ma_device_type deviceType;
6783 } releaseAudioClient;
6784 } data;
6786
6788{
6790 ma_backend backend; /* DirectSound, ALSA, etc. */
6792 ma_log log; /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */
6797 ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */
6798 ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */
6799 ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */
6802 ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */
6803
6804 union
6805 {
6806#ifdef MA_SUPPORT_WASAPI
6807 struct
6808 {
6809 ma_thread commandThread;
6810 ma_mutex commandLock;
6811 ma_semaphore commandSem;
6812 ma_uint32 commandIndex;
6813 ma_uint32 commandCount;
6814 ma_context_command__wasapi commands[4];
6815 } wasapi;
6816#endif
6817#ifdef MA_SUPPORT_DSOUND
6818 struct
6819 {
6820 ma_handle hDSoundDLL;
6821 ma_proc DirectSoundCreate;
6822 ma_proc DirectSoundEnumerateA;
6823 ma_proc DirectSoundCaptureCreate;
6824 ma_proc DirectSoundCaptureEnumerateA;
6825 } dsound;
6826#endif
6827#ifdef MA_SUPPORT_WINMM
6828 struct
6829 {
6830 ma_handle hWinMM;
6831 ma_proc waveOutGetNumDevs;
6832 ma_proc waveOutGetDevCapsA;
6833 ma_proc waveOutOpen;
6834 ma_proc waveOutClose;
6835 ma_proc waveOutPrepareHeader;
6836 ma_proc waveOutUnprepareHeader;
6837 ma_proc waveOutWrite;
6838 ma_proc waveOutReset;
6839 ma_proc waveInGetNumDevs;
6840 ma_proc waveInGetDevCapsA;
6841 ma_proc waveInOpen;
6842 ma_proc waveInClose;
6843 ma_proc waveInPrepareHeader;
6844 ma_proc waveInUnprepareHeader;
6845 ma_proc waveInAddBuffer;
6846 ma_proc waveInStart;
6847 ma_proc waveInReset;
6848 } winmm;
6849#endif
6850#ifdef MA_SUPPORT_ALSA
6851 struct
6852 {
6853 ma_handle asoundSO;
6854 ma_proc snd_pcm_open;
6855 ma_proc snd_pcm_close;
6856 ma_proc snd_pcm_hw_params_sizeof;
6857 ma_proc snd_pcm_hw_params_any;
6858 ma_proc snd_pcm_hw_params_set_format;
6859 ma_proc snd_pcm_hw_params_set_format_first;
6860 ma_proc snd_pcm_hw_params_get_format_mask;
6861 ma_proc snd_pcm_hw_params_set_channels;
6862 ma_proc snd_pcm_hw_params_set_channels_near;
6863 ma_proc snd_pcm_hw_params_set_channels_minmax;
6864 ma_proc snd_pcm_hw_params_set_rate_resample;
6865 ma_proc snd_pcm_hw_params_set_rate;
6866 ma_proc snd_pcm_hw_params_set_rate_near;
6867 ma_proc snd_pcm_hw_params_set_buffer_size_near;
6868 ma_proc snd_pcm_hw_params_set_periods_near;
6869 ma_proc snd_pcm_hw_params_set_access;
6870 ma_proc snd_pcm_hw_params_get_format;
6871 ma_proc snd_pcm_hw_params_get_channels;
6872 ma_proc snd_pcm_hw_params_get_channels_min;
6873 ma_proc snd_pcm_hw_params_get_channels_max;
6874 ma_proc snd_pcm_hw_params_get_rate;
6875 ma_proc snd_pcm_hw_params_get_rate_min;
6876 ma_proc snd_pcm_hw_params_get_rate_max;
6877 ma_proc snd_pcm_hw_params_get_buffer_size;
6878 ma_proc snd_pcm_hw_params_get_periods;
6879 ma_proc snd_pcm_hw_params_get_access;
6880 ma_proc snd_pcm_hw_params_test_format;
6881 ma_proc snd_pcm_hw_params_test_channels;
6882 ma_proc snd_pcm_hw_params_test_rate;
6883 ma_proc snd_pcm_hw_params;
6884 ma_proc snd_pcm_sw_params_sizeof;
6885 ma_proc snd_pcm_sw_params_current;
6886 ma_proc snd_pcm_sw_params_get_boundary;
6887 ma_proc snd_pcm_sw_params_set_avail_min;
6888 ma_proc snd_pcm_sw_params_set_start_threshold;
6889 ma_proc snd_pcm_sw_params_set_stop_threshold;
6890 ma_proc snd_pcm_sw_params;
6891 ma_proc snd_pcm_format_mask_sizeof;
6892 ma_proc snd_pcm_format_mask_test;
6893 ma_proc snd_pcm_get_chmap;
6894 ma_proc snd_pcm_state;
6895 ma_proc snd_pcm_prepare;
6896 ma_proc snd_pcm_start;
6897 ma_proc snd_pcm_drop;
6898 ma_proc snd_pcm_drain;
6899 ma_proc snd_pcm_reset;
6900 ma_proc snd_device_name_hint;
6901 ma_proc snd_device_name_get_hint;
6902 ma_proc snd_card_get_index;
6903 ma_proc snd_device_name_free_hint;
6904 ma_proc snd_pcm_mmap_begin;
6905 ma_proc snd_pcm_mmap_commit;
6906 ma_proc snd_pcm_recover;
6907 ma_proc snd_pcm_readi;
6908 ma_proc snd_pcm_writei;
6909 ma_proc snd_pcm_avail;
6910 ma_proc snd_pcm_avail_update;
6911 ma_proc snd_pcm_wait;
6912 ma_proc snd_pcm_nonblock;
6913 ma_proc snd_pcm_info;
6914 ma_proc snd_pcm_info_sizeof;
6915 ma_proc snd_pcm_info_get_name;
6916 ma_proc snd_pcm_poll_descriptors;
6917 ma_proc snd_pcm_poll_descriptors_count;
6918 ma_proc snd_pcm_poll_descriptors_revents;
6919 ma_proc snd_config_update_free_global;
6920
6921 ma_mutex internalDeviceEnumLock;
6922 ma_bool32 useVerboseDeviceEnumeration;
6923 } alsa;
6924#endif
6925#ifdef MA_SUPPORT_PULSEAUDIO
6926 struct
6927 {
6928 ma_handle pulseSO;
6929 ma_proc pa_mainloop_new;
6930 ma_proc pa_mainloop_free;
6931 ma_proc pa_mainloop_quit;
6932 ma_proc pa_mainloop_get_api;
6933 ma_proc pa_mainloop_iterate;
6934 ma_proc pa_mainloop_wakeup;
6935 ma_proc pa_threaded_mainloop_new;
6936 ma_proc pa_threaded_mainloop_free;
6937 ma_proc pa_threaded_mainloop_start;
6938 ma_proc pa_threaded_mainloop_stop;
6939 ma_proc pa_threaded_mainloop_lock;
6940 ma_proc pa_threaded_mainloop_unlock;
6941 ma_proc pa_threaded_mainloop_wait;
6942 ma_proc pa_threaded_mainloop_signal;
6943 ma_proc pa_threaded_mainloop_accept;
6944 ma_proc pa_threaded_mainloop_get_retval;
6945 ma_proc pa_threaded_mainloop_get_api;
6946 ma_proc pa_threaded_mainloop_in_thread;
6947 ma_proc pa_threaded_mainloop_set_name;
6948 ma_proc pa_context_new;
6949 ma_proc pa_context_unref;
6950 ma_proc pa_context_connect;
6951 ma_proc pa_context_disconnect;
6952 ma_proc pa_context_set_state_callback;
6953 ma_proc pa_context_get_state;
6954 ma_proc pa_context_get_sink_info_list;
6955 ma_proc pa_context_get_source_info_list;
6956 ma_proc pa_context_get_sink_info_by_name;
6957 ma_proc pa_context_get_source_info_by_name;
6958 ma_proc pa_operation_unref;
6959 ma_proc pa_operation_get_state;
6960 ma_proc pa_channel_map_init_extend;
6961 ma_proc pa_channel_map_valid;
6962 ma_proc pa_channel_map_compatible;
6963 ma_proc pa_stream_new;
6964 ma_proc pa_stream_unref;
6965 ma_proc pa_stream_connect_playback;
6966 ma_proc pa_stream_connect_record;
6967 ma_proc pa_stream_disconnect;
6968 ma_proc pa_stream_get_state;
6969 ma_proc pa_stream_get_sample_spec;
6970 ma_proc pa_stream_get_channel_map;
6971 ma_proc pa_stream_get_buffer_attr;
6972 ma_proc pa_stream_set_buffer_attr;
6973 ma_proc pa_stream_get_device_name;
6974 ma_proc pa_stream_set_write_callback;
6975 ma_proc pa_stream_set_read_callback;
6976 ma_proc pa_stream_set_suspended_callback;
6977 ma_proc pa_stream_set_moved_callback;
6978 ma_proc pa_stream_is_suspended;
6979 ma_proc pa_stream_flush;
6980 ma_proc pa_stream_drain;
6981 ma_proc pa_stream_is_corked;
6982 ma_proc pa_stream_cork;
6983 ma_proc pa_stream_trigger;
6984 ma_proc pa_stream_begin_write;
6985 ma_proc pa_stream_write;
6986 ma_proc pa_stream_peek;
6987 ma_proc pa_stream_drop;
6988 ma_proc pa_stream_writable_size;
6989 ma_proc pa_stream_readable_size;
6990
6991 /*pa_mainloop**/ ma_ptr pMainLoop;
6992 /*pa_context**/ ma_ptr pPulseContext;
6993 char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */
6994 char* pServerName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */
6995 } pulse;
6996#endif
6997#ifdef MA_SUPPORT_JACK
6998 struct
6999 {
7000 ma_handle jackSO;
7001 ma_proc jack_client_open;
7002 ma_proc jack_client_close;
7003 ma_proc jack_client_name_size;
7004 ma_proc jack_set_process_callback;
7005 ma_proc jack_set_buffer_size_callback;
7006 ma_proc jack_on_shutdown;
7007 ma_proc jack_get_sample_rate;
7008 ma_proc jack_get_buffer_size;
7009 ma_proc jack_get_ports;
7010 ma_proc jack_activate;
7011 ma_proc jack_deactivate;
7012 ma_proc jack_connect;
7013 ma_proc jack_port_register;
7014 ma_proc jack_port_name;
7015 ma_proc jack_port_get_buffer;
7016 ma_proc jack_free;
7017
7018 char* pClientName;
7019 ma_bool32 tryStartServer;
7020 } jack;
7021#endif
7022#ifdef MA_SUPPORT_COREAUDIO
7023 struct
7024 {
7025 ma_handle hCoreFoundation;
7026 ma_proc CFStringGetCString;
7027 ma_proc CFRelease;
7028
7029 ma_handle hCoreAudio;
7030 ma_proc AudioObjectGetPropertyData;
7031 ma_proc AudioObjectGetPropertyDataSize;
7032 ma_proc AudioObjectSetPropertyData;
7033 ma_proc AudioObjectAddPropertyListener;
7034 ma_proc AudioObjectRemovePropertyListener;
7035
7036 ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */
7037 ma_proc AudioComponentFindNext;
7038 ma_proc AudioComponentInstanceDispose;
7039 ma_proc AudioComponentInstanceNew;
7040 ma_proc AudioOutputUnitStart;
7041 ma_proc AudioOutputUnitStop;
7042 ma_proc AudioUnitAddPropertyListener;
7043 ma_proc AudioUnitGetPropertyInfo;
7044 ma_proc AudioUnitGetProperty;
7045 ma_proc AudioUnitSetProperty;
7046 ma_proc AudioUnitInitialize;
7047 ma_proc AudioUnitRender;
7048
7049 /*AudioComponent*/ ma_ptr component;
7050 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(). */
7051 } coreaudio;
7052#endif
7053#ifdef MA_SUPPORT_SNDIO
7054 struct
7055 {
7056 ma_handle sndioSO;
7057 ma_proc sio_open;
7058 ma_proc sio_close;
7059 ma_proc sio_setpar;
7060 ma_proc sio_getpar;
7061 ma_proc sio_getcap;
7062 ma_proc sio_start;
7063 ma_proc sio_stop;
7064 ma_proc sio_read;
7065 ma_proc sio_write;
7066 ma_proc sio_onmove;
7067 ma_proc sio_nfds;
7068 ma_proc sio_pollfd;
7069 ma_proc sio_revents;
7070 ma_proc sio_eof;
7071 ma_proc sio_setvol;
7072 ma_proc sio_onvol;
7073 ma_proc sio_initpar;
7074 } sndio;
7075#endif
7076#ifdef MA_SUPPORT_AUDIO4
7077 struct
7078 {
7079 int _unused;
7080 } audio4;
7081#endif
7082#ifdef MA_SUPPORT_OSS
7083 struct
7084 {
7085 int versionMajor;
7086 int versionMinor;
7087 } oss;
7088#endif
7089#ifdef MA_SUPPORT_AAUDIO
7090 struct
7091 {
7092 ma_handle hAAudio; /* libaaudio.so */
7093 ma_proc AAudio_createStreamBuilder;
7094 ma_proc AAudioStreamBuilder_delete;
7095 ma_proc AAudioStreamBuilder_setDeviceId;
7096 ma_proc AAudioStreamBuilder_setDirection;
7097 ma_proc AAudioStreamBuilder_setSharingMode;
7098 ma_proc AAudioStreamBuilder_setFormat;
7099 ma_proc AAudioStreamBuilder_setChannelCount;
7100 ma_proc AAudioStreamBuilder_setSampleRate;
7101 ma_proc AAudioStreamBuilder_setBufferCapacityInFrames;
7102 ma_proc AAudioStreamBuilder_setFramesPerDataCallback;
7103 ma_proc AAudioStreamBuilder_setDataCallback;
7104 ma_proc AAudioStreamBuilder_setErrorCallback;
7105 ma_proc AAudioStreamBuilder_setPerformanceMode;
7106 ma_proc AAudioStreamBuilder_setUsage;
7107 ma_proc AAudioStreamBuilder_setContentType;
7108 ma_proc AAudioStreamBuilder_setInputPreset;
7109 ma_proc AAudioStreamBuilder_openStream;
7110 ma_proc AAudioStream_close;
7111 ma_proc AAudioStream_getState;
7112 ma_proc AAudioStream_waitForStateChange;
7113 ma_proc AAudioStream_getFormat;
7114 ma_proc AAudioStream_getChannelCount;
7115 ma_proc AAudioStream_getSampleRate;
7116 ma_proc AAudioStream_getBufferCapacityInFrames;
7117 ma_proc AAudioStream_getFramesPerDataCallback;
7118 ma_proc AAudioStream_getFramesPerBurst;
7119 ma_proc AAudioStream_requestStart;
7120 ma_proc AAudioStream_requestStop;
7121 ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */
7122 } aaudio;
7123#endif
7124#ifdef MA_SUPPORT_OPENSL
7125 struct
7126 {
7127 ma_handle libOpenSLES;
7128 ma_handle SL_IID_ENGINE;
7129 ma_handle SL_IID_AUDIOIODEVICECAPABILITIES;
7130 ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
7131 ma_handle SL_IID_RECORD;
7132 ma_handle SL_IID_PLAY;
7133 ma_handle SL_IID_OUTPUTMIX;
7134 ma_handle SL_IID_ANDROIDCONFIGURATION;
7135 ma_proc slCreateEngine;
7136 } opensl;
7137#endif
7138#ifdef MA_SUPPORT_WEBAUDIO
7139 struct
7140 {
7141 int _unused;
7142 } webaudio;
7143#endif
7144#ifdef MA_SUPPORT_NULL
7145 struct
7146 {
7149#endif
7150 };
7151
7152 union
7153 {
7154#ifdef MA_WIN32
7155 struct
7156 {
7157 /*HMODULE*/ ma_handle hOle32DLL;
7158 ma_proc CoInitializeEx;
7159 ma_proc CoUninitialize;
7160 ma_proc CoCreateInstance;
7161 ma_proc CoTaskMemFree;
7162 ma_proc PropVariantClear;
7163 ma_proc StringFromGUID2;
7164
7165 /*HMODULE*/ ma_handle hUser32DLL;
7166 ma_proc GetForegroundWindow;
7167 ma_proc GetDesktopWindow;
7168
7169 /*HMODULE*/ ma_handle hAdvapi32DLL;
7170 ma_proc RegOpenKeyExA;
7171 ma_proc RegCloseKey;
7172 ma_proc RegQueryValueExA;
7173 } win32;
7174#endif
7175#ifdef MA_POSIX
7176 struct
7177 {
7195#endif
7196 int _unused;
7197 };
7198};
7199
7201{
7205 MA_ATOMIC(4, ma_device_state) state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */
7206 ma_device_data_proc onData; /* Set once at initialization time and should not be changed after. */
7207 ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */
7208 ma_stop_proc onStop; /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */
7209 void* pUserData; /* Application defined data. */
7215 ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */
7216 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(). */
7221 MA_ATOMIC(4, float) masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */
7222 ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */
7223 struct
7224 {
7228 struct
7229 {
7233 struct
7234 {
7235 ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */
7236 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. */
7237 char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */
7238 ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
7250 void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */
7252 ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */
7253 void* pInputCache; /* In external format. Can be null. */
7258 struct
7259 {
7260 ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */
7261 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. */
7262 char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */
7263 ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
7275 void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */
7277 ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */
7279
7280 union
7281 {
7282#ifdef MA_SUPPORT_WASAPI
7283 struct
7284 {
7285 /*IAudioClient**/ ma_ptr pAudioClientPlayback;
7286 /*IAudioClient**/ ma_ptr pAudioClientCapture;
7287 /*IAudioRenderClient**/ ma_ptr pRenderClient;
7288 /*IAudioCaptureClient**/ ma_ptr pCaptureClient;
7289 /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */
7290 ma_IMMNotificationClient notificationClient;
7291 /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */
7292 /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */
7293 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. */
7294 ma_uint32 actualBufferSizeInFramesCapture;
7295 ma_uint32 originalPeriodSizeInFrames;
7296 ma_uint32 originalPeriodSizeInMilliseconds;
7297 ma_uint32 originalPeriods;
7298 ma_performance_profile originalPerformanceProfile;
7299 ma_uint32 periodSizeInFramesPlayback;
7300 ma_uint32 periodSizeInFramesCapture;
7301 void* pMappedBufferCapture;
7302 ma_uint32 mappedBufferCaptureCap;
7303 ma_uint32 mappedBufferCaptureLen;
7304 void* pMappedBufferPlayback;
7305 ma_uint32 mappedBufferPlaybackCap;
7306 ma_uint32 mappedBufferPlaybackLen;
7307 MA_ATOMIC(4, ma_bool32) isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
7308 MA_ATOMIC(4, ma_bool32) isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
7309 ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
7310 ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
7311 ma_bool8 noHardwareOffloading;
7312 ma_bool8 allowCaptureAutoStreamRouting;
7313 ma_bool8 allowPlaybackAutoStreamRouting;
7314 ma_bool8 isDetachedPlayback;
7315 ma_bool8 isDetachedCapture;
7316 } wasapi;
7317#endif
7318#ifdef MA_SUPPORT_DSOUND
7319 struct
7320 {
7321 /*LPDIRECTSOUND*/ ma_ptr pPlayback;
7322 /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer;
7323 /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer;
7324 /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture;
7325 /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer;
7326 } dsound;
7327#endif
7328#ifdef MA_SUPPORT_WINMM
7329 struct
7330 {
7331 /*HWAVEOUT*/ ma_handle hDevicePlayback;
7332 /*HWAVEIN*/ ma_handle hDeviceCapture;
7333 /*HANDLE*/ ma_handle hEventPlayback;
7334 /*HANDLE*/ ma_handle hEventCapture;
7335 ma_uint32 fragmentSizeInFrames;
7336 ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */
7337 ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */
7338 ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */
7339 ma_uint32 headerFramesConsumedCapture; /* ^^^ */
7340 /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */
7341 /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */
7342 ma_uint8* pIntermediaryBufferPlayback;
7343 ma_uint8* pIntermediaryBufferCapture;
7344 ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */
7345 } winmm;
7346#endif
7347#ifdef MA_SUPPORT_ALSA
7348 struct
7349 {
7350 /*snd_pcm_t**/ ma_ptr pPCMPlayback;
7351 /*snd_pcm_t**/ ma_ptr pPCMCapture;
7352 /*struct pollfd**/ void* pPollDescriptorsPlayback;
7353 /*struct pollfd**/ void* pPollDescriptorsCapture;
7354 int pollDescriptorCountPlayback;
7355 int pollDescriptorCountCapture;
7356 int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */
7357 int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */
7358 ma_bool8 isUsingMMapPlayback;
7359 ma_bool8 isUsingMMapCapture;
7360 } alsa;
7361#endif
7362#ifdef MA_SUPPORT_PULSEAUDIO
7363 struct
7364 {
7365 /*pa_mainloop**/ ma_ptr pMainLoop;
7366 /*pa_context**/ ma_ptr pPulseContext;
7367 /*pa_stream**/ ma_ptr pStreamPlayback;
7368 /*pa_stream**/ ma_ptr pStreamCapture;
7369 } pulse;
7370#endif
7371#ifdef MA_SUPPORT_JACK
7372 struct
7373 {
7374 /*jack_client_t**/ ma_ptr pClient;
7375 /*jack_port_t**/ ma_ptr* ppPortsPlayback;
7376 /*jack_port_t**/ ma_ptr* ppPortsCapture;
7377 float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */
7378 float* pIntermediaryBufferCapture;
7379 } jack;
7380#endif
7381#ifdef MA_SUPPORT_COREAUDIO
7382 struct
7383 {
7384 ma_uint32 deviceObjectIDPlayback;
7385 ma_uint32 deviceObjectIDCapture;
7386 /*AudioUnit*/ ma_ptr audioUnitPlayback;
7387 /*AudioUnit*/ ma_ptr audioUnitCapture;
7388 /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */
7389 ma_uint32 audioBufferCapInFrames; /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */
7391 ma_uint32 originalPeriodSizeInFrames;
7392 ma_uint32 originalPeriodSizeInMilliseconds;
7393 ma_uint32 originalPeriods;
7394 ma_performance_profile originalPerformanceProfile;
7395 ma_bool32 isDefaultPlaybackDevice;
7396 ma_bool32 isDefaultCaptureDevice;
7397 ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
7398 ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
7399 void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */
7400 } coreaudio;
7401#endif
7402#ifdef MA_SUPPORT_SNDIO
7403 struct
7404 {
7405 ma_ptr handlePlayback;
7406 ma_ptr handleCapture;
7407 ma_bool32 isStartedPlayback;
7408 ma_bool32 isStartedCapture;
7409 } sndio;
7410#endif
7411#ifdef MA_SUPPORT_AUDIO4
7412 struct
7413 {
7414 int fdPlayback;
7415 int fdCapture;
7416 } audio4;
7417#endif
7418#ifdef MA_SUPPORT_OSS
7419 struct
7420 {
7421 int fdPlayback;
7422 int fdCapture;
7423 } oss;
7424#endif
7425#ifdef MA_SUPPORT_AAUDIO
7426 struct
7427 {
7428 /*AAudioStream**/ ma_ptr pStreamPlayback;
7429 /*AAudioStream**/ ma_ptr pStreamCapture;
7430 ma_aaudio_usage usage;
7431 ma_aaudio_content_type contentType;
7432 ma_aaudio_input_preset inputPreset;
7433 ma_bool32 noAutoStartAfterReroute;
7434 } aaudio;
7435#endif
7436#ifdef MA_SUPPORT_OPENSL
7437 struct
7438 {
7439 /*SLObjectItf*/ ma_ptr pOutputMixObj;
7440 /*SLOutputMixItf*/ ma_ptr pOutputMix;
7441 /*SLObjectItf*/ ma_ptr pAudioPlayerObj;
7442 /*SLPlayItf*/ ma_ptr pAudioPlayer;
7443 /*SLObjectItf*/ ma_ptr pAudioRecorderObj;
7444 /*SLRecordItf*/ ma_ptr pAudioRecorder;
7445 /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback;
7446 /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture;
7447 ma_bool32 isDrainingCapture;
7448 ma_bool32 isDrainingPlayback;
7449 ma_uint32 currentBufferIndexPlayback;
7450 ma_uint32 currentBufferIndexCapture;
7451 ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */
7452 ma_uint8* pBufferCapture;
7453 } opensl;
7454#endif
7455#ifdef MA_SUPPORT_WEBAUDIO
7456 struct
7457 {
7458 int indexPlayback; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */
7459 int indexCapture;
7460 } webaudio;
7461#endif
7462#ifdef MA_SUPPORT_NULL
7463 struct
7464 {
7477 MA_ATOMIC(4, ma_bool32) isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */
7479#endif
7480 };
7481};
7482#if defined(_MSC_VER) && !defined(__clang__)
7483 #pragma warning(pop)
7484#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
7485 #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
7486#endif
7487
7488/*
7489Initializes a `ma_context_config` object.
7490
7491
7492Return Value
7493------------
7494A `ma_context_config` initialized to defaults.
7495
7496
7497Remarks
7498-------
7499You 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
7500is updated and new members are added to `ma_context_config`. It also sets logical defaults.
7501
7502You can override members of the returned object by changing it's members directly.
7503
7504
7505See Also
7506--------
7507ma_context_init()
7508*/
7510
7511/*
7512Initializes a context.
7513
7514The 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
7515device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices.
7516
7517
7518Parameters
7519----------
7520backends (in, optional)
7521 A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.
7522
7523backendCount (in, optional)
7524 The number of items in `backend`. Ignored if `backend` is NULL.
7525
7526pConfig (in, optional)
7527 The context configuration.
7528
7529pContext (in)
7530 A pointer to the context object being initialized.
7531
7532
7533Return Value
7534------------
7535MA_SUCCESS if successful; any other error code otherwise.
7536
7537
7538Thread Safety
7539-------------
7540Unsafe. Do not call this function across multiple threads as some backends read and write to global state.
7541
7542
7543Remarks
7544-------
7545When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order:
7546
7547 |-------------|-----------------------|--------------------------------------------------------|
7548 | Name | Enum Name | Supported Operating Systems |
7549 |-------------|-----------------------|--------------------------------------------------------|
7550 | WASAPI | ma_backend_wasapi | Windows Vista+ |
7551 | DirectSound | ma_backend_dsound | Windows XP+ |
7552 | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) |
7553 | Core Audio | ma_backend_coreaudio | macOS, iOS |
7554 | ALSA | ma_backend_alsa | Linux |
7555 | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
7556 | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
7557 | sndio | ma_backend_sndio | OpenBSD |
7558 | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
7559 | OSS | ma_backend_oss | FreeBSD |
7560 | AAudio | ma_backend_aaudio | Android 8+ |
7561 | OpenSL|ES | ma_backend_opensl | Android (API level 16+) |
7562 | Web Audio | ma_backend_webaudio | Web (via Emscripten) |
7563 | Null | ma_backend_null | Cross Platform (not used on Web) |
7564 |-------------|-----------------------|--------------------------------------------------------|
7565
7566The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings
7567can then be set directly on the structure. Below are the members of the `ma_context_config` object.
7568
7569 pLog
7570 A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not
7571 require logging. See the `ma_log` API for details on how to use the logging system.
7572
7573 threadPriority
7574 The desired priority to use for the audio thread. Allowable values include the following:
7575
7576 |--------------------------------------|
7577 | Thread Priority |
7578 |--------------------------------------|
7579 | ma_thread_priority_idle |
7580 | ma_thread_priority_lowest |
7581 | ma_thread_priority_low |
7582 | ma_thread_priority_normal |
7583 | ma_thread_priority_high |
7584 | ma_thread_priority_highest (default) |
7585 | ma_thread_priority_realtime |
7586 | ma_thread_priority_default |
7587 |--------------------------------------|
7588
7589 threadStackSize
7590 The desired size of the stack for the audio thread. Defaults to the operating system's default.
7591
7592 pUserData
7593 A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`.
7594
7595 allocationCallbacks
7596 Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation
7597 callbacks will be used for anything tied to the context, including devices.
7598
7599 alsa.useVerboseDeviceEnumeration
7600 ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique
7601 card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes
7602 it so the ALSA backend includes all devices. Defaults to false.
7603
7604 pulse.pApplicationName
7605 PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`.
7606
7607 pulse.pServerName
7608 PulseAudio only. The name of the server to connect to with `pa_context_connect()`.
7609
7610 pulse.tryAutoSpawn
7611 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
7612 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
7613 intrusive for the end user.
7614
7615 coreaudio.sessionCategory
7616 iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.
7617
7618 |-----------------------------------------|-------------------------------------|
7619 | miniaudio Token | Core Audio Token |
7620 |-----------------------------------------|-------------------------------------|
7621 | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient |
7622 | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient |
7623 | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback |
7624 | ma_ios_session_category_record | AVAudioSessionCategoryRecord |
7625 | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord |
7626 | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute |
7627 | ma_ios_session_category_none | AVAudioSessionCategoryAmbient |
7628 | ma_ios_session_category_default | AVAudioSessionCategoryAmbient |
7629 |-----------------------------------------|-------------------------------------|
7630
7631 coreaudio.sessionCategoryOptions
7632 iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.
7633
7634 |---------------------------------------------------------------------------|------------------------------------------------------------------|
7635 | miniaudio Token | Core Audio Token |
7636 |---------------------------------------------------------------------------|------------------------------------------------------------------|
7637 | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers |
7638 | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers |
7639 | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth |
7640 | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker |
7641 | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers |
7642 | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP |
7643 | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay |
7644 |---------------------------------------------------------------------------|------------------------------------------------------------------|
7645
7646 coreaudio.noAudioSessionActivate
7647 iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization.
7648
7649 coreaudio.noAudioSessionDeactivate
7650 iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization.
7651
7652 jack.pClientName
7653 The name of the client to pass to `jack_client_open()`.
7654
7655 jack.tryStartServer
7656 Whether or not to try auto-starting the JACK server. Defaults to false.
7657
7658
7659It 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
7660relevant backends every time it's initialized.
7661
7662The location of the context cannot change throughout it's lifetime. Consider allocating the `ma_context` object with `malloc()` if this is an issue. The
7663reason for this is that a pointer to the context is stored in the `ma_device` structure.
7664
7665
7666Example 1 - Default Initialization
7667----------------------------------
7668The example below shows how to initialize the context using the default configuration.
7669
7670```c
7671ma_context context;
7672ma_result result = ma_context_init(NULL, 0, NULL, &context);
7673if (result != MA_SUCCESS) {
7674 // Error.
7675}
7676```
7677
7678
7679Example 2 - Custom Configuration
7680--------------------------------
7681The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program
7682wants 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
7683want an error to be returned if no valid backend is available which they achieve by excluding the Null backend.
7684
7685For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface.
7686
7687```c
7688ma_backend backends[] = {
7689 ma_backend_alsa,
7690 ma_backend_pulseaudio,
7691 ma_backend_wasapi,
7692 ma_backend_dsound
7693};
7694
7695ma_log log;
7696ma_log_init(&log);
7697ma_log_register_callback(&log, ma_log_callback_init(my_log_callbac, pMyLogUserData));
7698
7699ma_context_config config = ma_context_config_init();
7700config.pLog = &log; // Specify a custom log object in the config so any logs that are posted from ma_context_init() are captured.
7701
7702ma_context context;
7703ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context);
7704if (result != MA_SUCCESS) {
7705 // Error.
7706 if (result == MA_NO_BACKEND) {
7707 // Couldn't find an appropriate backend.
7708 }
7709}
7710
7711// You could also attach a log callback post-initialization:
7712ma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData));
7713```
7714
7715
7716See Also
7717--------
7718ma_context_config_init()
7719ma_context_uninit()
7720*/
7721MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext);
7722
7723/*
7724Uninitializes a context.
7725
7726
7727Return Value
7728------------
7729MA_SUCCESS if successful; any other error code otherwise.
7730
7731
7732Thread Safety
7733-------------
7734Unsafe. Do not call this function across multiple threads as some backends read and write to global state.
7735
7736
7737Remarks
7738-------
7739Results are undefined if you call this while any device created by this context is still active.
7740
7741
7742See Also
7743--------
7744ma_context_init()
7745*/
7747
7748/*
7749Retrieves the size of the ma_context object.
7750
7751This is mainly for the purpose of bindings to know how much memory to allocate.
7752*/
7754
7755/*
7756Retrieves a pointer to the log object associated with this context.
7757
7758
7759Remarks
7760-------
7761Pass the returned pointer to `ma_log_post()`, `ma_log_postv()` or `ma_log_postf()` to post a log
7762message.
7763
7764You can attach your own logging callback to the log with `ma_log_register_callback()`
7765
7766
7767Return Value
7768------------
7769A pointer to the `ma_log` object that the context uses to post log messages. If some error occurs,
7770NULL will be returned.
7771*/
7773
7774/*
7775Enumerates over every device (both playback and capture).
7776
7777This 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
7778an internal heap allocation, or it simply suits your code better.
7779
7780Note 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
7781opening 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,
7782but don't call it from within the enumeration callback.
7783
7784Returning false from the callback will stop enumeration. Returning true will continue enumeration.
7785
7786
7787Parameters
7788----------
7789pContext (in)
7790 A pointer to the context performing the enumeration.
7791
7792callback (in)
7793 The callback to fire for each enumerated device.
7794
7795pUserData (in)
7796 A pointer to application-defined data passed to the callback.
7797
7798
7799Return Value
7800------------
7801MA_SUCCESS if successful; any other error code otherwise.
7802
7803
7804Thread Safety
7805-------------
7806Safe. This is guarded using a simple mutex lock.
7807
7808
7809Remarks
7810-------
7811Do _not_ assume the first enumerated device of a given type is the default device.
7812
7813Some backends and platforms may only support default playback and capture devices.
7814
7815In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also,
7816do not try to call `ma_context_get_device_info()` from within the callback.
7817
7818Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation.
7819
7820
7821Example 1 - Simple Enumeration
7822------------------------------
7823ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
7824{
7825 printf("Device Name: %s\n", pInfo->name);
7826 return MA_TRUE;
7827}
7828
7829ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData);
7830if (result != MA_SUCCESS) {
7831 // Error.
7832}
7833
7834
7835See Also
7836--------
7837ma_context_get_devices()
7838*/
7840
7841/*
7842Retrieves basic information about every active playback and/or capture device.
7843
7844This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos`
7845parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback.
7846
7847
7848Parameters
7849----------
7850pContext (in)
7851 A pointer to the context performing the enumeration.
7852
7853ppPlaybackDeviceInfos (out)
7854 A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices.
7855
7856pPlaybackDeviceCount (out)
7857 A pointer to an unsigned integer that will receive the number of playback devices.
7858
7859ppCaptureDeviceInfos (out)
7860 A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices.
7861
7862pCaptureDeviceCount (out)
7863 A pointer to an unsigned integer that will receive the number of capture devices.
7864
7865
7866Return Value
7867------------
7868MA_SUCCESS if successful; any other error code otherwise.
7869
7870
7871Thread Safety
7872-------------
7873Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple
7874threads. Instead, you need to make a copy of the returned data with your own higher level synchronization.
7875
7876
7877Remarks
7878-------
7879It is _not_ safe to assume the first device in the list is the default device.
7880
7881You can pass in NULL for the playback or capture lists in which case they'll be ignored.
7882
7883The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers.
7884
7885
7886See Also
7887--------
7888ma_context_get_devices()
7889*/
7890MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount);
7891
7892/*
7893Retrieves information about a device of the given type, with the specified ID and share mode.
7894
7895
7896Parameters
7897----------
7898pContext (in)
7899 A pointer to the context performing the query.
7900
7901deviceType (in)
7902 The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`.
7903
7904pDeviceID (in)
7905 The ID of the device being queried.
7906
7907pDeviceInfo (out)
7908 A pointer to the `ma_device_info` structure that will receive the device information.
7909
7910
7911Return Value
7912------------
7913MA_SUCCESS if successful; any other error code otherwise.
7914
7915
7916Thread Safety
7917-------------
7918Safe. This is guarded using a simple mutex lock.
7919
7920
7921Remarks
7922-------
7923Do _not_ call this from within the `ma_context_enumerate_devices()` callback.
7924
7925It'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
7926shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify
7927which 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
7928the requested share mode is unsupported.
7929
7930This leaves pDeviceInfo unmodified in the result of an error.
7931*/
7933
7934/*
7935Determines if the given context supports loopback mode.
7936
7937
7938Parameters
7939----------
7940pContext (in)
7941 A pointer to the context getting queried.
7942
7943
7944Return Value
7945------------
7946MA_TRUE if the context supports loopback mode; MA_FALSE otherwise.
7947*/
7949
7950
7951
7952/*
7953Initializes a device config with default settings.
7954
7955
7956Parameters
7957----------
7958deviceType (in)
7959 The type of the device this config is being initialized for. This must set to one of the following:
7960
7961 |-------------------------|
7962 | Device Type |
7963 |-------------------------|
7964 | ma_device_type_playback |
7965 | ma_device_type_capture |
7966 | ma_device_type_duplex |
7967 | ma_device_type_loopback |
7968 |-------------------------|
7969
7970
7971Return Value
7972------------
7973A new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks.
7974
7975
7976Thread Safety
7977-------------
7978Safe.
7979
7980
7981Callback Safety
7982---------------
7983Safe, but don't try initializing a device in a callback.
7984
7985
7986Remarks
7987-------
7988The 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
7989typical configuration which sets the sample format, channel count, sample rate, data callback and user data. These are usually things you will want to change
7990before initializing the device.
7991
7992See `ma_device_init()` for details on specific configuration options.
7993
7994
7995Example 1 - Simple Configuration
7996--------------------------------
7997The 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
7998then the returned object is modified directly. This is important because it ensures that your program continues to work as new configuration options are added
7999to the `ma_device_config` structure.
8000
8001```c
8002ma_device_config config = ma_device_config_init(ma_device_type_playback);
8003config.playback.format = ma_format_f32;
8004config.playback.channels = 2;
8005config.sampleRate = 48000;
8006config.dataCallback = ma_data_callback;
8007config.pUserData = pMyUserData;
8008```
8009
8010
8011See Also
8012--------
8013ma_device_init()
8014ma_device_init_ex()
8015*/
8017
8018
8019/*
8020Initializes a device.
8021
8022A 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
8023from 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
8024playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the
8025device is done via a callback which is fired by miniaudio at periodic time intervals.
8026
8027The frequency at which data is delivered to and from a device depends on the size of it's period. The size of the period can be defined in terms of PCM frames
8028or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and
8029increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but
8030miniaudio'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
8031media 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
8032backend is ultimately responsible for what it gives you. You cannot assume you will get exactly what you ask for.
8033
8034When 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
8035format 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
8036can 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.
8037
8038
8039Parameters
8040----------
8041pContext (in, optional)
8042 A pointer to the context that owns the device. This can be null, in which case it creates a default context internally.
8043
8044pConfig (in)
8045 A pointer to the device configuration. Cannot be null. See remarks for details.
8046
8047pDevice (out)
8048 A pointer to the device object being initialized.
8049
8050
8051Return Value
8052------------
8053MA_SUCCESS if successful; any other error code otherwise.
8054
8055
8056Thread Safety
8057-------------
8058Unsafe. 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
8059calling this at the same time as `ma_device_uninit()`.
8060
8061
8062Callback Safety
8063---------------
8064Unsafe. It is not safe to call this inside any callback.
8065
8066
8067Remarks
8068-------
8069Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so:
8070
8071 ```c
8072 ma_context_init(NULL, 0, NULL, &context);
8073 ```
8074
8075Do 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
8076device.pContext for the initialization of other devices.
8077
8078The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can
8079then be set directly on the structure. Below are the members of the `ma_device_config` object.
8080
8081 deviceType
8082 Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`.
8083
8084 sampleRate
8085 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.
8086
8087 periodSizeInFrames
8088 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
8089 be used depending on the selected performance profile. This value affects latency. See below for details.
8090
8091 periodSizeInMilliseconds
8092 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
8093 used depending on the selected performance profile. The value affects latency. See below for details.
8094
8095 periods
8096 The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by
8097 this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured.
8098
8099 performanceProfile
8100 A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or
8101 `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value.
8102
8103 noPreSilencedOutputBuffer
8104 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
8105 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
8106 callback will write to every sample in the output buffer, or if you are doing your own clearing.
8107
8108 noClip
8109 When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. When set to false (default), the
8110 contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or not the clip. This only
8111 applies when the playback sample format is f32.
8112
8113 noDisableDenormals
8114 By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals.
8115
8116 noFixedSizedCallback
8117 Allows miniaudio to fire the data callback with any frame count. When this is set to true, the data callback will be fired with a consistent frame
8118 count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to false, miniaudio will fire the callback with whatever the
8119 backend requests, which could be anything.
8120
8121 dataCallback
8122 The callback to fire whenever data is ready to be delivered to or from the device.
8123
8124 notificationCallback
8125 The callback to fire when something has changed with the device, such as whether or not it has been started or stopped.
8126
8127 pUserData
8128 The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`.
8129
8130 resampling.algorithm
8131 The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The
8132 default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`.
8133
8134 resampling.pBackendVTable
8135 A pointer to an optional vtable that can be used for plugging in a custom resampler.
8136
8137 resampling.pBackendUserData
8138 A pointer that will passed to callbacks in pBackendVTable.
8139
8140 resampling.linear.lpfOrder
8141 The linear resampler applies a low-pass filter as part of it's procesing for anti-aliasing. This setting controls the order of the filter. The higher
8142 the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is
8143 `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`.
8144
8145 playback.pDeviceID
8146 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
8147 default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration.
8148
8149 playback.format
8150 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
8151 initialization from the device object directly with `device.playback.format`.
8152
8153 playback.channels
8154 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
8155 from the device object directly with `device.playback.channels`.
8156
8157 playback.pChannelMap
8158 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
8159 device object direct with `device.playback.pChannelMap`. When set, the buffer should contain `channels` items.
8160
8161 playback.shareMode
8162 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
8163 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
8164 ma_share_mode_shared and reinitializing.
8165
8166 capture.pDeviceID
8167 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
8168 default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration.
8169
8170 capture.format
8171 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
8172 initialization from the device object directly with `device.capture.format`.
8173
8174 capture.channels
8175 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
8176 from the device object directly with `device.capture.channels`.
8177
8178 capture.pChannelMap
8179 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
8180 device object direct with `device.capture.pChannelMap`. When set, the buffer should contain `channels` items.
8181
8182 capture.shareMode
8183 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
8184 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
8185 ma_share_mode_shared and reinitializing.
8186
8187 wasapi.noAutoConvertSRC
8188 WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false.
8189
8190 wasapi.noDefaultQualitySRC
8191 WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`.
8192 You should usually leave this set to false, which is the default.
8193
8194 wasapi.noAutoStreamRouting
8195 WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false.
8196
8197 wasapi.noHardwareOffloading
8198 WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false.
8199
8200 alsa.noMMap
8201 ALSA only. When set to true, disables MMap mode. Defaults to false.
8202
8203 alsa.noAutoFormat
8204 ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false.
8205
8206 alsa.noAutoChannels
8207 ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false.
8208
8209 alsa.noAutoResample
8210 ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false.
8211
8212 pulse.pStreamNamePlayback
8213 PulseAudio only. Sets the stream name for playback.
8214
8215 pulse.pStreamNameCapture
8216 PulseAudio only. Sets the stream name for capture.
8217
8218 coreaudio.allowNominalSampleRateChange
8219 Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This
8220 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
8221 that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will
8222 find the closest match between the sample rate requested in the device config and the sample rates natively supported by the
8223 hardware. When set to false, the sample rate currently set by the operating system will always be used.
8224
8225 opensl.streamType
8226 OpenSL only. Explicitly sets the stream type. If left unset (`ma_opensl_stream_type_default`), the
8227 stream type will be left unset. Think of this as the type of audio you're playing.
8228
8229 opensl.recordingPreset
8230 OpenSL only. Explicitly sets the type of recording your program will be doing. When left
8231 unset, the recording preset will be left unchanged.
8232
8233 aaudio.usage
8234 AAudio only. Explicitly sets the nature of the audio the program will be consuming. When
8235 left unset, the usage will be left unchanged.
8236
8237 aaudio.contentType
8238 AAudio only. Sets the content type. When left unset, the content type will be left unchanged.
8239
8240 aaudio.inputPreset
8241 AAudio only. Explicitly sets the type of recording your program will be doing. When left
8242 unset, the input preset will be left unchanged.
8243
8244 aaudio.noAutoStartAfterReroute
8245 AAudio only. Controls whether or not the device should be automatically restarted after a
8246 stream reroute. When set to false (default) the device will be restarted automatically;
8247 otherwise the device will be stopped.
8248
8249
8250Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device.
8251
8252After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`.
8253
8254If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or
8255`MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or
8256`ma_performance_profile_conservative`.
8257
8258If 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
8259in 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
8260config) 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,
8261for 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.
8262Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary.
8263
8264When sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by the config
8265and 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
8266on an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`,
8267`playback/capture.channels` and `sampleRate` members of the device object.
8268
8269When 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
8270asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information.
8271
8272ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture.
8273If these fail it will try falling back to the "hw" device.
8274
8275
8276Example 1 - Simple Initialization
8277---------------------------------
8278This 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
8279playback device this is usually all you need.
8280
8281```c
8282ma_device_config config = ma_device_config_init(ma_device_type_playback);
8283config.playback.format = ma_format_f32;
8284config.playback.channels = 2;
8285config.sampleRate = 48000;
8286config.dataCallback = ma_data_callback;
8287config.pMyUserData = pMyUserData;
8288
8289ma_device device;
8290ma_result result = ma_device_init(NULL, &config, &device);
8291if (result != MA_SUCCESS) {
8292 // Error
8293}
8294```
8295
8296
8297Example 2 - Advanced Initialization
8298-----------------------------------
8299This 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
8300and 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
8301enumeration.
8302
8303```c
8304ma_context context;
8305ma_result result = ma_context_init(NULL, 0, NULL, &context);
8306if (result != MA_SUCCESS) {
8307 // Error
8308}
8309
8310ma_device_info* pPlaybackDeviceInfos;
8311ma_uint32 playbackDeviceCount;
8312result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL);
8313if (result != MA_SUCCESS) {
8314 // Error
8315}
8316
8317// ... choose a device from pPlaybackDeviceInfos ...
8318
8319ma_device_config config = ma_device_config_init(ma_device_type_playback);
8320config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices().
8321config.playback.format = ma_format_f32;
8322config.playback.channels = 2;
8323config.sampleRate = 48000;
8324config.dataCallback = ma_data_callback;
8325config.pUserData = pMyUserData;
8326config.periodSizeInMilliseconds = 10;
8327config.periods = 3;
8328
8329ma_device device;
8330result = ma_device_init(&context, &config, &device);
8331if (result != MA_SUCCESS) {
8332 // Error
8333}
8334```
8335
8336
8337See Also
8338--------
8339ma_device_config_init()
8340ma_device_uninit()
8341ma_device_start()
8342ma_context_init()
8343ma_context_get_devices()
8344ma_context_enumerate_devices()
8345*/
8347
8348/*
8349Initializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context.
8350
8351This 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
8352allows you to configure the internally created context.
8353
8354
8355Parameters
8356----------
8357backends (in, optional)
8358 A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.
8359
8360backendCount (in, optional)
8361 The number of items in `backend`. Ignored if `backend` is NULL.
8362
8363pContextConfig (in, optional)
8364 The context configuration.
8365
8366pConfig (in)
8367 A pointer to the device configuration. Cannot be null. See remarks for details.
8368
8369pDevice (out)
8370 A pointer to the device object being initialized.
8371
8372
8373Return Value
8374------------
8375MA_SUCCESS if successful; any other error code otherwise.
8376
8377
8378Thread Safety
8379-------------
8380Unsafe. 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
8381calling this at the same time as `ma_device_uninit()`.
8382
8383
8384Callback Safety
8385---------------
8386Unsafe. It is not safe to call this inside any callback.
8387
8388
8389Remarks
8390-------
8391You only need to use this function if you want to configure the context differently to it's defaults. You should never use this function if you want to manage
8392your own context.
8393
8394See the documentation for `ma_context_init()` for information on the different context configuration options.
8395
8396
8397See Also
8398--------
8399ma_device_init()
8400ma_device_uninit()
8401ma_device_config_init()
8402ma_context_init()
8403*/
8404MA_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);
8405
8406/*
8407Uninitializes a device.
8408
8409This will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do.
8410
8411
8412Parameters
8413----------
8414pDevice (in)
8415 A pointer to the device to stop.
8416
8417
8418Return Value
8419------------
8420Nothing
8421
8422
8423Thread Safety
8424-------------
8425Unsafe. As soon as this API is called the device should be considered undefined.
8426
8427
8428Callback Safety
8429---------------
8430Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.
8431
8432
8433See Also
8434--------
8435ma_device_init()
8436ma_device_stop()
8437*/
8439
8440
8441/*
8442Retrieves a pointer to the context that owns the given device.
8443*/
8445
8446/*
8447Helper function for retrieving the log object associated with the context that owns this device.
8448*/
8450
8451
8452/*
8453Retrieves information about the device.
8454
8455
8456Parameters
8457----------
8458pDevice (in)
8459 A pointer to the device whose information is being retrieved.
8460
8461type (in)
8462 The device type. This parameter is required for duplex devices. When retrieving device
8463 information, you are doing so for an individual playback or capture device.
8464
8465pDeviceInfo (out)
8466 A pointer to the `ma_device_info` that will receive the device information.
8467
8468
8469Return Value
8470------------
8471MA_SUCCESS if successful; any other error code otherwise.
8472
8473
8474Thread Safety
8475-------------
8476Unsafe. This should be considered unsafe because it may be calling into the backend which may or
8477may not be safe.
8478
8479
8480Callback Safety
8481---------------
8482Unsafe. You should avoid calling this in the data callback because it may call into the backend
8483which may or may not be safe.
8484*/
8486
8487
8488/*
8489Retrieves the name of the device.
8490
8491
8492Parameters
8493----------
8494pDevice (in)
8495 A pointer to the device whose information is being retrieved.
8496
8497type (in)
8498 The device type. This parameter is required for duplex devices. When retrieving device
8499 information, you are doing so for an individual playback or capture device.
8500
8501pName (out)
8502 A pointer to the buffer that will receive the name.
8503
8504nameCap (in)
8505 The capacity of the output buffer, including space for the null terminator.
8506
8507pLengthNotIncludingNullTerminator (out, optional)
8508 A pointer to the variable that will receive the length of the name, not including the null
8509 terminator.
8510
8511
8512Return Value
8513------------
8514MA_SUCCESS if successful; any other error code otherwise.
8515
8516
8517Thread Safety
8518-------------
8519Unsafe. This should be considered unsafe because it may be calling into the backend which may or
8520may not be safe.
8521
8522
8523Callback Safety
8524---------------
8525Unsafe. You should avoid calling this in the data callback because it may call into the backend
8526which may or may not be safe.
8527
8528
8529Remarks
8530-------
8531If the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to
8532`pName` if you want to first get the length of the name for the purpose of memory allocation of the
8533output buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for
8534most cases and will avoid the need for the inefficiency of calling this function twice.
8535
8536This is implemented in terms of `ma_device_get_info()`.
8537*/
8538MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator);
8539
8540
8541/*
8542Starts the device. For playback devices this begins playback. For capture devices it begins recording.
8543
8544Use `ma_device_stop()` to stop the device.
8545
8546
8547Parameters
8548----------
8549pDevice (in)
8550 A pointer to the device to start.
8551
8552
8553Return Value
8554------------
8555MA_SUCCESS if successful; any other error code otherwise.
8556
8557
8558Thread Safety
8559-------------
8560Safe. It's safe to call this from any thread with the exception of the callback thread.
8561
8562
8563Callback Safety
8564---------------
8565Unsafe. It is not safe to call this inside any callback.
8566
8567
8568Remarks
8569-------
8570For 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
8571audio data in the buffer, which needs to be done before the device begins playback.
8572
8573This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety.
8574
8575Do not call this in any callback.
8576
8577
8578See Also
8579--------
8580ma_device_stop()
8581*/
8583
8584/*
8585Stops the device. For playback devices this stops playback. For capture devices it stops recording.
8586
8587Use `ma_device_start()` to start the device again.
8588
8589
8590Parameters
8591----------
8592pDevice (in)
8593 A pointer to the device to stop.
8594
8595
8596Return Value
8597------------
8598MA_SUCCESS if successful; any other error code otherwise.
8599
8600
8601Thread Safety
8602-------------
8603Safe. It's safe to call this from any thread with the exception of the callback thread.
8604
8605
8606Callback Safety
8607---------------
8608Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.
8609
8610
8611Remarks
8612-------
8613This 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
8614backends 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
8615that was specified at initialization time).
8616
8617Backends 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
8618the 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
8619speakers or received from the microphone which can in turn result in de-syncs.
8620
8621Do not call this in any callback.
8622
8623This will be called implicitly by `ma_device_uninit()`.
8624
8625
8626See Also
8627--------
8628ma_device_start()
8629*/
8631
8632/*
8633Determines whether or not the device is started.
8634
8635
8636Parameters
8637----------
8638pDevice (in)
8639 A pointer to the device whose start state is being retrieved.
8640
8641
8642Return Value
8643------------
8644True if the device is started, false otherwise.
8645
8646
8647Thread Safety
8648-------------
8649Safe. 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
8650value will be out of sync.
8651
8652
8653Callback Safety
8654---------------
8655Safe. This is implemented as a simple accessor.
8656
8657
8658See Also
8659--------
8660ma_device_start()
8661ma_device_stop()
8662*/
8664
8665
8666/*
8667Retrieves the state of the device.
8668
8669
8670Parameters
8671----------
8672pDevice (in)
8673 A pointer to the device whose state is being retrieved.
8674
8675
8676Return Value
8677------------
8678The current state of the device. The return value will be one of the following:
8679
8680 +-------------------------------+------------------------------------------------------------------------------+
8681 | ma_device_state_uninitialized | Will only be returned if the device is in the middle of initialization. |
8682 +-------------------------------+------------------------------------------------------------------------------+
8683 | ma_device_state_stopped | The device is stopped. The initial state of the device after initialization. |
8684 +-------------------------------+------------------------------------------------------------------------------+
8685 | ma_device_state_started | The device started and requesting and/or delivering audio data. |
8686 +-------------------------------+------------------------------------------------------------------------------+
8687 | ma_device_state_starting | The device is in the process of starting. |
8688 +-------------------------------+------------------------------------------------------------------------------+
8689 | ma_device_state_stopping | The device is in the process of stopping. |
8690 +-------------------------------+------------------------------------------------------------------------------+
8691
8692
8693Thread Safety
8694-------------
8695Safe. 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,
8696there's a possibility the return value could be out of sync. See remarks.
8697
8698
8699Callback Safety
8700---------------
8701Safe. This is implemented as a simple accessor.
8702
8703
8704Remarks
8705-------
8706The general flow of a devices state goes like this:
8707
8708 ```
8709 ma_device_init() -> ma_device_state_uninitialized -> ma_device_state_stopped
8710 ma_device_start() -> ma_device_state_starting -> ma_device_state_started
8711 ma_device_stop() -> ma_device_state_stopping -> ma_device_state_stopped
8712 ```
8713
8714When 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
8715value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own
8716synchronization.
8717*/
8719
8720
8721/*
8722Performs post backend initialization routines for setting up internal data conversion.
8723
8724This should be called whenever the backend is initialized. The only time this should be called from
8725outside of miniaudio is if you're implementing a custom backend, and you would only do it if you
8726are reinitializing the backend due to rerouting or reinitializing for some reason.
8727
8728
8729Parameters
8730----------
8731pDevice [in]
8732 A pointer to the device.
8733
8734deviceType [in]
8735 The type of the device that was just reinitialized.
8736
8737pPlaybackDescriptor [in]
8738 The descriptor of the playback device containing the internal data format and buffer sizes.
8739
8740pPlaybackDescriptor [in]
8741 The descriptor of the capture device containing the internal data format and buffer sizes.
8742
8743
8744Return Value
8745------------
8746MA_SUCCESS if successful; any other error otherwise.
8747
8748
8749Thread Safety
8750-------------
8751Unsafe. This will be reinitializing internal data converters which may be in use by another thread.
8752
8753
8754Callback Safety
8755---------------
8756Unsafe. This will be reinitializing internal data converters which may be in use by the callback.
8757
8758
8759Remarks
8760-------
8761For a duplex device, you can call this for only one side of the system. This is why the deviceType
8762is specified as a parameter rather than deriving it from the device.
8763
8764You do not need to call this manually unless you are doing a custom backend, in which case you need
8765only do it if you're manually performing rerouting or reinitialization.
8766*/
8767MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPlaybackDescriptor, const ma_device_descriptor* pCaptureDescriptor);
8768
8769
8770/*
8771Sets the master volume factor for the device.
8772
8773The 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
8774values less than 0 decreases the volume.
8775
8776
8777Parameters
8778----------
8779pDevice (in)
8780 A pointer to the device whose volume is being set.
8781
8782volume (in)
8783 The new volume factor. Must be >= 0.
8784
8785
8786Return Value
8787------------
8788MA_SUCCESS if the volume was set successfully.
8789MA_INVALID_ARGS if pDevice is NULL.
8790MA_INVALID_ARGS if volume is negative.
8791
8792
8793Thread Safety
8794-------------
8795Safe. This just sets a local member of the device object.
8796
8797
8798Callback Safety
8799---------------
8800Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.
8801
8802
8803Remarks
8804-------
8805This applies the volume factor across all channels.
8806
8807This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.
8808
8809
8810See Also
8811--------
8812ma_device_get_master_volume()
8813ma_device_set_master_volume_db()
8814ma_device_get_master_volume_db()
8815*/
8817
8818/*
8819Retrieves the master volume factor for the device.
8820
8821
8822Parameters
8823----------
8824pDevice (in)
8825 A pointer to the device whose volume factor is being retrieved.
8826
8827pVolume (in)
8828 A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1].
8829
8830
8831Return Value
8832------------
8833MA_SUCCESS if successful.
8834MA_INVALID_ARGS if pDevice is NULL.
8835MA_INVALID_ARGS if pVolume is NULL.
8836
8837
8838Thread Safety
8839-------------
8840Safe. This just a simple member retrieval.
8841
8842
8843Callback Safety
8844---------------
8845Safe.
8846
8847
8848Remarks
8849-------
8850If an error occurs, `*pVolume` will be set to 0.
8851
8852
8853See Also
8854--------
8855ma_device_set_master_volume()
8856ma_device_set_master_volume_gain_db()
8857ma_device_get_master_volume_gain_db()
8858*/
8860
8861/*
8862Sets the master volume for the device as gain in decibels.
8863
8864A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume.
8865
8866
8867Parameters
8868----------
8869pDevice (in)
8870 A pointer to the device whose gain is being set.
8871
8872gainDB (in)
8873 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.
8874
8875
8876Return Value
8877------------
8878MA_SUCCESS if the volume was set successfully.
8879MA_INVALID_ARGS if pDevice is NULL.
8880MA_INVALID_ARGS if the gain is > 0.
8881
8882
8883Thread Safety
8884-------------
8885Safe. This just sets a local member of the device object.
8886
8887
8888Callback Safety
8889---------------
8890Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.
8891
8892
8893Remarks
8894-------
8895This applies the gain across all channels.
8896
8897This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.
8898
8899
8900See Also
8901--------
8902ma_device_get_master_volume_gain_db()
8903ma_device_set_master_volume()
8904ma_device_get_master_volume()
8905*/
8907
8908/*
8909Retrieves the master gain in decibels.
8910
8911
8912Parameters
8913----------
8914pDevice (in)
8915 A pointer to the device whose gain is being retrieved.
8916
8917pGainDB (in)
8918 A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0.
8919
8920
8921Return Value
8922------------
8923MA_SUCCESS if successful.
8924MA_INVALID_ARGS if pDevice is NULL.
8925MA_INVALID_ARGS if pGainDB is NULL.
8926
8927
8928Thread Safety
8929-------------
8930Safe. This just a simple member retrieval.
8931
8932
8933Callback Safety
8934---------------
8935Safe.
8936
8937
8938Remarks
8939-------
8940If an error occurs, `*pGainDB` will be set to 0.
8941
8942
8943See Also
8944--------
8945ma_device_set_master_volume_db()
8946ma_device_set_master_volume()
8947ma_device_get_master_volume()
8948*/
8950
8951
8952/*
8953Called from the data callback of asynchronous backends to allow miniaudio to process the data and fire the miniaudio data callback.
8954
8955
8956Parameters
8957----------
8958pDevice (in)
8959 A pointer to device whose processing the data callback.
8960
8961pOutput (out)
8962 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
8963 this can be NULL, in which case pInput must not be NULL.
8964
8965pInput (in)
8966 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
8967 NULL, in which case `pOutput` must not be NULL.
8968
8969frameCount (in)
8970 The number of frames being processed.
8971
8972
8973Return Value
8974------------
8975MA_SUCCESS if successful; any other result code otherwise.
8976
8977
8978Thread Safety
8979-------------
8980This function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a
8981playback and capture device in duplex setups.
8982
8983
8984Callback Safety
8985---------------
8986Do not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend.
8987
8988
8989Remarks
8990-------
8991If both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in
8992which case `pInput` will be processed first, followed by `pOutput`.
8993
8994If you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that
8995callback.
8996*/
8997MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
8998
8999
9000/*
9001Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile.
9002
9003This function is used by backends for helping determine an appropriately sized buffer to use with
9004the device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the
9005`pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a
9006best guess at the device's native sample rate is also required which is where `nativeSampleRate`
9007comes in. In addition, the performance profile is also needed for cases where both the period size
9008in frames and milliseconds are both zero.
9009
9010
9011Parameters
9012----------
9013pDescriptor (in)
9014 A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members
9015 will be used for the calculation of the buffer size.
9016
9017nativeSampleRate (in)
9018 The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of
9019 `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which
9020 case a sample rate is required to convert to a size in frames.
9021
9022performanceProfile (in)
9023 When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are
9024 zero, miniaudio will fall back to a buffer size based on the performance profile. The profile
9025 to use for this calculation is determine by this parameter.
9026
9027
9028Return Value
9029------------
9030The calculated buffer size in frames.
9031
9032
9033Thread Safety
9034-------------
9035This is safe so long as nothing modifies `pDescriptor` at the same time. However, this function
9036should only ever be called from within the backend's device initialization routine and therefore
9037shouldn't have any multithreading concerns.
9038
9039
9040Callback Safety
9041---------------
9042This is safe to call within the data callback, but there is no reason to ever do this.
9043
9044
9045Remarks
9046-------
9047If `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that
9048is also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead.
9049*/
9051
9052
9053
9054/*
9055Retrieves a friendly name for a backend.
9056*/
9058
9059/*
9060Determines whether or not the given backend is available by the compilation environment.
9061*/
9063
9064/*
9065Retrieves compile-time enabled backends.
9066
9067
9068Parameters
9069----------
9070pBackends (out, optional)
9071 A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting
9072 the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends.
9073
9074backendCap (in)
9075 The capacity of the `pBackends` buffer.
9076
9077pBackendCount (out)
9078 A pointer to the variable that will receive the enabled backend count.
9079
9080
9081Return Value
9082------------
9083MA_SUCCESS if successful.
9084MA_INVALID_ARGS if `pBackendCount` is NULL.
9085MA_NO_SPACE if the capacity of `pBackends` is not large enough.
9086
9087If `MA_NO_SPACE` is returned, the `pBackends` buffer will be filled with `*pBackendCount` values.
9088
9089
9090Thread Safety
9091-------------
9092Safe.
9093
9094
9095Callback Safety
9096---------------
9097Safe.
9098
9099
9100Remarks
9101-------
9102If you want to retrieve the number of backends so you can determine the capacity of `pBackends` buffer, you can call
9103this function with `pBackends` set to NULL.
9104
9105This will also enumerate the null backend. If you don't want to include this you need to check for `ma_backend_null`
9106when you enumerate over the returned backends and handle it appropriately. Alternatively, you can disable it at
9107compile time with `MA_NO_NULL`.
9108
9109The returned backends are determined based on compile time settings, not the platform it's currently running on. For
9110example, PulseAudio will be returned if it was enabled at compile time, even when the user doesn't actually have
9111PulseAudio installed.
9112
9113
9114Example 1
9115---------
9116The example below retrieves the enabled backend count using a fixed sized buffer allocated on the stack. The buffer is
9117given a capacity of `MA_BACKEND_COUNT` which will guarantee it'll be large enough to store all available backends.
9118Since `MA_BACKEND_COUNT` is always a relatively small value, this should be suitable for most scenarios.
9119
9120```
9121ma_backend enabledBackends[MA_BACKEND_COUNT];
9122size_t enabledBackendCount;
9123
9124result = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount);
9125if (result != MA_SUCCESS) {
9126 // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid.
9127}
9128```
9129
9130
9131See Also
9132--------
9133ma_is_backend_enabled()
9134*/
9135MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount);
9136
9137/*
9138Determines whether or not loopback mode is support by a backend.
9139*/
9141
9142#endif /* MA_NO_DEVICE_IO */
9143
9144
9145
9146
9152/*
9153Calculates a buffer size in milliseconds from the specified number of frames and sample rate.
9154*/
9156
9157/*
9158Calculates a buffer size in frames from the specified number of milliseconds and sample rate.
9159*/
9161
9162/*
9163Copies PCM frames from one buffer to another.
9164*/
9165MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
9166
9167/*
9168Copies silent frames into the given buffer.
9169
9170Remarks
9171-------
9172For 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
9173makes more sense for the purpose of mixing to initialize it to the center point.
9174*/
9175MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
9176
9177
9178/*
9179Offsets a pointer by the specified number of PCM frames.
9180*/
9181MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels);
9182MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels);
9183static 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); }
9184static 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); }
9185
9186
9187/*
9188Clips samples.
9189*/
9190MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count);
9191MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count);
9192MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count);
9193MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count);
9194MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count);
9195MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
9196
9197/*
9198Helper for applying a volume factor to samples.
9199
9200Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place.
9201*/
9202MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor);
9203MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor);
9204MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor);
9205MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor);
9206MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor);
9207
9208MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor);
9209MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor);
9210MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor);
9211MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor);
9212MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor);
9213
9214MA_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);
9215MA_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);
9216MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
9217MA_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);
9218MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
9219MA_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);
9220
9221MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
9222MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
9223MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
9224MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
9225MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
9226MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor);
9227
9228MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains);
9229
9230
9235MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume);
9236MA_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);
9237
9238
9239/*
9240Helper for converting a linear factor to gain in decibels.
9241*/
9242MA_API float ma_volume_linear_to_db(float factor);
9243
9244/*
9245Helper for converting gain in decibels to a linear factor.
9246*/
9248
9249
9250
9251
9252
9257typedef void ma_data_source;
9258
9259#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001
9260
9261typedef struct
9262{
9263 ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
9264 ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex);
9265 ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
9266 ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor);
9267 ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength);
9268 ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping);
9271
9272typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource);
9273
9274typedef struct
9275{
9278
9280
9281
9282typedef struct
9283{
9286 ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */
9287 ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */
9288 ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */
9289 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. */
9290 ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */
9291 ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */
9292 MA_ATOMIC(4, ma_bool32) isLooping;
9294
9297MA_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. */
9298MA_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); */
9300MA_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);
9302MA_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. */
9308MA_API void ma_data_source_get_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames);
9310MA_API void ma_data_source_get_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames);
9317
9318
9319typedef struct
9320{
9326 const void* pData;
9328
9329MA_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);
9331MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames);
9334MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount);
9335MA_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. */
9340
9341
9342
9343typedef struct
9344{
9348 const void* pData; /* If set to NULL, will allocate a block of memory for you. */
9351
9352MA_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);
9353
9354typedef struct
9355{
9358 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(). */
9359 ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */
9361
9364MA_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. */
9367MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop);
9369MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount);
9370MA_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. */
9375
9376
9377/*
9378Paged Audio Buffer
9379==================
9380A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It
9381can be used for cases where audio data is streamed in asynchronously while allowing data to be read
9382at the same time.
9383
9384This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across
9385simultaneously across different threads, however only one thread at a time can append, and only one
9386thread at a time can read and seek.
9387*/
9390{
9394};
9395
9396typedef struct
9397{
9400 ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */
9401 MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */
9403
9409MA_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);
9412MA_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);
9413
9414
9415typedef struct
9416{
9417 ma_paged_audio_buffer_data* pData; /* Must not be null. */
9419
9421
9422
9423typedef struct
9424{
9426 ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */
9428 ma_uint64 relativeCursor; /* Relative to the current page. */
9431
9434MA_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. */
9438
9439
9440
9441
9450typedef void ma_vfs;
9452
9453typedef enum
9454{
9455 MA_OPEN_MODE_READ = 0x00000001,
9456 MA_OPEN_MODE_WRITE = 0x00000002
9458
9459typedef enum
9460{
9463 ma_seek_origin_end /* Not used by decoders. */
9465
9466typedef struct
9467{
9469} ma_file_info;
9470
9471typedef struct
9472{
9473 ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
9474 ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
9475 ma_result (* onClose)(ma_vfs* pVFS, ma_vfs_file file);
9476 ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead);
9477 ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten);
9478 ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin);
9479 ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor);
9480 ma_result (* onInfo) (ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo);
9482
9483MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
9484MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
9486MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead);
9487MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten);
9491MA_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);
9492
9493typedef struct
9494{
9496 ma_allocation_callbacks allocationCallbacks; /* Only used for the wchar_t version of open() on non-Windows platforms. */
9498
9500
9501
9502
9503typedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead);
9504typedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin);
9505typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor);
9506
9507
9508
9509#if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)
9510typedef enum
9511{
9518#endif
9519
9520
9529#ifndef MA_NO_DECODING
9530typedef struct ma_decoder ma_decoder;
9531
9532
9533typedef struct
9534{
9536 ma_uint32 seekPointCount; /* Set to > 0 to generate a seektable if the decoding backend supports it. */
9538
9540
9541
9542typedef struct
9543{
9544 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);
9545 ma_result (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */
9546 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. */
9547 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. */
9548 void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks);
9550
9551
9552typedef ma_result (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); /* Returns the number of bytes read. */
9553typedef ma_result (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin);
9554typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor);
9555
9556typedef struct
9557{
9558 ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */
9559 ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */
9560 ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */
9567 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. */
9572
9574{
9576 ma_data_source* pBackend; /* The decoding backend we'll be pulling data from. */
9577 const ma_decoding_backend_vtable* pBackendVTable; /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */
9583 ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */
9587 ma_data_converter converter; /* Data conversion is achieved by running frames through this. */
9588 void* pInputCache; /* In input format. Can be null if it's not needed. */
9589 ma_uint64 inputCacheCap; /* The capacity of the input cache. */
9590 ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */
9591 ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cahce. */
9593 union
9594 {
9595 struct
9596 {
9600 struct
9601 {
9603 size_t dataSize;
9605 } memory; /* Only used for decoders that were opened against a block of memory. */
9607};
9608
9609MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate);
9611
9613MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
9614MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
9615MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
9616MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
9617MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
9618
9619/*
9620Uninitializes a decoder.
9621*/
9623
9624/*
9625Reads PCM frames from the given decoder.
9626
9627This is not thread safe without your own synchronization.
9628*/
9629MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
9630
9631/*
9632Seeks to a PCM frame based on it's absolute index.
9633
9634This is not thread safe without your own synchronization.
9635*/
9637
9638/*
9639Retrieves the decoder's output data format.
9640*/
9641MA_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);
9642
9643/*
9644Retrieves the current position of the read cursor in PCM frames.
9645*/
9647
9648/*
9649Retrieves the length of the decoder in PCM frames.
9650
9651Do not call this on streams of an undefined length, such as internet radio.
9652
9653If the length is unknown or an error occurs, 0 will be returned.
9654
9655This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio
9656uses internally.
9657
9658For MP3's, this will decode the entire file. Do not call this in time critical scenarios.
9659
9660This function is not thread safe without your own synchronization.
9661*/
9663
9664/*
9665Retrieves the number of frames that can be read before reaching the end.
9666
9667This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in
9668particular ensuring you do not call it on streams of an undefined length, such as internet radio.
9669
9670If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be
9671returned.
9672*/
9674
9675/*
9676Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input,
9677pConfig should be set to what you want. On output it will be set to what you got.
9678*/
9679MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
9680MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
9681MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
9682
9683#endif /* MA_NO_DECODING */
9684
9685
9686
9694#ifndef MA_NO_ENCODING
9695typedef struct ma_encoder ma_encoder;
9696
9697typedef ma_result (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten);
9698typedef ma_result (* ma_encoder_seek_proc) (ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin);
9700typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder);
9701typedef ma_result (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten);
9702
9703typedef struct
9704{
9711
9713
9715{
9723 void* pInternalEncoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */
9724 union
9725 {
9726 struct
9727 {
9732};
9733
9735MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
9736MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
9737MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
9738MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
9740MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten);
9741
9742#endif /* MA_NO_ENCODING */
9743
9744
9745
9750#ifndef MA_NO_GENERATION
9751typedef enum
9752{
9758
9759typedef struct
9760{
9768
9769MA_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);
9770
9771typedef struct
9772{
9775 double advance;
9776 double time;
9777} ma_waveform;
9778
9781MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
9787
9788typedef enum
9789{
9794
9795
9796typedef struct
9797{
9805
9807
9808typedef struct
9809{
9813 union
9814 {
9815 struct
9816 {
9817 double** bin;
9820 } pink;
9821 struct
9822 {
9823 double* accumulation;
9824 } brownian;
9825 } state;
9826
9827 /* Memory management. */
9828 void* _pHeap;
9830} ma_noise;
9831
9832MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes);
9834MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise);
9835MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks);
9836MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
9840
9841#endif /* MA_NO_GENERATION */
9842
9843
9844
9845
9850/* The resource manager cannot be enabled if there is no decoder. */
9851#if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_NO_DECODING)
9852#define MA_NO_RESOURCE_MANAGER
9853#endif
9854
9855#ifndef MA_NO_RESOURCE_MANAGER
9861
9862typedef enum
9863{
9864 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. */
9865 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. */
9866 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */
9867 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(). */
9869
9870
9871/*
9872Pipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional.
9873*/
9874typedef struct
9875{
9879
9880typedef struct
9881{
9882 ma_resource_manager_pipeline_stage_notification init; /* Initialization of the decoder. */
9885
9887
9888
9889
9890/* BEGIN BACKWARDS COMPATIBILITY */
9891/* TODO: Remove this block in version 0.12. */
9892#if 1
9893#define ma_resource_manager_job ma_job
9894#define ma_resource_manager_job_init ma_job_init
9895#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING MA_JOB_QUEUE_FLAG_NON_BLOCKING
9896#define ma_resource_manager_job_queue_config ma_job_queue_config
9897#define ma_resource_manager_job_queue_config_init ma_job_queue_config_init
9898#define ma_resource_manager_job_queue ma_job_queue
9899#define ma_resource_manager_job_queue_get_heap_size ma_job_queue_get_heap_size
9900#define ma_resource_manager_job_queue_init_preallocated ma_job_queue_init_preallocated
9901#define ma_resource_manager_job_queue_init ma_job_queue_init
9902#define ma_resource_manager_job_queue_uninit ma_job_queue_uninit
9903#define ma_resource_manager_job_queue_post ma_job_queue_post
9904#define ma_resource_manager_job_queue_next ma_job_queue_next
9905#endif
9906/* END BACKWARDS COMPATIBILITY */
9907
9908
9909
9910
9911/* 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. */
9912#ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT
9913#define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64
9914#endif
9915
9916typedef enum
9917{
9918 /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */
9920
9921 /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */
9924
9925typedef struct
9926{
9927 const char* pFilePath;
9928 const wchar_t* pFilePathW;
9938
9940
9941
9942typedef enum
9943{
9944 ma_resource_manager_data_supply_type_unknown = 0, /* Used for determining whether or the data supply has been initialized. */
9945 ma_resource_manager_data_supply_type_encoded, /* Data supply is an encoded buffer. Connector is ma_decoder. */
9946 ma_resource_manager_data_supply_type_decoded, /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */
9947 ma_resource_manager_data_supply_type_decoded_paged /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */
9949
9950typedef struct
9951{
9952 MA_ATOMIC(4, ma_resource_manager_data_supply_type) type; /* Read and written from different threads so needs to be accessed atomically. */
9953 union
9954 {
9955 struct
9956 {
9957 const void* pData;
9959 } encoded;
9960 struct
9961 {
9962 const void* pData;
9968 } decoded;
9969 struct
9970 {
9972 ma_uint64 decodedFrameCount;
9973 ma_uint32 sampleRate;
9974 } decodedPaged;
9977
9979{
9980 ma_uint32 hashedName32; /* The hashed name. This is the key. */
9982 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. */
9983 MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */
9984 MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
9985 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_*()). */
9990};
9991
9993{
9994 ma_data_source_base ds; /* Base data source. A data buffer is a data source. */
9995 ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this buffer. */
9996 ma_resource_manager_data_buffer_node* pNode; /* The data node. This is reference counted and is what supplies the data. */
9997 ma_uint32 flags; /* The flags that were passed used to initialize the buffer. */
9998 MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */
9999 MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
10000 ma_uint64 seekTargetInPCMFrames; /* Only updated by the public API. Never written nor read from the job thread. */
10001 ma_bool32 seekToCursorOnNextRead; /* On the next read we need to seek to the frame cursor. */
10002 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. */
10003 MA_ATOMIC(4, ma_bool32) isLooping; /* Can be read and written by different threads at the same time. Must be used atomically. */
10004 ma_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. */
10005 union
10006 {
10007 ma_decoder decoder; /* Supply type is ma_resource_manager_data_supply_type_encoded */
10008 ma_audio_buffer buffer; /* Supply type is ma_resource_manager_data_supply_type_decoded */
10009 ma_paged_audio_buffer pagedBuffer; /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */
10010 } connector; /* Connects this object to the node's data supply. */
10011};
10012
10014{
10015 ma_data_source_base ds; /* Base data source. A data stream is a data source. */
10016 ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this data stream. */
10017 ma_uint32 flags; /* The flags that were passed used to initialize the stream. */
10018 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. */
10019 ma_bool32 isDecoderInitialized; /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */
10020 ma_uint64 totalLengthInPCMFrames; /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */
10021 ma_uint32 relativeCursor; /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */
10022 MA_ATOMIC(8, ma_uint64) absoluteCursor; /* The playback cursor, in absolute position starting from the start of the file. */
10023 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. */
10024 MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */
10025 MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
10026
10027 /* Written by the public API, read by the job thread. */
10028 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. */
10029
10030 /* Written by the job thread, read by the public API. */
10031 void* pPageData; /* Buffer containing the decoded data of each page. Allocated once at initialization time. */
10032 MA_ATOMIC(4, ma_uint32) pageFrameCount[2]; /* The number of valid PCM frames in each page. Used to determine the last valid frame. */
10033
10034 /* Written and read by both the public API and the job thread. These must be atomic. */
10035 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. */
10036 MA_ATOMIC(4, ma_bool32) isDecoderAtEnd; /* Whether or not the decoder has reached the end. */
10037 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. */
10038 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. */
10039};
10040
10042{
10043 union
10044 {
10047 } backend; /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */
10048
10049 ma_uint32 flags; /* The flags that were passed in to ma_resource_manager_data_source_init(). */
10050 MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */
10051 MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
10052};
10053
10054typedef struct
10055{
10058 ma_format decodedFormat; /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */
10059 ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */
10060 ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */
10061 ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */
10062 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. */
10064 ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */
10069
10071
10073{
10075 ma_resource_manager_data_buffer_node* pRootDataBufferNode; /* The root buffer in the binary tree. */
10076#ifndef MA_NO_THREADING
10077 ma_mutex dataBufferBSTLock; /* For synchronizing access to the data buffer binary tree. */
10078 ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]; /* The threads for executing jobs. */
10079#endif
10080 ma_job_queue jobQueue; /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */
10081 ma_default_vfs defaultVFS; /* Only used if a custom VFS is not specified. */
10082 ma_log log; /* Only used if no log was specified in the config. */
10083};
10084
10085/* Init. */
10089
10090/* Registration. */
10091MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags);
10092MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags);
10093MA_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. */
10094MA_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);
10095MA_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. */
10096MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes);
10101
10102/* Data Buffers. */
10110MA_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);
10117
10118/* Data Streams. */
10125MA_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);
10132
10133/* Data Sources. */
10141MA_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);
10148
10149/* Job management. */
10151MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager); /* Helper for posting a quit job. */
10153MA_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. */
10154MA_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. */
10155#endif /* MA_NO_RESOURCE_MANAGER */
10156
10157
10158
10159
10164#ifndef MA_NO_NODE_GRAPH
10165/* Must never exceed 254. */
10166#ifndef MA_MAX_NODE_BUS_COUNT
10167#define MA_MAX_NODE_BUS_COUNT 254
10168#endif
10169
10170/* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */
10171#ifndef MA_MAX_NODE_LOCAL_BUS_COUNT
10172#define MA_MAX_NODE_LOCAL_BUS_COUNT 2
10173#endif
10174
10175/* Use this when the bus count is determined by the node instance rather than the vtable. */
10176#define MA_NODE_BUS_COUNT_UNKNOWN 255
10177
10179typedef void ma_node;
10180
10181
10182/* Node flags. */
10183typedef enum
10184{
10189 MA_NODE_FLAG_SILENT_OUTPUT = 0x00000010
10191
10192
10193/* The playback state of a node. Either started or stopped. */
10194typedef enum
10195{
10199
10200
10201typedef struct
10202{
10203 /*
10204 Extended processing callback. This callback is used for effects that process input and output
10205 at different rates (i.e. they perform resampling). This is similar to the simple version, only
10206 they take two seperate frame counts: one for input, and one for output.
10207
10208 On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas
10209 `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`.
10210
10211 On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set
10212 `pFrameCountIn` to the number of input frames that were consumed.
10213 */
10214 void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut);
10215
10216 /*
10217 A callback for retrieving the number of a input frames that are required to output the
10218 specified number of output frames. You would only want to implement this when the node performs
10219 resampling. This is optional, even for nodes that perform resampling, but it does offer a
10220 small reduction in latency as it allows miniaudio to calculate the exact number of input frames
10221 to read at a time instead of having to estimate.
10222 */
10223 ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount);
10224
10225 /*
10226 The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn`
10227 parameters of the callbacks above.
10228 */
10230
10231 /*
10232 The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut`
10233 parameters of the callbacks above.
10234 */
10236
10237 /*
10238 Flags describing characteristics of the node. This is currently just a placeholder for some
10239 ideas for later on.
10240 */
10243
10244typedef struct
10245{
10246 const ma_node_vtable* vtable; /* Should never be null. Initialization of the node will fail if so. */
10247 ma_node_state initialState; /* Defaults to ma_node_state_started. */
10248 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). */
10249 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). */
10250 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`. */
10251 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`. */
10253
10255
10256
10257/*
10258A node has multiple output buses. An output bus is attached to an input bus as an item in a linked
10259list. Think of the input bus as a linked list, with the output bus being an item in that list.
10260*/
10263{
10264 /* Immutable. */
10265 ma_node* pNode; /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */
10266 ma_uint8 outputBusIndex; /* The index of the output bus on pNode that this output bus represents. */
10267 ma_uint8 channels; /* The number of channels in the audio stream for this bus. */
10268
10269 /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */
10270 MA_ATOMIC(1, ma_uint8) inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. */
10271 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_*. */
10272 MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */
10273 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. */
10274 MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */
10275 MA_ATOMIC(4, float) volume; /* Linear. */
10276 MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext; /* If null, it's the tail node or detached. */
10277 MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev; /* If null, it's the head node or detached. */
10278 MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode; /* The node that this output bus is attached to. Required for detaching. */
10279};
10280
10281/*
10282A node has multiple input buses. The output buses of a node are connecting to the input busses of
10283another. An input bus is essentially just a linked list of output buses.
10284*/
10287{
10288 /* Mutable via multiple threads. */
10289 ma_node_output_bus head; /* Dummy head node for simplifying some lock-free thread-safety stuff. */
10290 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. */
10291 MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */
10292
10293 /* Set once at startup. */
10294 ma_uint8 channels; /* The number of channels in the audio stream for this bus. */
10295};
10296
10297
10300{
10301 /* These variables are set once at startup. */
10302 ma_node_graph* pNodeGraph; /* The graph this node belongs to. */
10304 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. */
10305 ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */
10306
10307 /* These variables are read and written only from the audio thread. */
10311
10312 /* These variables are read and written between different threads. */
10313 MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */
10314 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. */
10315 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()`. */
10320
10321 /* Memory management. */
10324 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. */
10325 ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */
10326};
10327
10328MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes);
10329MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode);
10330MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode);
10331MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10337MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex);
10341MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex);
10350
10351
10352typedef struct
10353{
10357
10359
10360
10362{
10363 /* Immutable. */
10364 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. */
10365 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(). */
10367
10368 /* Read and written by multiple threads. */
10369 MA_ATOMIC(4, ma_bool32) isReading;
10370};
10371
10372MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph);
10373MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks);
10375MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
10379
10380
10381
10382/* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */
10383typedef struct
10384{
10388
10390
10391
10392typedef struct
10393{
10397
10399MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks);
10402
10403
10404/* Splitter Node. 1 input, 2 outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */
10405typedef struct
10406{
10410
10412
10413
10414typedef struct
10415{
10418
10419MA_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);
10420MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks);
10421
10422
10423/*
10424Biquad Node
10425*/
10426typedef struct
10427{
10431
10432MA_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);
10433
10434
10435typedef struct
10436{
10440
10443MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10444
10445
10446/*
10447Low Pass Filter Node
10448*/
10449typedef struct
10450{
10454
10455MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
10456
10457
10458typedef struct
10459{
10462} ma_lpf_node;
10463
10464MA_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);
10466MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10467
10468
10469/*
10470High Pass Filter Node
10471*/
10472typedef struct
10473{
10477
10478MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
10479
10480
10481typedef struct
10482{
10485} ma_hpf_node;
10486
10487MA_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);
10489MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10490
10491
10492/*
10493Band Pass Filter Node
10494*/
10495typedef struct
10496{
10500
10501MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
10502
10503
10504typedef struct
10505{
10508} ma_bpf_node;
10509
10510MA_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);
10512MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10513
10514
10515/*
10516Notching Filter Node
10517*/
10518typedef struct
10519{
10523
10524MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency);
10525
10526
10527typedef struct
10528{
10532
10533MA_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);
10535MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10536
10537
10538/*
10539Peaking Filter Node
10540*/
10541typedef struct
10542{
10546
10547MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
10548
10549
10550typedef struct
10551{
10554} ma_peak_node;
10555
10556MA_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);
10558MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10559
10560
10561/*
10562Low Shelf Filter Node
10563*/
10564typedef struct
10565{
10569
10570MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
10571
10572
10573typedef struct
10574{
10578
10582
10583
10584/*
10585High Shelf Filter Node
10586*/
10587typedef struct
10588{
10592
10593MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
10594
10595
10596typedef struct
10597{
10601
10605
10606
10607typedef struct
10608{
10612
10613MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay);
10614
10615
10616typedef struct
10617{
10621
10622MA_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);
10623MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks);
10624MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value);
10626MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value);
10628MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value);
10630#endif /* MA_NO_NODE_GRAPH */
10631
10632
10633
10638#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH)
10639typedef struct ma_engine ma_engine;
10640typedef struct ma_sound ma_sound;
10641
10642
10643/* Sound flags. */
10644typedef enum
10645{
10646 MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */
10647 MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */
10648 MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */
10649 MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */
10650 MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00000010, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */
10651 MA_SOUND_FLAG_NO_PITCH = 0x00000020, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */
10652 MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00000040 /* Disable spatialization. */
10654
10655#ifndef MA_ENGINE_MAX_LISTENERS
10656#define MA_ENGINE_MAX_LISTENERS 4
10657#endif
10658
10659#define MA_LISTENER_INDEX_CLOSEST ((ma_uint8)-1)
10660
10661typedef enum
10662{
10666
10667typedef struct
10668{
10673 ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */
10674 ma_bool8 isPitchDisabled; /* Pitching can be explicitly disable with MA_SOUND_FLAG_NO_PITCH to optimize processing. */
10675 ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */
10676 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. */
10678
10680
10681
10682/* Base node object for both ma_sound and ma_sound_group. */
10683typedef struct
10684{
10685 ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */
10686 ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */
10687 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. */
10689 ma_linear_resampler resampler; /* For pitch shift. */
10692 MA_ATOMIC(4, float) pitch;
10693 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. */
10694 float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */
10695 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. */
10696 MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */
10697 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. */
10698
10699 /* Memory management. */
10701 void* _pHeap;
10703
10704MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes);
10706MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode);
10707MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks);
10708
10709
10710#define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF
10711
10712typedef struct
10713{
10714 const char* pFilePath; /* Set this to load from the resource manager. */
10715 const wchar_t* pFilePathW; /* Set this to load from the resource manager. */
10716 ma_data_source* pDataSource; /* Set this to load from an existing data source. */
10717 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`. */
10718 ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */
10719 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. */
10720 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). */
10721 ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */
10722 ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */
10728 ma_fence* pDoneFence; /* Released when the resource manager has finished decoding the entire sound. Not used with streams. */
10730
10732
10734{
10735 ma_engine_node engineNode; /* Must be the first member for compatibility with the ma_node API. */
10737 ma_uint64 seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */
10740
10741 /*
10742 We're declaring a resource manager data source object here to save us a malloc when loading a
10743 sound via the resource manager, which I *think* will be the most common scenario.
10744 */
10745#ifndef MA_NO_RESOURCE_MANAGER
10747#endif
10748};
10749
10750/* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */
10753{
10757};
10758
10759/* A sound group is just a sound. */
10762
10764
10765
10766typedef struct
10767{
10768#if !defined(MA_NO_RESOURCE_MANAGER)
10769 ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */
10770#endif
10771#if !defined(MA_NO_DEVICE_IO)
10773 ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */
10774 ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */
10775#endif
10776 ma_log* pLog; /* When set to NULL, will use the context's log. */
10777 ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */
10778 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. */
10779 ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */
10780 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.*/
10781 ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */
10782 ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */
10783 ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */
10785 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(). */
10786 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. */
10787 ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */
10788 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. */
10790
10792
10793
10795{
10796 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. */
10797#if !defined(MA_NO_RESOURCE_MANAGER)
10799#endif
10800#if !defined(MA_NO_DEVICE_IO)
10801 ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */
10802#endif
10810 ma_spinlock inlinedSoundLock; /* For synchronizing access so the inlined sound list. */
10811 ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */
10812 MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */
10813 ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */
10815};
10816
10819MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
10821#if !defined(MA_NO_RESOURCE_MANAGER)
10823#endif
10831
10836
10838MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ);
10839MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
10841MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
10843MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
10845MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
10846MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
10847MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
10849MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled);
10851
10852#ifndef MA_NO_RESOURCE_MANAGER
10853MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex);
10854MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */
10855#endif
10856
10857#ifndef MA_NO_RESOURCE_MANAGER
10858MA_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);
10859MA_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);
10860MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound);
10861#endif
10869MA_API void ma_sound_set_volume(ma_sound* pSound, float volume);
10871MA_API void ma_sound_set_pan(ma_sound* pSound, float pan);
10872MA_API float ma_sound_get_pan(const ma_sound* pSound);
10875MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch);
10883MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z);
10885MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z);
10887MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z);
10893MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff);
10895MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain);
10897MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain);
10899MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance);
10901MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance);
10903MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
10904MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
10905MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor);
10907MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor);
10909MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames);
10910MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds);
10912MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames);
10913MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds);
10914MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames);
10915MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds);
10921MA_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(). */
10922MA_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);
10927
10948MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z);
10950MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z);
10952MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z);
10968MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
10969MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
10972MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor);
10974MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames);
10975MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds);
10980MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds);
10983#endif /* MA_NO_ENGINE */
10984
10985#ifdef __cplusplus
10986}
10987#endif
10988#endif /* miniaudio_h */
10989
10990
10991/*
10992This is for preventing greying out of the implementation section.
10993*/
10994#if defined(Q_CREATOR_RUN) || defined(__INTELLISENSE__) || defined(__CDT_PARSER__)
10995#define MINIAUDIO_IMPLEMENTATION
10996#endif
10997
10998
11005#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
11006#ifndef miniaudio_c
11007#define miniaudio_c
11008
11009#include <assert.h>
11010#include <limits.h> /* For INT_MAX */
11011#include <math.h> /* sin(), etc. */
11012
11013#include <stdarg.h>
11014#include <stdio.h>
11015#if !defined(_MSC_VER) && !defined(__DMC__)
11016 #include <strings.h> /* For strcasecmp(). */
11017 #include <wchar.h> /* For wcslen(), wcsrtombs() */
11018#endif
11019#ifdef _MSC_VER
11020 #include <float.h> /* For _controlfp_s constants */
11021#endif
11022
11023#ifdef MA_WIN32
11024#include <windows.h>
11025#else
11026#include <stdlib.h> /* For malloc(), free(), wcstombs(). */
11027#include <string.h> /* For memset() */
11028#include <sched.h>
11029#include <sys/time.h> /* select() (used for ma_sleep()). */
11030#include <pthread.h>
11031#endif
11032
11033#include <sys/stat.h> /* For fstat(), etc. */
11034
11035#ifdef MA_EMSCRIPTEN
11036#include <emscripten/emscripten.h>
11037#endif
11038
11039#if !defined(MA_64BIT) && !defined(MA_32BIT)
11040#ifdef _WIN32
11041#ifdef _WIN64
11042#define MA_64BIT
11043#else
11044#define MA_32BIT
11045#endif
11046#endif
11047#endif
11048
11049#if !defined(MA_64BIT) && !defined(MA_32BIT)
11050#ifdef __GNUC__
11051#ifdef __LP64__
11052#define MA_64BIT
11053#else
11054#define MA_32BIT
11055#endif
11056#endif
11057#endif
11058
11059#if !defined(MA_64BIT) && !defined(MA_32BIT)
11060#include <stdint.h>
11061#if INTPTR_MAX == INT64_MAX
11062#define MA_64BIT
11063#else
11064#define MA_32BIT
11065#endif
11066#endif
11067
11068/* Architecture Detection */
11069#if defined(__x86_64__) || defined(_M_X64)
11070#define MA_X64
11071#elif defined(__i386) || defined(_M_IX86)
11072#define MA_X86
11073#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64)
11074#define MA_ARM
11075#endif
11076
11077/* Intrinsics Support */
11078#if defined(MA_X64) || defined(MA_X86)
11079 #if defined(_MSC_VER) && !defined(__clang__)
11080 /* MSVC. */
11081 #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */
11082 #define MA_SUPPORT_SSE2
11083 #endif
11084 /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */
11085 /* #define MA_SUPPORT_AVX*/
11086 /*#endif*/
11087 #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */
11088 #define MA_SUPPORT_AVX2
11089 #endif
11090 #else
11091 /* Assume GNUC-style. */
11092 #if defined(__SSE2__) && !defined(MA_NO_SSE2)
11093 #define MA_SUPPORT_SSE2
11094 #endif
11095 /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/
11096 /* #define MA_SUPPORT_AVX*/
11097 /*#endif*/
11098 #if defined(__AVX2__) && !defined(MA_NO_AVX2)
11099 #define MA_SUPPORT_AVX2
11100 #endif
11101 #endif
11102
11103 /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */
11104 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
11105 #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include(<emmintrin.h>)
11106 #define MA_SUPPORT_SSE2
11107 #endif
11108 /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include(<immintrin.h>)*/
11109 /* #define MA_SUPPORT_AVX*/
11110 /*#endif*/
11111 #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include(<immintrin.h>)
11112 #define MA_SUPPORT_AVX2
11113 #endif
11114 #endif
11115
11116 #if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX)
11117 #include <immintrin.h>
11118 #elif defined(MA_SUPPORT_SSE2)
11119 #include <emmintrin.h>
11120 #endif
11121#endif
11122
11123#if defined(MA_ARM)
11124 #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
11125 #define MA_SUPPORT_NEON
11126 #include <arm_neon.h>
11127 #endif
11128#endif
11129
11130/* Begin globally disabled warnings. */
11131#if defined(_MSC_VER)
11132 #pragma warning(push)
11133 #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */
11134 #pragma warning(disable:4049) /* compiler limit : terminating line number emission */
11135#endif
11136
11137#if defined(MA_X64) || defined(MA_X86)
11138 #if defined(_MSC_VER) && !defined(__clang__)
11139 #if _MSC_VER >= 1400
11140 #include <intrin.h>
11141 static MA_INLINE void ma_cpuid(int info[4], int fid)
11142 {
11143 __cpuid(info, fid);
11144 }
11145 #else
11146 #define MA_NO_CPUID
11147 #endif
11148
11149 #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219)
11150 static MA_INLINE unsigned __int64 ma_xgetbv(int reg)
11151 {
11152 return _xgetbv(reg);
11153 }
11154 #else
11155 #define MA_NO_XGETBV
11156 #endif
11157 #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID)
11158 static MA_INLINE void ma_cpuid(int info[4], int fid)
11159 {
11160 /*
11161 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
11162 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
11163 supporting different assembly dialects.
11164
11165 What's basically happening is that we're saving and restoring the ebx register manually.
11166 */
11167 #if defined(DRFLAC_X86) && defined(__PIC__)
11168 __asm__ __volatile__ (
11169 "xchg{l} {%%}ebx, %k1;"
11170 "cpuid;"
11171 "xchg{l} {%%}ebx, %k1;"
11172 : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
11173 );
11174 #else
11175 __asm__ __volatile__ (
11176 "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
11177 );
11178 #endif
11179 }
11180
11181 static MA_INLINE ma_uint64 ma_xgetbv(int reg)
11182 {
11183 unsigned int hi;
11184 unsigned int lo;
11185
11186 __asm__ __volatile__ (
11187 "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg)
11188 );
11189
11190 return ((ma_uint64)hi << 32) | (ma_uint64)lo;
11191 }
11192 #else
11193 #define MA_NO_CPUID
11194 #define MA_NO_XGETBV
11195 #endif
11196#else
11197 #define MA_NO_CPUID
11198 #define MA_NO_XGETBV
11199#endif
11200
11201static MA_INLINE ma_bool32 ma_has_sse2(void)
11202{
11203#if defined(MA_SUPPORT_SSE2)
11204 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2)
11205 #if defined(MA_X64)
11206 return MA_TRUE; /* 64-bit targets always support SSE2. */
11207 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
11208 return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */
11209 #else
11210 #if defined(MA_NO_CPUID)
11211 return MA_FALSE;
11212 #else
11213 int info[4];
11214 ma_cpuid(info, 1);
11215 return (info[3] & (1 << 26)) != 0;
11216 #endif
11217 #endif
11218 #else
11219 return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */
11220 #endif
11221#else
11222 return MA_FALSE; /* No compiler support. */
11223#endif
11224}
11225
11226#if 0
11227static MA_INLINE ma_bool32 ma_has_avx()
11228{
11229#if defined(MA_SUPPORT_AVX)
11230 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX)
11231 #if defined(_AVX_) || defined(__AVX__)
11232 return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */
11233 #else
11234 /* AVX requires both CPU and OS support. */
11235 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
11236 return MA_FALSE;
11237 #else
11238 int info[4];
11239 ma_cpuid(info, 1);
11240 if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) {
11241 ma_uint64 xrc = ma_xgetbv(0);
11242 if ((xrc & 0x06) == 0x06) {
11243 return MA_TRUE;
11244 } else {
11245 return MA_FALSE;
11246 }
11247 } else {
11248 return MA_FALSE;
11249 }
11250 #endif
11251 #endif
11252 #else
11253 return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */
11254 #endif
11255#else
11256 return MA_FALSE; /* No compiler support. */
11257#endif
11258}
11259#endif
11260
11261static MA_INLINE ma_bool32 ma_has_avx2(void)
11262{
11263#if defined(MA_SUPPORT_AVX2)
11264 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2)
11265 #if defined(_AVX2_) || defined(__AVX2__)
11266 return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */
11267 #else
11268 /* AVX2 requires both CPU and OS support. */
11269 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
11270 return MA_FALSE;
11271 #else
11272 int info1[4];
11273 int info7[4];
11274 ma_cpuid(info1, 1);
11275 ma_cpuid(info7, 7);
11276 if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) {
11277 ma_uint64 xrc = ma_xgetbv(0);
11278 if ((xrc & 0x06) == 0x06) {
11279 return MA_TRUE;
11280 } else {
11281 return MA_FALSE;
11282 }
11283 } else {
11284 return MA_FALSE;
11285 }
11286 #endif
11287 #endif
11288 #else
11289 return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */
11290 #endif
11291#else
11292 return MA_FALSE; /* No compiler support. */
11293#endif
11294}
11295
11296static MA_INLINE ma_bool32 ma_has_neon(void)
11297{
11298#if defined(MA_SUPPORT_NEON)
11299 #if defined(MA_ARM) && !defined(MA_NO_NEON)
11300 #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
11301 return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */
11302 #else
11303 /* TODO: Runtime check. */
11304 return MA_FALSE;
11305 #endif
11306 #else
11307 return MA_FALSE; /* NEON is only supported on ARM architectures. */
11308 #endif
11309#else
11310 return MA_FALSE; /* No compiler support. */
11311#endif
11312}
11313
11314#define MA_SIMD_NONE 0
11315#define MA_SIMD_SSE2 1
11316#define MA_SIMD_AVX2 2
11317#define MA_SIMD_NEON 3
11318
11319#ifndef MA_PREFERRED_SIMD
11320 # if defined(MA_SUPPORT_SSE2) && defined(MA_PREFER_SSE2)
11321 #define MA_PREFERRED_SIMD MA_SIMD_SSE2
11322 #elif defined(MA_SUPPORT_AVX2) && defined(MA_PREFER_AVX2)
11323 #define MA_PREFERRED_SIMD MA_SIMD_AVX2
11324 #elif defined(MA_SUPPORT_NEON) && defined(MA_PREFER_NEON)
11325 #define MA_PREFERRED_SIMD MA_SIMD_NEON
11326 #else
11327 #define MA_PREFERRED_SIMD MA_SIMD_NONE
11328 #endif
11329#endif
11330
11331#if defined(__has_builtin)
11332 #define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x)
11333#else
11334 #define MA_COMPILER_HAS_BUILTIN(x) 0
11335#endif
11336
11337#ifndef MA_ASSUME
11338 #if MA_COMPILER_HAS_BUILTIN(__builtin_assume)
11339 #define MA_ASSUME(x) __builtin_assume(x)
11340 #elif MA_COMPILER_HAS_BUILTIN(__builtin_unreachable)
11341 #define MA_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0)
11342 #elif defined(_MSC_VER)
11343 #define MA_ASSUME(x) __assume(x)
11344 #else
11345 #define MA_ASSUME(x) (void)(x)
11346 #endif
11347#endif
11348
11349#ifndef MA_RESTRICT
11350 #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER)
11351 #define MA_RESTRICT __restrict
11352 #else
11353 #define MA_RESTRICT
11354 #endif
11355#endif
11356
11357#if defined(_MSC_VER) && _MSC_VER >= 1400
11358 #define MA_HAS_BYTESWAP16_INTRINSIC
11359 #define MA_HAS_BYTESWAP32_INTRINSIC
11360 #define MA_HAS_BYTESWAP64_INTRINSIC
11361#elif defined(__clang__)
11362 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap16)
11363 #define MA_HAS_BYTESWAP16_INTRINSIC
11364 #endif
11365 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap32)
11366 #define MA_HAS_BYTESWAP32_INTRINSIC
11367 #endif
11368 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap64)
11369 #define MA_HAS_BYTESWAP64_INTRINSIC
11370 #endif
11371#elif defined(__GNUC__)
11372 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
11373 #define MA_HAS_BYTESWAP32_INTRINSIC
11374 #define MA_HAS_BYTESWAP64_INTRINSIC
11375 #endif
11376 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
11377 #define MA_HAS_BYTESWAP16_INTRINSIC
11378 #endif
11379#endif
11380
11381
11382static MA_INLINE ma_bool32 ma_is_little_endian(void)
11383{
11384#if defined(MA_X86) || defined(MA_X64)
11385 return MA_TRUE;
11386#else
11387 int n = 1;
11388 return (*(char*)&n) == 1;
11389#endif
11390}
11391
11392static MA_INLINE ma_bool32 ma_is_big_endian(void)
11393{
11394 return !ma_is_little_endian();
11395}
11396
11397
11398static MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n)
11399{
11400#ifdef MA_HAS_BYTESWAP32_INTRINSIC
11401 #if defined(_MSC_VER)
11402 return _byteswap_ulong(n);
11403 #elif defined(__GNUC__) || defined(__clang__)
11404 #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. */
11405 /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */
11406 ma_uint32 r;
11407 __asm__ __volatile__ (
11408 #if defined(MA_64BIT)
11409 "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! */
11410 #else
11411 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
11412 #endif
11413 );
11414 return r;
11415 #else
11416 return __builtin_bswap32(n);
11417 #endif
11418 #else
11419 #error "This compiler does not support the byte swap intrinsic."
11420 #endif
11421#else
11422 return ((n & 0xFF000000) >> 24) |
11423 ((n & 0x00FF0000) >> 8) |
11424 ((n & 0x0000FF00) << 8) |
11425 ((n & 0x000000FF) << 24);
11426#endif
11427}
11428
11429
11430#if !defined(MA_EMSCRIPTEN)
11431#ifdef MA_WIN32
11432static void ma_sleep__win32(ma_uint32 milliseconds)
11433{
11434 Sleep((DWORD)milliseconds);
11435}
11436#endif
11437#ifdef MA_POSIX
11438static void ma_sleep__posix(ma_uint32 milliseconds)
11439{
11440#ifdef MA_EMSCRIPTEN
11441 (void)milliseconds;
11442 MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */
11443#else
11444 #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L
11445 struct timespec ts;
11446 ts.tv_sec = milliseconds / 1000;
11447 ts.tv_nsec = milliseconds % 1000 * 1000000;
11448 nanosleep(&ts, NULL);
11449 #else
11450 struct timeval tv;
11451 tv.tv_sec = milliseconds / 1000;
11452 tv.tv_usec = milliseconds % 1000 * 1000;
11453 select(0, NULL, NULL, NULL, &tv);
11454 #endif
11455#endif
11456}
11457#endif
11458
11459static MA_INLINE void ma_sleep(ma_uint32 milliseconds)
11460{
11461#ifdef MA_WIN32
11462 ma_sleep__win32(milliseconds);
11463#endif
11464#ifdef MA_POSIX
11465 ma_sleep__posix(milliseconds);
11466#endif
11467}
11468#endif
11469
11470static MA_INLINE void ma_yield()
11471{
11472#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
11473 /* x86/x64 */
11474 #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__)
11475 #if _MSC_VER >= 1400
11476 _mm_pause();
11477 #else
11478 #if defined(__DMC__)
11479 /* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */
11480 __asm nop;
11481 #else
11482 __asm pause;
11483 #endif
11484 #endif
11485 #else
11486 __asm__ __volatile__ ("pause");
11487 #endif
11488#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__)
11489 /* ARM */
11490 #if defined(_MSC_VER)
11491 /* 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. */
11492 __yield();
11493 #else
11494 __asm__ __volatile__ ("yield"); /* ARMv6K/ARMv6T2 and above. */
11495 #endif
11496#else
11497 /* Unknown or unsupported architecture. No-op. */
11498#endif
11499}
11500
11501
11502#define MA_MM_DENORMALS_ZERO_MASK 0x0040
11503#define MA_MM_FLUSH_ZERO_MASK 0x8000
11504
11505static MA_INLINE unsigned int ma_disable_denormals()
11506{
11507 unsigned int prevState;
11508
11509 #if defined(_MSC_VER)
11510 {
11511 /*
11512 Older versions of Visual Studio don't support the "safe" versions of _controlfp_s(). I don't
11513 know which version of Visual Studio first added support for _controlfp_s(), but I do know
11514 that VC6 lacks support. _MSC_VER = 1200 is VC6, but if you get compilation errors on older
11515 versions of Visual Studio, let me know and I'll make the necessary adjustment.
11516 */
11517 #if _MSC_VER <= 1200
11518 {
11519 prevState = _statusfp();
11520 _controlfp(prevState | _DN_FLUSH, _MCW_DN);
11521 }
11522 #else
11523 {
11524 unsigned int unused;
11525 _controlfp_s(&prevState, 0, 0);
11526 _controlfp_s(&unused, prevState | _DN_FLUSH, _MCW_DN);
11527 }
11528 #endif
11529 }
11530 #elif defined(MA_X86) || defined(MA_X64)
11531 {
11532 #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */
11533 {
11534 prevState = _mm_getcsr();
11535 _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK);
11536 }
11537 #else
11538 {
11539 /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */
11540 prevState = 0;
11541 }
11542 #endif
11543 }
11544 #else
11545 {
11546 /* Unknown or unsupported architecture. No-op. */
11547 prevState = 0;
11548 }
11549 #endif
11550
11551 return prevState;
11552}
11553
11554static MA_INLINE void ma_restore_denormals(unsigned int prevState)
11555{
11556 #if defined(_MSC_VER)
11557 {
11558 /* Older versions of Visual Studio do not support _controlfp_s(). See ma_disable_denormals(). */
11559 #if _MSC_VER <= 1200
11560 {
11561 _controlfp(prevState, _MCW_DN);
11562 }
11563 #else
11564 {
11565 unsigned int unused;
11566 _controlfp_s(&unused, prevState, _MCW_DN);
11567 }
11568 #endif
11569 }
11570 #elif defined(MA_X86) || defined(MA_X64)
11571 {
11572 #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */
11573 {
11574 _mm_setcsr(prevState);
11575 }
11576 #else
11577 {
11578 /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */
11579 (void)prevState;
11580 }
11581 #endif
11582 }
11583 #else
11584 {
11585 /* Unknown or unsupported architecture. No-op. */
11586 (void)prevState;
11587 }
11588 #endif
11589}
11590
11591
11592
11593#ifndef MA_COINIT_VALUE
11594#define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */
11595#endif
11596
11597
11598#ifndef MA_FLT_MAX
11599 #ifdef FLT_MAX
11600 #define MA_FLT_MAX FLT_MAX
11601 #else
11602 #define MA_FLT_MAX 3.402823466e+38F
11603 #endif
11604#endif
11605
11606
11607#ifndef MA_PI
11608#define MA_PI 3.14159265358979323846264f
11609#endif
11610#ifndef MA_PI_D
11611#define MA_PI_D 3.14159265358979323846264
11612#endif
11613#ifndef MA_TAU
11614#define MA_TAU 6.28318530717958647693f
11615#endif
11616#ifndef MA_TAU_D
11617#define MA_TAU_D 6.28318530717958647693
11618#endif
11619
11620
11621/* The default format when ma_format_unknown (0) is requested when initializing a device. */
11622#ifndef MA_DEFAULT_FORMAT
11623#define MA_DEFAULT_FORMAT ma_format_f32
11624#endif
11625
11626/* The default channel count to use when 0 is used when initializing a device. */
11627#ifndef MA_DEFAULT_CHANNELS
11628#define MA_DEFAULT_CHANNELS 2
11629#endif
11630
11631/* The default sample rate to use when 0 is used when initializing a device. */
11632#ifndef MA_DEFAULT_SAMPLE_RATE
11633#define MA_DEFAULT_SAMPLE_RATE 48000
11634#endif
11635
11636/* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */
11637#ifndef MA_DEFAULT_PERIODS
11638#define MA_DEFAULT_PERIODS 3
11639#endif
11640
11641/* The default period size in milliseconds for low latency mode. */
11642#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY
11643#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10
11644#endif
11645
11646/* The default buffer size in milliseconds for conservative mode. */
11647#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE
11648#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100
11649#endif
11650
11651/* The default LPF filter order for linear resampling. Note that this is clamped to MA_MAX_FILTER_ORDER. */
11652#ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER
11653 #if MA_MAX_FILTER_ORDER >= 4
11654 #define MA_DEFAULT_RESAMPLER_LPF_ORDER 4
11655 #else
11656 #define MA_DEFAULT_RESAMPLER_LPF_ORDER MA_MAX_FILTER_ORDER
11657 #endif
11658#endif
11659
11660
11661#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
11662 #pragma GCC diagnostic push
11663 #pragma GCC diagnostic ignored "-Wunused-variable"
11664#endif
11665
11666/* Standard sample rates, in order of priority. */
11667static ma_uint32 g_maStandardSampleRatePriorities[] = {
11670
11674
11679
11683
11686};
11687
11688static MA_INLINE ma_bool32 ma_is_standard_sample_rate(ma_uint32 sampleRate)
11689{
11690 ma_uint32 iSampleRate;
11691
11692 for (iSampleRate = 0; iSampleRate < sizeof(g_maStandardSampleRatePriorities) / sizeof(g_maStandardSampleRatePriorities[0]); iSampleRate += 1) {
11693 if (g_maStandardSampleRatePriorities[iSampleRate] == sampleRate) {
11694 return MA_TRUE;
11695 }
11696 }
11697
11698 /* Getting here means the sample rate is not supported. */
11699 return MA_FALSE;
11700}
11701
11702
11703static ma_format g_maFormatPriorities[] = {
11704 ma_format_s16, /* Most common */
11706
11707 /*ma_format_s24_32,*/ /* Clean alignment */
11709
11710 ma_format_s24, /* Unclean alignment */
11711
11712 ma_format_u8 /* Low quality */
11713};
11714#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
11715 #pragma GCC diagnostic pop
11716#endif
11717
11718
11719MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)
11720{
11721 if (pMajor) {
11722 *pMajor = MA_VERSION_MAJOR;
11723 }
11724
11725 if (pMinor) {
11726 *pMinor = MA_VERSION_MINOR;
11727 }
11728
11729 if (pRevision) {
11730 *pRevision = MA_VERSION_REVISION;
11731 }
11732}
11733
11734MA_API const char* ma_version_string(void)
11735{
11736 return MA_VERSION_STRING;
11737}
11738
11739
11740
11745#ifndef MA_MALLOC
11746#ifdef MA_WIN32
11747#define MA_MALLOC(sz) HeapAlloc(GetProcessHeap(), 0, (sz))
11748#else
11749#define MA_MALLOC(sz) malloc((sz))
11750#endif
11751#endif
11752
11753#ifndef MA_REALLOC
11754#ifdef MA_WIN32
11755#define MA_REALLOC(p, sz) (((sz) > 0) ? ((p) ? HeapReAlloc(GetProcessHeap(), 0, (p), (sz)) : HeapAlloc(GetProcessHeap(), 0, (sz))) : ((VOID*)(size_t)(HeapFree(GetProcessHeap(), 0, (p)) & 0)))
11756#else
11757#define MA_REALLOC(p, sz) realloc((p), (sz))
11758#endif
11759#endif
11760
11761#ifndef MA_FREE
11762#ifdef MA_WIN32
11763#define MA_FREE(p) HeapFree(GetProcessHeap(), 0, (p))
11764#else
11765#define MA_FREE(p) free((p))
11766#endif
11767#endif
11768
11769#ifndef MA_ZERO_MEMORY
11770#ifdef MA_WIN32
11771#define MA_ZERO_MEMORY(p, sz) ZeroMemory((p), (sz))
11772#else
11773#define MA_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
11774#endif
11775#endif
11776
11777#ifndef MA_COPY_MEMORY
11778#ifdef MA_WIN32
11779#define MA_COPY_MEMORY(dst, src, sz) CopyMemory((dst), (src), (sz))
11780#else
11781#define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
11782#endif
11783#endif
11784
11785#ifndef MA_MOVE_MEMORY
11786#ifdef MA_WIN32
11787#define MA_MOVE_MEMORY(dst, src, sz) MoveMemory((dst), (src), (sz))
11788#else
11789#define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz))
11790#endif
11791#endif
11792
11793#ifndef MA_ASSERT
11794#ifdef MA_WIN32
11795#define MA_ASSERT(condition) assert(condition)
11796#else
11797#define MA_ASSERT(condition) assert(condition)
11798#endif
11799#endif
11800
11801#define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p)))
11802
11803#define ma_countof(x) (sizeof(x) / sizeof(x[0]))
11804#define ma_max(x, y) (((x) > (y)) ? (x) : (y))
11805#define ma_min(x, y) (((x) < (y)) ? (x) : (y))
11806#define ma_abs(x) (((x) > 0) ? (x) : -(x))
11807#define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi)))
11808#define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset))
11809#define ma_align(x, a) ((x + (a-1)) & ~(a-1))
11810#define ma_align_64(x) ma_align(x, 8)
11811
11812#define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels))
11813
11814static MA_INLINE double ma_sind(double x)
11815{
11816 /* TODO: Implement custom sin(x). */
11817 return sin(x);
11818}
11819
11820static MA_INLINE double ma_expd(double x)
11821{
11822 /* TODO: Implement custom exp(x). */
11823 return exp(x);
11824}
11825
11826static MA_INLINE double ma_logd(double x)
11827{
11828 /* TODO: Implement custom log(x). */
11829 return log(x);
11830}
11831
11832static MA_INLINE double ma_powd(double x, double y)
11833{
11834 /* TODO: Implement custom pow(x, y). */
11835 return pow(x, y);
11836}
11837
11838static MA_INLINE double ma_sqrtd(double x)
11839{
11840 /* TODO: Implement custom sqrt(x). */
11841 return sqrt(x);
11842}
11843
11844
11845static MA_INLINE float ma_sinf(float x)
11846{
11847 return (float)ma_sind((float)x);
11848}
11849
11850static MA_INLINE double ma_cosd(double x)
11851{
11852 return ma_sind((MA_PI_D*0.5) - x);
11853}
11854
11855static MA_INLINE float ma_cosf(float x)
11856{
11857 return (float)ma_cosd((float)x);
11858}
11859
11860static MA_INLINE double ma_log10d(double x)
11861{
11862 return ma_logd(x) * 0.43429448190325182765;
11863}
11864
11865static MA_INLINE float ma_powf(float x, float y)
11866{
11867 return (float)ma_powd((double)x, (double)y);
11868}
11869
11870static MA_INLINE float ma_log10f(float x)
11871{
11872 return (float)ma_log10d((double)x);
11873}
11874
11875
11876static MA_INLINE double ma_degrees_to_radians(double degrees)
11877{
11878 return degrees * 0.01745329252;
11879}
11880
11881static MA_INLINE double ma_radians_to_degrees(double radians)
11882{
11883 return radians * 57.295779512896;
11884}
11885
11886static MA_INLINE float ma_degrees_to_radians_f(float degrees)
11887{
11888 return degrees * 0.01745329252f;
11889}
11890
11891static MA_INLINE float ma_radians_to_degrees_f(float radians)
11892{
11893 return radians * 57.295779512896f;
11894}
11895
11896
11897/*
11898Return Values:
11899 0: Success
11900 22: EINVAL
11901 34: ERANGE
11902
11903Not using symbolic constants for errors because I want to avoid #including errno.h
11904*/
11905MA_API int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
11906{
11907 size_t i;
11908
11909 if (dst == 0) {
11910 return 22;
11911 }
11912 if (dstSizeInBytes == 0) {
11913 return 34;
11914 }
11915 if (src == 0) {
11916 dst[0] = '\0';
11917 return 22;
11918 }
11919
11920 for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
11921 dst[i] = src[i];
11922 }
11923
11924 if (i < dstSizeInBytes) {
11925 dst[i] = '\0';
11926 return 0;
11927 }
11928
11929 dst[0] = '\0';
11930 return 34;
11931}
11932
11933MA_API int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src)
11934{
11935 size_t i;
11936
11937 if (dst == 0) {
11938 return 22;
11939 }
11940 if (dstCap == 0) {
11941 return 34;
11942 }
11943 if (src == 0) {
11944 dst[0] = '\0';
11945 return 22;
11946 }
11947
11948 for (i = 0; i < dstCap && src[i] != '\0'; ++i) {
11949 dst[i] = src[i];
11950 }
11951
11952 if (i < dstCap) {
11953 dst[i] = '\0';
11954 return 0;
11955 }
11956
11957 dst[0] = '\0';
11958 return 34;
11959}
11960
11961
11962MA_API int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
11963{
11964 size_t maxcount;
11965 size_t i;
11966
11967 if (dst == 0) {
11968 return 22;
11969 }
11970 if (dstSizeInBytes == 0) {
11971 return 34;
11972 }
11973 if (src == 0) {
11974 dst[0] = '\0';
11975 return 22;
11976 }
11977
11978 maxcount = count;
11979 if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */
11980 maxcount = dstSizeInBytes - 1;
11981 }
11982
11983 for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
11984 dst[i] = src[i];
11985 }
11986
11987 if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
11988 dst[i] = '\0';
11989 return 0;
11990 }
11991
11992 dst[0] = '\0';
11993 return 34;
11994}
11995
11996MA_API int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
11997{
11998 char* dstorig;
11999
12000 if (dst == 0) {
12001 return 22;
12002 }
12003 if (dstSizeInBytes == 0) {
12004 return 34;
12005 }
12006 if (src == 0) {
12007 dst[0] = '\0';
12008 return 22;
12009 }
12010
12011 dstorig = dst;
12012
12013 while (dstSizeInBytes > 0 && dst[0] != '\0') {
12014 dst += 1;
12015 dstSizeInBytes -= 1;
12016 }
12017
12018 if (dstSizeInBytes == 0) {
12019 return 22; /* Unterminated. */
12020 }
12021
12022
12023 while (dstSizeInBytes > 0 && src[0] != '\0') {
12024 *dst++ = *src++;
12025 dstSizeInBytes -= 1;
12026 }
12027
12028 if (dstSizeInBytes > 0) {
12029 dst[0] = '\0';
12030 } else {
12031 dstorig[0] = '\0';
12032 return 34;
12033 }
12034
12035 return 0;
12036}
12037
12038MA_API int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
12039{
12040 char* dstorig;
12041
12042 if (dst == 0) {
12043 return 22;
12044 }
12045 if (dstSizeInBytes == 0) {
12046 return 34;
12047 }
12048 if (src == 0) {
12049 return 22;
12050 }
12051
12052 dstorig = dst;
12053
12054 while (dstSizeInBytes > 0 && dst[0] != '\0') {
12055 dst += 1;
12056 dstSizeInBytes -= 1;
12057 }
12058
12059 if (dstSizeInBytes == 0) {
12060 return 22; /* Unterminated. */
12061 }
12062
12063
12064 if (count == ((size_t)-1)) { /* _TRUNCATE */
12065 count = dstSizeInBytes - 1;
12066 }
12067
12068 while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) {
12069 *dst++ = *src++;
12070 dstSizeInBytes -= 1;
12071 count -= 1;
12072 }
12073
12074 if (dstSizeInBytes > 0) {
12075 dst[0] = '\0';
12076 } else {
12077 dstorig[0] = '\0';
12078 return 34;
12079 }
12080
12081 return 0;
12082}
12083
12084MA_API int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix)
12085{
12086 int sign;
12087 unsigned int valueU;
12088 char* dstEnd;
12089
12090 if (dst == NULL || dstSizeInBytes == 0) {
12091 return 22;
12092 }
12093 if (radix < 2 || radix > 36) {
12094 dst[0] = '\0';
12095 return 22;
12096 }
12097
12098 sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */
12099
12100 if (value < 0) {
12101 valueU = -value;
12102 } else {
12103 valueU = value;
12104 }
12105
12106 dstEnd = dst;
12107 do
12108 {
12109 int remainder = valueU % radix;
12110 if (remainder > 9) {
12111 *dstEnd = (char)((remainder - 10) + 'a');
12112 } else {
12113 *dstEnd = (char)(remainder + '0');
12114 }
12115
12116 dstEnd += 1;
12117 dstSizeInBytes -= 1;
12118 valueU /= radix;
12119 } while (dstSizeInBytes > 0 && valueU > 0);
12120
12121 if (dstSizeInBytes == 0) {
12122 dst[0] = '\0';
12123 return 22; /* Ran out of room in the output buffer. */
12124 }
12125
12126 if (sign < 0) {
12127 *dstEnd++ = '-';
12128 dstSizeInBytes -= 1;
12129 }
12130
12131 if (dstSizeInBytes == 0) {
12132 dst[0] = '\0';
12133 return 22; /* Ran out of room in the output buffer. */
12134 }
12135
12136 *dstEnd = '\0';
12137
12138
12139 /* At this point the string will be reversed. */
12140 dstEnd -= 1;
12141 while (dst < dstEnd) {
12142 char temp = *dst;
12143 *dst = *dstEnd;
12144 *dstEnd = temp;
12145
12146 dst += 1;
12147 dstEnd -= 1;
12148 }
12149
12150 return 0;
12151}
12152
12153MA_API int ma_strcmp(const char* str1, const char* str2)
12154{
12155 if (str1 == str2) return 0;
12156
12157 /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */
12158 if (str1 == NULL) return -1;
12159 if (str2 == NULL) return 1;
12160
12161 for (;;) {
12162 if (str1[0] == '\0') {
12163 break;
12164 }
12165 if (str1[0] != str2[0]) {
12166 break;
12167 }
12168
12169 str1 += 1;
12170 str2 += 1;
12171 }
12172
12173 return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0];
12174}
12175
12176MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB)
12177{
12178 int result;
12179
12180 result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1);
12181 if (result != 0) {
12182 return result;
12183 }
12184
12185 result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1);
12186 if (result != 0) {
12187 return result;
12188 }
12189
12190 return result;
12191}
12192
12193MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks)
12194{
12195 if (src == NULL) {
12196 return NULL;
12197 }
12198
12199 size_t sz = strlen(src)+1;
12200 char* dst = (char*)ma_malloc(sz, pAllocationCallbacks);
12201 if (dst == NULL) {
12202 return NULL;
12203 }
12204
12205 ma_strcpy_s(dst, sz, src);
12206
12207 return dst;
12208}
12209
12210MA_API wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks)
12211{
12212 size_t sz = wcslen(src)+1;
12213 wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks);
12214 if (dst == NULL) {
12215 return NULL;
12216 }
12217
12218 ma_wcscpy_s(dst, sz, src);
12219
12220 return dst;
12221}
12222
12223
12224#include <errno.h>
12225static ma_result ma_result_from_errno(int e)
12226{
12227 switch (e)
12228 {
12229 case 0: return MA_SUCCESS;
12230 #ifdef EPERM
12231 case EPERM: return MA_INVALID_OPERATION;
12232 #endif
12233 #ifdef ENOENT
12234 case ENOENT: return MA_DOES_NOT_EXIST;
12235 #endif
12236 #ifdef ESRCH
12237 case ESRCH: return MA_DOES_NOT_EXIST;
12238 #endif
12239 #ifdef EINTR
12240 case EINTR: return MA_INTERRUPT;
12241 #endif
12242 #ifdef EIO
12243 case EIO: return MA_IO_ERROR;
12244 #endif
12245 #ifdef ENXIO
12246 case ENXIO: return MA_DOES_NOT_EXIST;
12247 #endif
12248 #ifdef E2BIG
12249 case E2BIG: return MA_INVALID_ARGS;
12250 #endif
12251 #ifdef ENOEXEC
12252 case ENOEXEC: return MA_INVALID_FILE;
12253 #endif
12254 #ifdef EBADF
12255 case EBADF: return MA_INVALID_FILE;
12256 #endif
12257 #ifdef ECHILD
12258 case ECHILD: return MA_ERROR;
12259 #endif
12260 #ifdef EAGAIN
12261 case EAGAIN: return MA_UNAVAILABLE;
12262 #endif
12263 #ifdef ENOMEM
12264 case ENOMEM: return MA_OUT_OF_MEMORY;
12265 #endif
12266 #ifdef EACCES
12267 case EACCES: return MA_ACCESS_DENIED;
12268 #endif
12269 #ifdef EFAULT
12270 case EFAULT: return MA_BAD_ADDRESS;
12271 #endif
12272 #ifdef ENOTBLK
12273 case ENOTBLK: return MA_ERROR;
12274 #endif
12275 #ifdef EBUSY
12276 case EBUSY: return MA_BUSY;
12277 #endif
12278 #ifdef EEXIST
12279 case EEXIST: return MA_ALREADY_EXISTS;
12280 #endif
12281 #ifdef EXDEV
12282 case EXDEV: return MA_ERROR;
12283 #endif
12284 #ifdef ENODEV
12285 case ENODEV: return MA_DOES_NOT_EXIST;
12286 #endif
12287 #ifdef ENOTDIR
12288 case ENOTDIR: return MA_NOT_DIRECTORY;
12289 #endif
12290 #ifdef EISDIR
12291 case EISDIR: return MA_IS_DIRECTORY;
12292 #endif
12293 #ifdef EINVAL
12294 case EINVAL: return MA_INVALID_ARGS;
12295 #endif
12296 #ifdef ENFILE
12297 case ENFILE: return MA_TOO_MANY_OPEN_FILES;
12298 #endif
12299 #ifdef EMFILE
12300 case EMFILE: return MA_TOO_MANY_OPEN_FILES;
12301 #endif
12302 #ifdef ENOTTY
12303 case ENOTTY: return MA_INVALID_OPERATION;
12304 #endif
12305 #ifdef ETXTBSY
12306 case ETXTBSY: return MA_BUSY;
12307 #endif
12308 #ifdef EFBIG
12309 case EFBIG: return MA_TOO_BIG;
12310 #endif
12311 #ifdef ENOSPC
12312 case ENOSPC: return MA_NO_SPACE;
12313 #endif
12314 #ifdef ESPIPE
12315 case ESPIPE: return MA_BAD_SEEK;
12316 #endif
12317 #ifdef EROFS
12318 case EROFS: return MA_ACCESS_DENIED;
12319 #endif
12320 #ifdef EMLINK
12321 case EMLINK: return MA_TOO_MANY_LINKS;
12322 #endif
12323 #ifdef EPIPE
12324 case EPIPE: return MA_BAD_PIPE;
12325 #endif
12326 #ifdef EDOM
12327 case EDOM: return MA_OUT_OF_RANGE;
12328 #endif
12329 #ifdef ERANGE
12330 case ERANGE: return MA_OUT_OF_RANGE;
12331 #endif
12332 #ifdef EDEADLK
12333 case EDEADLK: return MA_DEADLOCK;
12334 #endif
12335 #ifdef ENAMETOOLONG
12336 case ENAMETOOLONG: return MA_PATH_TOO_LONG;
12337 #endif
12338 #ifdef ENOLCK
12339 case ENOLCK: return MA_ERROR;
12340 #endif
12341 #ifdef ENOSYS
12342 case ENOSYS: return MA_NOT_IMPLEMENTED;
12343 #endif
12344 #ifdef ENOTEMPTY
12345 case ENOTEMPTY: return MA_DIRECTORY_NOT_EMPTY;
12346 #endif
12347 #ifdef ELOOP
12348 case ELOOP: return MA_TOO_MANY_LINKS;
12349 #endif
12350 #ifdef ENOMSG
12351 case ENOMSG: return MA_NO_MESSAGE;
12352 #endif
12353 #ifdef EIDRM
12354 case EIDRM: return MA_ERROR;
12355 #endif
12356 #ifdef ECHRNG
12357 case ECHRNG: return MA_ERROR;
12358 #endif
12359 #ifdef EL2NSYNC
12360 case EL2NSYNC: return MA_ERROR;
12361 #endif
12362 #ifdef EL3HLT
12363 case EL3HLT: return MA_ERROR;
12364 #endif
12365 #ifdef EL3RST
12366 case EL3RST: return MA_ERROR;
12367 #endif
12368 #ifdef ELNRNG
12369 case ELNRNG: return MA_OUT_OF_RANGE;
12370 #endif
12371 #ifdef EUNATCH
12372 case EUNATCH: return MA_ERROR;
12373 #endif
12374 #ifdef ENOCSI
12375 case ENOCSI: return MA_ERROR;
12376 #endif
12377 #ifdef EL2HLT
12378 case EL2HLT: return MA_ERROR;
12379 #endif
12380 #ifdef EBADE
12381 case EBADE: return MA_ERROR;
12382 #endif
12383 #ifdef EBADR
12384 case EBADR: return MA_ERROR;
12385 #endif
12386 #ifdef EXFULL
12387 case EXFULL: return MA_ERROR;
12388 #endif
12389 #ifdef ENOANO
12390 case ENOANO: return MA_ERROR;
12391 #endif
12392 #ifdef EBADRQC
12393 case EBADRQC: return MA_ERROR;
12394 #endif
12395 #ifdef EBADSLT
12396 case EBADSLT: return MA_ERROR;
12397 #endif
12398 #ifdef EBFONT
12399 case EBFONT: return MA_INVALID_FILE;
12400 #endif
12401 #ifdef ENOSTR
12402 case ENOSTR: return MA_ERROR;
12403 #endif
12404 #ifdef ENODATA
12405 case ENODATA: return MA_NO_DATA_AVAILABLE;
12406 #endif
12407 #ifdef ETIME
12408 case ETIME: return MA_TIMEOUT;
12409 #endif
12410 #ifdef ENOSR
12411 case ENOSR: return MA_NO_DATA_AVAILABLE;
12412 #endif
12413 #ifdef ENONET
12414 case ENONET: return MA_NO_NETWORK;
12415 #endif
12416 #ifdef ENOPKG
12417 case ENOPKG: return MA_ERROR;
12418 #endif
12419 #ifdef EREMOTE
12420 case EREMOTE: return MA_ERROR;
12421 #endif
12422 #ifdef ENOLINK
12423 case ENOLINK: return MA_ERROR;
12424 #endif
12425 #ifdef EADV
12426 case EADV: return MA_ERROR;
12427 #endif
12428 #ifdef ESRMNT
12429 case ESRMNT: return MA_ERROR;
12430 #endif
12431 #ifdef ECOMM
12432 case ECOMM: return MA_ERROR;
12433 #endif
12434 #ifdef EPROTO
12435 case EPROTO: return MA_ERROR;
12436 #endif
12437 #ifdef EMULTIHOP
12438 case EMULTIHOP: return MA_ERROR;
12439 #endif
12440 #ifdef EDOTDOT
12441 case EDOTDOT: return MA_ERROR;
12442 #endif
12443 #ifdef EBADMSG
12444 case EBADMSG: return MA_BAD_MESSAGE;
12445 #endif
12446 #ifdef EOVERFLOW
12447 case EOVERFLOW: return MA_TOO_BIG;
12448 #endif
12449 #ifdef ENOTUNIQ
12450 case ENOTUNIQ: return MA_NOT_UNIQUE;
12451 #endif
12452 #ifdef EBADFD
12453 case EBADFD: return MA_ERROR;
12454 #endif
12455 #ifdef EREMCHG
12456 case EREMCHG: return MA_ERROR;
12457 #endif
12458 #ifdef ELIBACC
12459 case ELIBACC: return MA_ACCESS_DENIED;
12460 #endif
12461 #ifdef ELIBBAD
12462 case ELIBBAD: return MA_INVALID_FILE;
12463 #endif
12464 #ifdef ELIBSCN
12465 case ELIBSCN: return MA_INVALID_FILE;
12466 #endif
12467 #ifdef ELIBMAX
12468 case ELIBMAX: return MA_ERROR;
12469 #endif
12470 #ifdef ELIBEXEC
12471 case ELIBEXEC: return MA_ERROR;
12472 #endif
12473 #ifdef EILSEQ
12474 case EILSEQ: return MA_INVALID_DATA;
12475 #endif
12476 #ifdef ERESTART
12477 case ERESTART: return MA_ERROR;
12478 #endif
12479 #ifdef ESTRPIPE
12480 case ESTRPIPE: return MA_ERROR;
12481 #endif
12482 #ifdef EUSERS
12483 case EUSERS: return MA_ERROR;
12484 #endif
12485 #ifdef ENOTSOCK
12486 case ENOTSOCK: return MA_NOT_SOCKET;
12487 #endif
12488 #ifdef EDESTADDRREQ
12489 case EDESTADDRREQ: return MA_NO_ADDRESS;
12490 #endif
12491 #ifdef EMSGSIZE
12492 case EMSGSIZE: return MA_TOO_BIG;
12493 #endif
12494 #ifdef EPROTOTYPE
12495 case EPROTOTYPE: return MA_BAD_PROTOCOL;
12496 #endif
12497 #ifdef ENOPROTOOPT
12498 case ENOPROTOOPT: return MA_PROTOCOL_UNAVAILABLE;
12499 #endif
12500 #ifdef EPROTONOSUPPORT
12501 case EPROTONOSUPPORT: return MA_PROTOCOL_NOT_SUPPORTED;
12502 #endif
12503 #ifdef ESOCKTNOSUPPORT
12504 case ESOCKTNOSUPPORT: return MA_SOCKET_NOT_SUPPORTED;
12505 #endif
12506 #ifdef EOPNOTSUPP
12507 case EOPNOTSUPP: return MA_INVALID_OPERATION;
12508 #endif
12509 #ifdef EPFNOSUPPORT
12510 case EPFNOSUPPORT: return MA_PROTOCOL_FAMILY_NOT_SUPPORTED;
12511 #endif
12512 #ifdef EAFNOSUPPORT
12513 case EAFNOSUPPORT: return MA_ADDRESS_FAMILY_NOT_SUPPORTED;
12514 #endif
12515 #ifdef EADDRINUSE
12516 case EADDRINUSE: return MA_ALREADY_IN_USE;
12517 #endif
12518 #ifdef EADDRNOTAVAIL
12519 case EADDRNOTAVAIL: return MA_ERROR;
12520 #endif
12521 #ifdef ENETDOWN
12522 case ENETDOWN: return MA_NO_NETWORK;
12523 #endif
12524 #ifdef ENETUNREACH
12525 case ENETUNREACH: return MA_NO_NETWORK;
12526 #endif
12527 #ifdef ENETRESET
12528 case ENETRESET: return MA_NO_NETWORK;
12529 #endif
12530 #ifdef ECONNABORTED
12531 case ECONNABORTED: return MA_NO_NETWORK;
12532 #endif
12533 #ifdef ECONNRESET
12534 case ECONNRESET: return MA_CONNECTION_RESET;
12535 #endif
12536 #ifdef ENOBUFS
12537 case ENOBUFS: return MA_NO_SPACE;
12538 #endif
12539 #ifdef EISCONN
12540 case EISCONN: return MA_ALREADY_CONNECTED;
12541 #endif
12542 #ifdef ENOTCONN
12543 case ENOTCONN: return MA_NOT_CONNECTED;
12544 #endif
12545 #ifdef ESHUTDOWN
12546 case ESHUTDOWN: return MA_ERROR;
12547 #endif
12548 #ifdef ETOOMANYREFS
12549 case ETOOMANYREFS: return MA_ERROR;
12550 #endif
12551 #ifdef ETIMEDOUT
12552 case ETIMEDOUT: return MA_TIMEOUT;
12553 #endif
12554 #ifdef ECONNREFUSED
12555 case ECONNREFUSED: return MA_CONNECTION_REFUSED;
12556 #endif
12557 #ifdef EHOSTDOWN
12558 case EHOSTDOWN: return MA_NO_HOST;
12559 #endif
12560 #ifdef EHOSTUNREACH
12561 case EHOSTUNREACH: return MA_NO_HOST;
12562 #endif
12563 #ifdef EALREADY
12564 case EALREADY: return MA_IN_PROGRESS;
12565 #endif
12566 #ifdef EINPROGRESS
12567 case EINPROGRESS: return MA_IN_PROGRESS;
12568 #endif
12569 #ifdef ESTALE
12570 case ESTALE: return MA_INVALID_FILE;
12571 #endif
12572 #ifdef EUCLEAN
12573 case EUCLEAN: return MA_ERROR;
12574 #endif
12575 #ifdef ENOTNAM
12576 case ENOTNAM: return MA_ERROR;
12577 #endif
12578 #ifdef ENAVAIL
12579 case ENAVAIL: return MA_ERROR;
12580 #endif
12581 #ifdef EISNAM
12582 case EISNAM: return MA_ERROR;
12583 #endif
12584 #ifdef EREMOTEIO
12585 case EREMOTEIO: return MA_IO_ERROR;
12586 #endif
12587 #ifdef EDQUOT
12588 case EDQUOT: return MA_NO_SPACE;
12589 #endif
12590 #ifdef ENOMEDIUM
12591 case ENOMEDIUM: return MA_DOES_NOT_EXIST;
12592 #endif
12593 #ifdef EMEDIUMTYPE
12594 case EMEDIUMTYPE: return MA_ERROR;
12595 #endif
12596 #ifdef ECANCELED
12597 case ECANCELED: return MA_CANCELLED;
12598 #endif
12599 #ifdef ENOKEY
12600 case ENOKEY: return MA_ERROR;
12601 #endif
12602 #ifdef EKEYEXPIRED
12603 case EKEYEXPIRED: return MA_ERROR;
12604 #endif
12605 #ifdef EKEYREVOKED
12606 case EKEYREVOKED: return MA_ERROR;
12607 #endif
12608 #ifdef EKEYREJECTED
12609 case EKEYREJECTED: return MA_ERROR;
12610 #endif
12611 #ifdef EOWNERDEAD
12612 case EOWNERDEAD: return MA_ERROR;
12613 #endif
12614 #ifdef ENOTRECOVERABLE
12615 case ENOTRECOVERABLE: return MA_ERROR;
12616 #endif
12617 #ifdef ERFKILL
12618 case ERFKILL: return MA_ERROR;
12619 #endif
12620 #ifdef EHWPOISON
12621 case EHWPOISON: return MA_ERROR;
12622 #endif
12623 default: return MA_ERROR;
12624 }
12625}
12626
12627MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
12628{
12629#if defined(_MSC_VER) && _MSC_VER >= 1400
12630 errno_t err;
12631#endif
12632
12633 if (ppFile != NULL) {
12634 *ppFile = NULL; /* Safety. */
12635 }
12636
12637 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
12638 return MA_INVALID_ARGS;
12639 }
12640
12641#if defined(_MSC_VER) && _MSC_VER >= 1400
12642 err = fopen_s(ppFile, pFilePath, pOpenMode);
12643 if (err != 0) {
12644 return ma_result_from_errno(err);
12645 }
12646#else
12647#if defined(_WIN32) || defined(__APPLE__)
12648 *ppFile = fopen(pFilePath, pOpenMode);
12649#else
12650 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
12651 *ppFile = fopen64(pFilePath, pOpenMode);
12652 #else
12653 *ppFile = fopen(pFilePath, pOpenMode);
12654 #endif
12655#endif
12656 if (*ppFile == NULL) {
12657 ma_result result = ma_result_from_errno(errno);
12658 if (result == MA_SUCCESS) {
12659 result = MA_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */
12660 }
12661
12662 return result;
12663 }
12664#endif
12665
12666 return MA_SUCCESS;
12667}
12668
12669
12670
12671/*
12672_wfopen() isn't always available in all compilation environments.
12673
12674 * Windows only.
12675 * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back).
12676 * MinGW-64 (both 32- and 64-bit) seems to support it.
12677 * MinGW wraps it in !defined(__STRICT_ANSI__).
12678 * OpenWatcom wraps it in !defined(_NO_EXT_KEYS).
12679
12680This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs()
12681fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support.
12682*/
12683#if defined(_WIN32)
12684 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
12685 #define MA_HAS_WFOPEN
12686 #endif
12687#endif
12688
12689MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks)
12690{
12691 if (ppFile != NULL) {
12692 *ppFile = NULL; /* Safety. */
12693 }
12694
12695 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
12696 return MA_INVALID_ARGS;
12697 }
12698
12699#if defined(MA_HAS_WFOPEN)
12700 {
12701 /* Use _wfopen() on Windows. */
12702 #if defined(_MSC_VER) && _MSC_VER >= 1400
12703 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
12704 if (err != 0) {
12705 return ma_result_from_errno(err);
12706 }
12707 #else
12708 *ppFile = _wfopen(pFilePath, pOpenMode);
12709 if (*ppFile == NULL) {
12710 return ma_result_from_errno(errno);
12711 }
12712 #endif
12713 (void)pAllocationCallbacks;
12714 }
12715#else
12716 /*
12717 Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can
12718 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
12719 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.
12720 */
12721 {
12722 mbstate_t mbs;
12723 size_t lenMB;
12724 const wchar_t* pFilePathTemp = pFilePath;
12725 char* pFilePathMB = NULL;
12726 char pOpenModeMB[32] = {0};
12727
12728 /* Get the length first. */
12729 MA_ZERO_OBJECT(&mbs);
12730 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
12731 if (lenMB == (size_t)-1) {
12732 return ma_result_from_errno(errno);
12733 }
12734
12735 pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks);
12736 if (pFilePathMB == NULL) {
12737 return MA_OUT_OF_MEMORY;
12738 }
12739
12740 pFilePathTemp = pFilePath;
12741 MA_ZERO_OBJECT(&mbs);
12742 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
12743
12744 /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */
12745 {
12746 size_t i = 0;
12747 for (;;) {
12748 if (pOpenMode[i] == 0) {
12749 pOpenModeMB[i] = '\0';
12750 break;
12751 }
12752
12753 pOpenModeMB[i] = (char)pOpenMode[i];
12754 i += 1;
12755 }
12756 }
12757
12758 *ppFile = fopen(pFilePathMB, pOpenModeMB);
12759
12760 ma_free(pFilePathMB, pAllocationCallbacks);
12761 }
12762
12763 if (*ppFile == NULL) {
12764 return MA_ERROR;
12765 }
12766#endif
12767
12768 return MA_SUCCESS;
12769}
12770
12771
12772
12773static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes)
12774{
12775#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
12776 MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes);
12777#else
12778 while (sizeInBytes > 0) {
12779 ma_uint64 bytesToCopyNow = sizeInBytes;
12780 if (bytesToCopyNow > MA_SIZE_MAX) {
12781 bytesToCopyNow = MA_SIZE_MAX;
12782 }
12783
12784 MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */
12785
12786 sizeInBytes -= bytesToCopyNow;
12787 dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow);
12788 src = (const void*)((const ma_uint8*)src + bytesToCopyNow);
12789 }
12790#endif
12791}
12792
12793static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes)
12794{
12795#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
12796 MA_ZERO_MEMORY(dst, (size_t)sizeInBytes);
12797#else
12798 while (sizeInBytes > 0) {
12799 ma_uint64 bytesToZeroNow = sizeInBytes;
12800 if (bytesToZeroNow > MA_SIZE_MAX) {
12801 bytesToZeroNow = MA_SIZE_MAX;
12802 }
12803
12804 MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */
12805
12806 sizeInBytes -= bytesToZeroNow;
12807 dst = (void*)((ma_uint8*)dst + bytesToZeroNow);
12808 }
12809#endif
12810}
12811
12812
12813/* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */
12814static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x)
12815{
12816 x--;
12817 x |= x >> 1;
12818 x |= x >> 2;
12819 x |= x >> 4;
12820 x |= x >> 8;
12821 x |= x >> 16;
12822 x++;
12823
12824 return x;
12825}
12826
12827static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x)
12828{
12829 return ma_next_power_of_2(x) >> 1;
12830}
12831
12832static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x)
12833{
12834 unsigned int prev = ma_prev_power_of_2(x);
12835 unsigned int next = ma_next_power_of_2(x);
12836 if ((next - x) > (x - prev)) {
12837 return prev;
12838 } else {
12839 return next;
12840 }
12841}
12842
12843static MA_INLINE unsigned int ma_count_set_bits(unsigned int x)
12844{
12845 unsigned int count = 0;
12846 while (x != 0) {
12847 if (x & 1) {
12848 count += 1;
12849 }
12850
12851 x = x >> 1;
12852 }
12853
12854 return count;
12855}
12856
12857
12858
12859
12864static void* ma__malloc_default(size_t sz, void* pUserData)
12865{
12866 (void)pUserData;
12867 return MA_MALLOC(sz);
12868}
12869
12870static void* ma__realloc_default(void* p, size_t sz, void* pUserData)
12871{
12872 (void)pUserData;
12873 return MA_REALLOC(p, sz);
12874}
12875
12876static void ma__free_default(void* p, void* pUserData)
12877{
12878 (void)pUserData;
12879 MA_FREE(p);
12880}
12881
12882static ma_allocation_callbacks ma_allocation_callbacks_init_default(void)
12883{
12884 ma_allocation_callbacks callbacks;
12885 callbacks.pUserData = NULL;
12886 callbacks.onMalloc = ma__malloc_default;
12887 callbacks.onRealloc = ma__realloc_default;
12888 callbacks.onFree = ma__free_default;
12889
12890 return callbacks;
12891}
12892
12893static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc)
12894{
12895 if (pDst == NULL) {
12896 return MA_INVALID_ARGS;
12897 }
12898
12899 if (pSrc == NULL) {
12900 *pDst = ma_allocation_callbacks_init_default();
12901 } else {
12902 if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) {
12903 *pDst = ma_allocation_callbacks_init_default();
12904 } else {
12905 if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) {
12906 return MA_INVALID_ARGS; /* Invalid allocation callbacks. */
12907 } else {
12908 *pDst = *pSrc;
12909 }
12910 }
12911 }
12912
12913 return MA_SUCCESS;
12914}
12915
12916
12917
12918
12919
12924MA_API const char* ma_log_level_to_string(ma_uint32 logLevel)
12925{
12926 switch (logLevel)
12927 {
12928 case MA_LOG_LEVEL_DEBUG: return "DEBUG";
12929 case MA_LOG_LEVEL_INFO: return "INFO";
12930 case MA_LOG_LEVEL_WARNING: return "WARNING";
12931 case MA_LOG_LEVEL_ERROR: return "ERROR";
12932 default: return "ERROR";
12933 }
12934}
12935
12936#if defined(MA_DEBUG_OUTPUT)
12937
12938/* Customize this to use a specific tag in __android_log_print() for debug output messages. */
12939#ifndef MA_ANDROID_LOG_TAG
12940#define MA_ANDROID_LOG_TAG "miniaudio"
12941#endif
12942
12943void ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage)
12944{
12945 (void)pUserData;
12946
12947 /* Special handling for some platforms. */
12948 #if defined(MA_ANDROID)
12949 {
12950 /* Android. */
12951 __android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, "%s: %s", ma_log_level_to_string(level), pMessage);
12952 }
12953 #else
12954 {
12955 /* Everything else. */
12956 printf("%s: %s", ma_log_level_to_string(level), pMessage);
12957 }
12958 #endif
12959}
12960#endif
12961
12963{
12964 ma_log_callback callback;
12965
12966 MA_ZERO_OBJECT(&callback);
12967 callback.onLog = onLog;
12968 callback.pUserData = pUserData;
12969
12970 return callback;
12971}
12972
12973
12974MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog)
12975{
12976 if (pLog == NULL) {
12977 return MA_INVALID_ARGS;
12978 }
12979
12980 MA_ZERO_OBJECT(pLog);
12981 ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks);
12982
12983 /* We need a mutex for thread safety. */
12984 #ifndef MA_NO_THREADING
12985 {
12986 ma_result result = ma_mutex_init(&pLog->lock);
12987 if (result != MA_SUCCESS) {
12988 return result;
12989 }
12990 }
12991 #endif
12992
12993 /* If we're using debug output, enable it. */
12994 #if defined(MA_DEBUG_OUTPUT)
12995 {
12996 ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL)); /* Doesn't really matter if this fails. */
12997 }
12998 #endif
12999
13000 return MA_SUCCESS;
13001}
13002
13003MA_API void ma_log_uninit(ma_log* pLog)
13004{
13005 if (pLog == NULL) {
13006 return;
13007 }
13008
13009#ifndef MA_NO_THREADING
13010 ma_mutex_uninit(&pLog->lock);
13011#endif
13012}
13013
13014static void ma_log_lock(ma_log* pLog)
13015{
13016#ifndef MA_NO_THREADING
13017 ma_mutex_lock(&pLog->lock);
13018#else
13019 (void)pLog;
13020#endif
13021}
13022
13023static void ma_log_unlock(ma_log* pLog)
13024{
13025#ifndef MA_NO_THREADING
13026 ma_mutex_unlock(&pLog->lock);
13027#else
13028 (void)pLog;
13029#endif
13030}
13031
13033{
13034 ma_result result = MA_SUCCESS;
13035
13036 if (pLog == NULL || callback.onLog == NULL) {
13037 return MA_INVALID_ARGS;
13038 }
13039
13040 ma_log_lock(pLog);
13041 {
13042 if (pLog->callbackCount == ma_countof(pLog->callbacks)) {
13043 result = MA_OUT_OF_MEMORY; /* Reached the maximum allowed log callbacks. */
13044 } else {
13045 pLog->callbacks[pLog->callbackCount] = callback;
13046 pLog->callbackCount += 1;
13047 }
13048 }
13049 ma_log_unlock(pLog);
13050
13051 return result;
13052}
13053
13055{
13056 if (pLog == NULL) {
13057 return MA_INVALID_ARGS;
13058 }
13059
13060 ma_log_lock(pLog);
13061 {
13062 ma_uint32 iLog;
13063 for (iLog = 0; iLog < pLog->callbackCount; ) {
13064 if (pLog->callbacks[iLog].onLog == callback.onLog) {
13065 /* Found. Move everything down a slot. */
13066 ma_uint32 jLog;
13067 for (jLog = iLog; jLog < pLog->callbackCount-1; jLog += 1) {
13068 pLog->callbacks[jLog] = pLog->callbacks[jLog + 1];
13069 }
13070
13071 pLog->callbackCount -= 1;
13072 } else {
13073 /* Not found. */
13074 iLog += 1;
13075 }
13076 }
13077 }
13078 ma_log_unlock(pLog);
13079
13080 return MA_SUCCESS;
13081}
13082
13083MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage)
13084{
13085 if (pLog == NULL || pMessage == NULL) {
13086 return MA_INVALID_ARGS;
13087 }
13088
13089 ma_log_lock(pLog);
13090 {
13091 ma_uint32 iLog;
13092 for (iLog = 0; iLog < pLog->callbackCount; iLog += 1) {
13093 if (pLog->callbacks[iLog].onLog) {
13094 pLog->callbacks[iLog].onLog(pLog->callbacks[iLog].pUserData, level, pMessage);
13095 }
13096 }
13097 }
13098 ma_log_unlock(pLog);
13099
13100 return MA_SUCCESS;
13101}
13102
13103
13104/*
13105We need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a
13106logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf().
13107*/
13108#if defined(_MSC_VER) && _MSC_VER < 1900
13109static int ma_vscprintf(const ma_allocation_callbacks* pAllocationCallbacks, const char* format, va_list args)
13110{
13111#if _MSC_VER > 1200
13112 return _vscprintf(format, args);
13113#else
13114 int result;
13115 char* pTempBuffer = NULL;
13116 size_t tempBufferCap = 1024;
13117
13118 if (format == NULL) {
13119 errno = EINVAL;
13120 return -1;
13121 }
13122
13123 for (;;) {
13124 char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, pAllocationCallbacks);
13125 if (pNewTempBuffer == NULL) {
13126 ma_free(pTempBuffer, pAllocationCallbacks);
13127 errno = ENOMEM;
13128 return -1; /* Out of memory. */
13129 }
13130
13131 pTempBuffer = pNewTempBuffer;
13132
13133 result = _vsnprintf(pTempBuffer, tempBufferCap, format, args);
13134 ma_free(pTempBuffer, NULL);
13135
13136 if (result != -1) {
13137 break; /* Got it. */
13138 }
13139
13140 /* 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. */
13141 tempBufferCap *= 2;
13142 }
13143
13144 return result;
13145#endif
13146}
13147#endif
13148
13149MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args)
13150{
13151 if (pLog == NULL || pFormat == NULL) {
13152 return MA_INVALID_ARGS;
13153 }
13154
13155 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
13156 {
13157 ma_result result;
13158 int length;
13159 char pFormattedMessageStack[1024];
13160 char* pFormattedMessageHeap = NULL;
13161
13162 /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */
13163 length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args);
13164 if (length < 0) {
13165 return MA_INVALID_OPERATION; /* An error occured when trying to convert the buffer. */
13166 }
13167
13168 if ((size_t)length < sizeof(pFormattedMessageStack)) {
13169 /* The string was written to the stack. */
13170 result = ma_log_post(pLog, level, pFormattedMessageStack);
13171 } else {
13172 /* The stack buffer was too small, try the heap. */
13173 pFormattedMessageHeap = (char*)ma_malloc(length + 1, &pLog->allocationCallbacks);
13174 if (pFormattedMessageHeap == NULL) {
13175 return MA_OUT_OF_MEMORY;
13176 }
13177
13178 length = vsnprintf(pFormattedMessageHeap, length + 1, pFormat, args);
13179 if (length < 0) {
13180 ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks);
13181 return MA_INVALID_OPERATION;
13182 }
13183
13184 result = ma_log_post(pLog, level, pFormattedMessageHeap);
13185 ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks);
13186 }
13187
13188 return result;
13189 }
13190 #else
13191 {
13192 /*
13193 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
13194 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
13195 a fixed sized stack allocated buffer.
13196 */
13197 #if defined(_MSC_VER) && _MSC_VER >= 1200 /* 1200 = VC6 */
13198 {
13199 ma_result result;
13200 int formattedLen;
13201 char* pFormattedMessage = NULL;
13202 va_list args2;
13203
13204 #if _MSC_VER >= 1800
13205 {
13206 va_copy(args2, args);
13207 }
13208 #else
13209 {
13210 args2 = args;
13211 }
13212 #endif
13213
13214 formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2);
13215 va_end(args2);
13216
13217 if (formattedLen <= 0) {
13218 return MA_INVALID_OPERATION;
13219 }
13220
13221 pFormattedMessage = (char*)ma_malloc(formattedLen + 1, &pLog->allocationCallbacks);
13222 if (pFormattedMessage == NULL) {
13223 return MA_OUT_OF_MEMORY;
13224 }
13225
13226 /* We'll get errors on newer versions of Visual Studio if we try to use vsprintf(). */
13227 #if _MSC_VER >= 1400 /* 1400 = Visual Studio 2005 */
13228 {
13229 vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args);
13230 }
13231 #else
13232 {
13233 vsprintf(pFormattedMessage, pFormat, args);
13234 }
13235 #endif
13236
13237 result = ma_log_post(pLog, level, pFormattedMessage);
13238 ma_free(pFormattedMessage, &pLog->allocationCallbacks);
13239
13240 return result;
13241 }
13242 #else
13243 {
13244 /* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */
13245 (void)level;
13246 (void)args;
13247
13248 return MA_INVALID_OPERATION;
13249 }
13250 #endif
13251 }
13252 #endif
13253}
13254
13255MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...)
13256{
13257 ma_result result;
13258 va_list args;
13259
13260 if (pLog == NULL || pFormat == NULL) {
13261 return MA_INVALID_ARGS;
13262 }
13263
13264 va_start(args, pFormat);
13265 {
13266 result = ma_log_postv(pLog, level, pFormat, args);
13267 }
13268 va_end(args);
13269
13270 return result;
13271}
13272
13273
13274
13275static MA_INLINE ma_uint8 ma_clip_u8(ma_int32 x)
13276{
13277 return (ma_uint8)(ma_clamp(x, -128, 127) + 128);
13278}
13279
13280static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x)
13281{
13282 return (ma_int16)ma_clamp(x, -32768, 32767);
13283}
13284
13285static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x)
13286{
13287 return (ma_int64)ma_clamp(x, -8388608, 8388607);
13288}
13289
13290static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x)
13291{
13292 /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */
13293 ma_int64 clipMin;
13294 ma_int64 clipMax;
13295 clipMin = -((ma_int64)2147483647 + 1);
13296 clipMax = (ma_int64)2147483647;
13297
13298 return (ma_int32)ma_clamp(x, clipMin, clipMax);
13299}
13300
13301static MA_INLINE float ma_clip_f32(float x)
13302{
13303 if (x < -1) return -1;
13304 if (x > +1) return +1;
13305 return x;
13306}
13307
13308
13309static MA_INLINE float ma_mix_f32(float x, float y, float a)
13310{
13311 return x*(1-a) + y*a;
13312}
13313static MA_INLINE float ma_mix_f32_fast(float x, float y, float a)
13314{
13315 float r0 = (y - x);
13316 float r1 = r0*a;
13317 return x + r1;
13318 /*return x + (y - x)*a;*/
13319}
13320
13321#if defined(MA_SUPPORT_SSE2)
13322static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a)
13323{
13324 return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a));
13325}
13326#endif
13327#if defined(MA_SUPPORT_AVX2)
13328static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a)
13329{
13330 return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a));
13331}
13332#endif
13333#if defined(MA_SUPPORT_NEON)
13334static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a)
13335{
13336 return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a));
13337}
13338#endif
13339
13340
13341static MA_INLINE double ma_mix_f64(double x, double y, double a)
13342{
13343 return x*(1-a) + y*a;
13344}
13345static MA_INLINE double ma_mix_f64_fast(double x, double y, double a)
13346{
13347 return x + (y - x)*a;
13348}
13349
13350static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi)
13351{
13352 return lo + x*(hi-lo);
13353}
13354
13355
13356/*
13357Greatest common factor using Euclid's algorithm iteratively.
13358*/
13359static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b)
13360{
13361 for (;;) {
13362 if (b == 0) {
13363 break;
13364 } else {
13365 ma_uint32 t = a;
13366 a = b;
13367 b = t % a;
13368 }
13369 }
13370
13371 return a;
13372}
13373
13374
13375static ma_uint32 ma_ffs_32(ma_uint32 x)
13376{
13377 ma_uint32 i;
13378
13379 /* Just a naive implementation just to get things working for now. Will optimize this later. */
13380 for (i = 0; i < 32; i += 1) {
13381 if ((x & (1 << i)) != 0) {
13382 return i;
13383 }
13384 }
13385
13386 return i;
13387}
13388
13389static MA_INLINE ma_int16 ma_float_to_fixed_16(float x)
13390{
13391 return (ma_int16)(x * (1 << 8));
13392}
13393
13394
13395
13396/*
13397Random Number Generation
13398
13399miniaudio uses the LCG random number generation algorithm. This is good enough for audio.
13400
13401Note that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across
13402multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for
13403miniaudio's purposes.
13404*/
13405#ifndef MA_DEFAULT_LCG_SEED
13406#define MA_DEFAULT_LCG_SEED 4321
13407#endif
13408
13409#define MA_LCG_M 2147483647
13410#define MA_LCG_A 48271
13411#define MA_LCG_C 0
13412
13413static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */
13414
13415static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed)
13416{
13417 MA_ASSERT(pLCG != NULL);
13418 pLCG->state = seed;
13419}
13420
13421static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG)
13422{
13423 pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M;
13424 return pLCG->state;
13425}
13426
13427static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG)
13428{
13429 return (ma_uint32)ma_lcg_rand_s32(pLCG);
13430}
13431
13432static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG)
13433{
13434 return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF);
13435}
13436
13437static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG)
13438{
13439 return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF;
13440}
13441
13442static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG)
13443{
13444 return (float)ma_lcg_rand_f64(pLCG);
13445}
13446
13447static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi)
13448{
13449 return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi);
13450}
13451
13452static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi)
13453{
13454 if (lo == hi) {
13455 return lo;
13456 }
13457
13458 return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1);
13459}
13460
13461
13462
13463static MA_INLINE void ma_seed(ma_int32 seed)
13464{
13465 ma_lcg_seed(&g_maLCG, seed);
13466}
13467
13468static MA_INLINE ma_int32 ma_rand_s32(void)
13469{
13470 return ma_lcg_rand_s32(&g_maLCG);
13471}
13472
13473static MA_INLINE ma_uint32 ma_rand_u32(void)
13474{
13475 return ma_lcg_rand_u32(&g_maLCG);
13476}
13477
13478static MA_INLINE double ma_rand_f64(void)
13479{
13480 return ma_lcg_rand_f64(&g_maLCG);
13481}
13482
13483static MA_INLINE float ma_rand_f32(void)
13484{
13485 return ma_lcg_rand_f32(&g_maLCG);
13486}
13487
13488static MA_INLINE float ma_rand_range_f32(float lo, float hi)
13489{
13490 return ma_lcg_rand_range_f32(&g_maLCG, lo, hi);
13491}
13492
13493static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi)
13494{
13495 return ma_lcg_rand_range_s32(&g_maLCG, lo, hi);
13496}
13497
13498
13499static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax)
13500{
13501 return ma_rand_range_f32(ditherMin, ditherMax);
13502}
13503
13504static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax)
13505{
13506 float a = ma_rand_range_f32(ditherMin, 0);
13507 float b = ma_rand_range_f32(0, ditherMax);
13508 return a + b;
13509}
13510
13511static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax)
13512{
13513 if (ditherMode == ma_dither_mode_rectangle) {
13514 return ma_dither_f32_rectangle(ditherMin, ditherMax);
13515 }
13516 if (ditherMode == ma_dither_mode_triangle) {
13517 return ma_dither_f32_triangle(ditherMin, ditherMax);
13518 }
13519
13520 return 0;
13521}
13522
13523static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax)
13524{
13525 if (ditherMode == ma_dither_mode_rectangle) {
13526 ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax);
13527 return a;
13528 }
13529 if (ditherMode == ma_dither_mode_triangle) {
13530 ma_int32 a = ma_rand_range_s32(ditherMin, 0);
13531 ma_int32 b = ma_rand_range_s32(0, ditherMax);
13532 return a + b;
13533 }
13534
13535 return 0;
13536}
13537
13538
13539
13544/* c89atomic.h begin */
13545#ifndef c89atomic_h
13546#define c89atomic_h
13547#if defined(__cplusplus)
13548extern "C" {
13549#endif
13550typedef signed char c89atomic_int8;
13551typedef unsigned char c89atomic_uint8;
13552typedef signed short c89atomic_int16;
13553typedef unsigned short c89atomic_uint16;
13554typedef signed int c89atomic_int32;
13555typedef unsigned int c89atomic_uint32;
13556#if defined(_MSC_VER) && !defined(__clang__)
13557 typedef signed __int64 c89atomic_int64;
13558 typedef unsigned __int64 c89atomic_uint64;
13559#else
13560 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
13561 #pragma GCC diagnostic push
13562 #pragma GCC diagnostic ignored "-Wlong-long"
13563 #if defined(__clang__)
13564 #pragma GCC diagnostic ignored "-Wc++11-long-long"
13565 #endif
13566 #endif
13567 typedef signed long long c89atomic_int64;
13568 typedef unsigned long long c89atomic_uint64;
13569 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
13570 #pragma GCC diagnostic pop
13571 #endif
13572#endif
13573typedef int c89atomic_memory_order;
13574typedef unsigned char c89atomic_bool;
13575#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT)
13576#ifdef _WIN32
13577#ifdef _WIN64
13578#define C89ATOMIC_64BIT
13579#else
13580#define C89ATOMIC_32BIT
13581#endif
13582#endif
13583#endif
13584#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT)
13585#ifdef __GNUC__
13586#ifdef __LP64__
13587#define C89ATOMIC_64BIT
13588#else
13589#define C89ATOMIC_32BIT
13590#endif
13591#endif
13592#endif
13593#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT)
13594#include <stdint.h>
13595#if INTPTR_MAX == INT64_MAX
13596#define C89ATOMIC_64BIT
13597#else
13598#define C89ATOMIC_32BIT
13599#endif
13600#endif
13601#if defined(__x86_64__) || defined(_M_X64)
13602#define C89ATOMIC_X64
13603#elif defined(__i386) || defined(_M_IX86)
13604#define C89ATOMIC_X86
13605#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64)
13606#define C89ATOMIC_ARM
13607#endif
13608#if defined(_MSC_VER)
13609 #define C89ATOMIC_INLINE __forceinline
13610#elif defined(__GNUC__)
13611 #if defined(__STRICT_ANSI__)
13612 #define C89ATOMIC_INLINE __inline__ __attribute__((always_inline))
13613 #else
13614 #define C89ATOMIC_INLINE inline __attribute__((always_inline))
13615 #endif
13616#elif defined(__WATCOMC__) || defined(__DMC__)
13617 #define C89ATOMIC_INLINE __inline
13618#else
13619 #define C89ATOMIC_INLINE
13620#endif
13621#define C89ATOMIC_HAS_8
13622#define C89ATOMIC_HAS_16
13623#define C89ATOMIC_HAS_32
13624#define C89ATOMIC_HAS_64
13625#if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__)
13626 #define c89atomic_memory_order_relaxed 0
13627 #define c89atomic_memory_order_consume 1
13628 #define c89atomic_memory_order_acquire 2
13629 #define c89atomic_memory_order_release 3
13630 #define c89atomic_memory_order_acq_rel 4
13631 #define c89atomic_memory_order_seq_cst 5
13632 #if _MSC_VER < 1600 && defined(C89ATOMIC_X86)
13633 #define C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY
13634 #endif
13635 #if _MSC_VER < 1600
13636 #undef C89ATOMIC_HAS_8
13637 #undef C89ATOMIC_HAS_16
13638 #endif
13639 #if !defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
13640 #include <intrin.h>
13641 #endif
13642 #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
13643 #if defined(C89ATOMIC_HAS_8)
13644 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired)
13645 {
13646 c89atomic_uint8 result = 0;
13647 __asm {
13648 mov ecx, dst
13649 mov al, expected
13650 mov dl, desired
13651 lock cmpxchg [ecx], dl
13652 mov result, al
13653 }
13654 return result;
13655 }
13656 #endif
13657 #if defined(C89ATOMIC_HAS_16)
13658 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired)
13659 {
13660 c89atomic_uint16 result = 0;
13661 __asm {
13662 mov ecx, dst
13663 mov ax, expected
13664 mov dx, desired
13665 lock cmpxchg [ecx], dx
13666 mov result, ax
13667 }
13668 return result;
13669 }
13670 #endif
13671 #if defined(C89ATOMIC_HAS_32)
13672 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired)
13673 {
13674 c89atomic_uint32 result = 0;
13675 __asm {
13676 mov ecx, dst
13677 mov eax, expected
13678 mov edx, desired
13679 lock cmpxchg [ecx], edx
13680 mov result, eax
13681 }
13682 return result;
13683 }
13684 #endif
13685 #if defined(C89ATOMIC_HAS_64)
13686 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired)
13687 {
13688 c89atomic_uint32 resultEAX = 0;
13689 c89atomic_uint32 resultEDX = 0;
13690 __asm {
13691 mov esi, dst
13692 mov eax, dword ptr expected
13693 mov edx, dword ptr expected + 4
13694 mov ebx, dword ptr desired
13695 mov ecx, dword ptr desired + 4
13696 lock cmpxchg8b qword ptr [esi]
13697 mov resultEAX, eax
13698 mov resultEDX, edx
13699 }
13700 return ((c89atomic_uint64)resultEDX << 32) | resultEAX;
13701 }
13702 #endif
13703 #else
13704 #if defined(C89ATOMIC_HAS_8)
13705 #define c89atomic_compare_and_swap_8( dst, expected, desired) (c89atomic_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected)
13706 #endif
13707 #if defined(C89ATOMIC_HAS_16)
13708 #define c89atomic_compare_and_swap_16(dst, expected, desired) (c89atomic_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected)
13709 #endif
13710 #if defined(C89ATOMIC_HAS_32)
13711 #define c89atomic_compare_and_swap_32(dst, expected, desired) (c89atomic_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected)
13712 #endif
13713 #if defined(C89ATOMIC_HAS_64)
13714 #define c89atomic_compare_and_swap_64(dst, expected, desired) (c89atomic_uint64)_InterlockedCompareExchange64((volatile c89atomic_int64*)dst, (c89atomic_int64)desired, (c89atomic_int64)expected)
13715 #endif
13716 #endif
13717 #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
13718 #if defined(C89ATOMIC_HAS_8)
13719 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
13720 {
13721 c89atomic_uint8 result = 0;
13722 (void)order;
13723 __asm {
13724 mov ecx, dst
13725 mov al, src
13726 lock xchg [ecx], al
13727 mov result, al
13728 }
13729 return result;
13730 }
13731 #endif
13732 #if defined(C89ATOMIC_HAS_16)
13733 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
13734 {
13735 c89atomic_uint16 result = 0;
13736 (void)order;
13737 __asm {
13738 mov ecx, dst
13739 mov ax, src
13740 lock xchg [ecx], ax
13741 mov result, ax
13742 }
13743 return result;
13744 }
13745 #endif
13746 #if defined(C89ATOMIC_HAS_32)
13747 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
13748 {
13749 c89atomic_uint32 result = 0;
13750 (void)order;
13751 __asm {
13752 mov ecx, dst
13753 mov eax, src
13754 lock xchg [ecx], eax
13755 mov result, eax
13756 }
13757 return result;
13758 }
13759 #endif
13760 #else
13761 #if defined(C89ATOMIC_HAS_8)
13762 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
13763 {
13764 (void)order;
13765 return (c89atomic_uint8)_InterlockedExchange8((volatile char*)dst, (char)src);
13766 }
13767 #endif
13768 #if defined(C89ATOMIC_HAS_16)
13769 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
13770 {
13771 (void)order;
13772 return (c89atomic_uint16)_InterlockedExchange16((volatile short*)dst, (short)src);
13773 }
13774 #endif
13775 #if defined(C89ATOMIC_HAS_32)
13776 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
13777 {
13778 (void)order;
13779 return (c89atomic_uint32)_InterlockedExchange((volatile long*)dst, (long)src);
13780 }
13781 #endif
13782 #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT)
13783 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
13784 {
13785 (void)order;
13786 return (c89atomic_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src);
13787 }
13788 #else
13789 #endif
13790 #endif
13791 #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT)
13792 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
13793 {
13794 c89atomic_uint64 oldValue;
13795 do {
13796 oldValue = *dst;
13797 } while (c89atomic_compare_and_swap_64(dst, oldValue, src) != oldValue);
13798 (void)order;
13799 return oldValue;
13800 }
13801 #endif
13802 #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
13803 #if defined(C89ATOMIC_HAS_8)
13804 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
13805 {
13806 c89atomic_uint8 result = 0;
13807 (void)order;
13808 __asm {
13809 mov ecx, dst
13810 mov al, src
13811 lock xadd [ecx], al
13812 mov result, al
13813 }
13814 return result;
13815 }
13816 #endif
13817 #if defined(C89ATOMIC_HAS_16)
13818 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
13819 {
13820 c89atomic_uint16 result = 0;
13821 (void)order;
13822 __asm {
13823 mov ecx, dst
13824 mov ax, src
13825 lock xadd [ecx], ax
13826 mov result, ax
13827 }
13828 return result;
13829 }
13830 #endif
13831 #if defined(C89ATOMIC_HAS_32)
13832 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
13833 {
13834 c89atomic_uint32 result = 0;
13835 (void)order;
13836 __asm {
13837 mov ecx, dst
13838 mov eax, src
13839 lock xadd [ecx], eax
13840 mov result, eax
13841 }
13842 return result;
13843 }
13844 #endif
13845 #else
13846 #if defined(C89ATOMIC_HAS_8)
13847 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
13848 {
13849 (void)order;
13850 return (c89atomic_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src);
13851 }
13852 #endif
13853 #if defined(C89ATOMIC_HAS_16)
13854 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
13855 {
13856 (void)order;
13857 return (c89atomic_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src);
13858 }
13859 #endif
13860 #if defined(C89ATOMIC_HAS_32)
13861 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
13862 {
13863 (void)order;
13864 return (c89atomic_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src);
13865 }
13866 #endif
13867 #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT)
13868 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
13869 {
13870 (void)order;
13871 return (c89atomic_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src);
13872 }
13873 #else
13874 #endif
13875 #endif
13876 #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT)
13877 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
13878 {
13879 c89atomic_uint64 oldValue;
13880 c89atomic_uint64 newValue;
13881 do {
13882 oldValue = *dst;
13883 newValue = oldValue + src;
13884 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
13885 (void)order;
13886 return oldValue;
13887 }
13888 #endif
13889 #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
13890 static C89ATOMIC_INLINE void __stdcall c89atomic_thread_fence(c89atomic_memory_order order)
13891 {
13892 (void)order;
13893 __asm {
13894 lock add [esp], 0
13895 }
13896 }
13897 #else
13898 #if defined(C89ATOMIC_X64)
13899 #define c89atomic_thread_fence(order) __faststorefence(), (void)order
13900 #else
13901 static C89ATOMIC_INLINE void c89atomic_thread_fence(c89atomic_memory_order order)
13902 {
13903 volatile c89atomic_uint32 barrier = 0;
13904 c89atomic_fetch_add_explicit_32(&barrier, 0, order);
13905 }
13906 #endif
13907 #endif
13908 #define c89atomic_compiler_fence() c89atomic_thread_fence(c89atomic_memory_order_seq_cst)
13909 #define c89atomic_signal_fence(order) c89atomic_thread_fence(order)
13910 #if defined(C89ATOMIC_HAS_8)
13911 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order)
13912 {
13913 (void)order;
13914 return c89atomic_compare_and_swap_8((volatile c89atomic_uint8*)ptr, 0, 0);
13915 }
13916 #endif
13917 #if defined(C89ATOMIC_HAS_16)
13918 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order)
13919 {
13920 (void)order;
13921 return c89atomic_compare_and_swap_16((volatile c89atomic_uint16*)ptr, 0, 0);
13922 }
13923 #endif
13924 #if defined(C89ATOMIC_HAS_32)
13925 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order)
13926 {
13927 (void)order;
13928 return c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)ptr, 0, 0);
13929 }
13930 #endif
13931 #if defined(C89ATOMIC_HAS_64)
13932 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order)
13933 {
13934 (void)order;
13935 return c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)ptr, 0, 0);
13936 }
13937 #endif
13938 #if defined(C89ATOMIC_HAS_8)
13939 #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order)
13940 #endif
13941 #if defined(C89ATOMIC_HAS_16)
13942 #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order)
13943 #endif
13944 #if defined(C89ATOMIC_HAS_32)
13945 #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order)
13946 #endif
13947 #if defined(C89ATOMIC_HAS_64)
13948 #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order)
13949 #endif
13950 #if defined(C89ATOMIC_HAS_8)
13951 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
13952 {
13953 c89atomic_uint8 oldValue;
13954 c89atomic_uint8 newValue;
13955 do {
13956 oldValue = *dst;
13957 newValue = (c89atomic_uint8)(oldValue - src);
13958 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
13959 (void)order;
13960 return oldValue;
13961 }
13962 #endif
13963 #if defined(C89ATOMIC_HAS_16)
13964 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
13965 {
13966 c89atomic_uint16 oldValue;
13967 c89atomic_uint16 newValue;
13968 do {
13969 oldValue = *dst;
13970 newValue = (c89atomic_uint16)(oldValue - src);
13971 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
13972 (void)order;
13973 return oldValue;
13974 }
13975 #endif
13976 #if defined(C89ATOMIC_HAS_32)
13977 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
13978 {
13979 c89atomic_uint32 oldValue;
13980 c89atomic_uint32 newValue;
13981 do {
13982 oldValue = *dst;
13983 newValue = oldValue - src;
13984 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
13985 (void)order;
13986 return oldValue;
13987 }
13988 #endif
13989 #if defined(C89ATOMIC_HAS_64)
13990 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
13991 {
13992 c89atomic_uint64 oldValue;
13993 c89atomic_uint64 newValue;
13994 do {
13995 oldValue = *dst;
13996 newValue = oldValue - src;
13997 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
13998 (void)order;
13999 return oldValue;
14000 }
14001 #endif
14002 #if defined(C89ATOMIC_HAS_8)
14003 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14004 {
14005 c89atomic_uint8 oldValue;
14006 c89atomic_uint8 newValue;
14007 do {
14008 oldValue = *dst;
14009 newValue = (c89atomic_uint8)(oldValue & src);
14010 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
14011 (void)order;
14012 return oldValue;
14013 }
14014 #endif
14015 #if defined(C89ATOMIC_HAS_16)
14016 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14017 {
14018 c89atomic_uint16 oldValue;
14019 c89atomic_uint16 newValue;
14020 do {
14021 oldValue = *dst;
14022 newValue = (c89atomic_uint16)(oldValue & src);
14023 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
14024 (void)order;
14025 return oldValue;
14026 }
14027 #endif
14028 #if defined(C89ATOMIC_HAS_32)
14029 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14030 {
14031 c89atomic_uint32 oldValue;
14032 c89atomic_uint32 newValue;
14033 do {
14034 oldValue = *dst;
14035 newValue = oldValue & src;
14036 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
14037 (void)order;
14038 return oldValue;
14039 }
14040 #endif
14041 #if defined(C89ATOMIC_HAS_64)
14042 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14043 {
14044 c89atomic_uint64 oldValue;
14045 c89atomic_uint64 newValue;
14046 do {
14047 oldValue = *dst;
14048 newValue = oldValue & src;
14049 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
14050 (void)order;
14051 return oldValue;
14052 }
14053 #endif
14054 #if defined(C89ATOMIC_HAS_8)
14055 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14056 {
14057 c89atomic_uint8 oldValue;
14058 c89atomic_uint8 newValue;
14059 do {
14060 oldValue = *dst;
14061 newValue = (c89atomic_uint8)(oldValue ^ src);
14062 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
14063 (void)order;
14064 return oldValue;
14065 }
14066 #endif
14067 #if defined(C89ATOMIC_HAS_16)
14068 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14069 {
14070 c89atomic_uint16 oldValue;
14071 c89atomic_uint16 newValue;
14072 do {
14073 oldValue = *dst;
14074 newValue = (c89atomic_uint16)(oldValue ^ src);
14075 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
14076 (void)order;
14077 return oldValue;
14078 }
14079 #endif
14080 #if defined(C89ATOMIC_HAS_32)
14081 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14082 {
14083 c89atomic_uint32 oldValue;
14084 c89atomic_uint32 newValue;
14085 do {
14086 oldValue = *dst;
14087 newValue = oldValue ^ src;
14088 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
14089 (void)order;
14090 return oldValue;
14091 }
14092 #endif
14093 #if defined(C89ATOMIC_HAS_64)
14094 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14095 {
14096 c89atomic_uint64 oldValue;
14097 c89atomic_uint64 newValue;
14098 do {
14099 oldValue = *dst;
14100 newValue = oldValue ^ src;
14101 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
14102 (void)order;
14103 return oldValue;
14104 }
14105 #endif
14106 #if defined(C89ATOMIC_HAS_8)
14107 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14108 {
14109 c89atomic_uint8 oldValue;
14110 c89atomic_uint8 newValue;
14111 do {
14112 oldValue = *dst;
14113 newValue = (c89atomic_uint8)(oldValue | src);
14114 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
14115 (void)order;
14116 return oldValue;
14117 }
14118 #endif
14119 #if defined(C89ATOMIC_HAS_16)
14120 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14121 {
14122 c89atomic_uint16 oldValue;
14123 c89atomic_uint16 newValue;
14124 do {
14125 oldValue = *dst;
14126 newValue = (c89atomic_uint16)(oldValue | src);
14127 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
14128 (void)order;
14129 return oldValue;
14130 }
14131 #endif
14132 #if defined(C89ATOMIC_HAS_32)
14133 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14134 {
14135 c89atomic_uint32 oldValue;
14136 c89atomic_uint32 newValue;
14137 do {
14138 oldValue = *dst;
14139 newValue = oldValue | src;
14140 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
14141 (void)order;
14142 return oldValue;
14143 }
14144 #endif
14145 #if defined(C89ATOMIC_HAS_64)
14146 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14147 {
14148 c89atomic_uint64 oldValue;
14149 c89atomic_uint64 newValue;
14150 do {
14151 oldValue = *dst;
14152 newValue = oldValue | src;
14153 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
14154 (void)order;
14155 return oldValue;
14156 }
14157 #endif
14158 #if defined(C89ATOMIC_HAS_8)
14159 #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order)
14160 #endif
14161 #if defined(C89ATOMIC_HAS_16)
14162 #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order)
14163 #endif
14164 #if defined(C89ATOMIC_HAS_32)
14165 #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order)
14166 #endif
14167 #if defined(C89ATOMIC_HAS_64)
14168 #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order)
14169 #endif
14170 #if defined(C89ATOMIC_HAS_8)
14171 #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order)
14172 #endif
14173 #if defined(C89ATOMIC_HAS_16)
14174 #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order)
14175 #endif
14176 #if defined(C89ATOMIC_HAS_32)
14177 #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order)
14178 #endif
14179 #if defined(C89ATOMIC_HAS_64)
14180 #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order)
14181 #endif
14182 #if defined(C89ATOMIC_HAS_8)
14183 typedef c89atomic_uint8 c89atomic_flag;
14184 #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order)
14185 #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order)
14186 #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order)
14187 #else
14188 typedef c89atomic_uint32 c89atomic_flag;
14189 #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_32(ptr, order)
14190 #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_32(ptr, order)
14191 #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_32(ptr, order)
14192 #endif
14193#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
14194 #define C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE
14195 #define C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE
14196 #define c89atomic_memory_order_relaxed __ATOMIC_RELAXED
14197 #define c89atomic_memory_order_consume __ATOMIC_CONSUME
14198 #define c89atomic_memory_order_acquire __ATOMIC_ACQUIRE
14199 #define c89atomic_memory_order_release __ATOMIC_RELEASE
14200 #define c89atomic_memory_order_acq_rel __ATOMIC_ACQ_REL
14201 #define c89atomic_memory_order_seq_cst __ATOMIC_SEQ_CST
14202 #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory")
14203 #define c89atomic_thread_fence(order) __atomic_thread_fence(order)
14204 #define c89atomic_signal_fence(order) __atomic_signal_fence(order)
14205 #define c89atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr)
14206 #define c89atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr)
14207 #define c89atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr)
14208 #define c89atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr)
14209 #define c89atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order)
14210 #define c89atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order)
14211 #define c89atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order)
14212 #define c89atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order)
14213 #define c89atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order)
14214 #define c89atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order)
14215 #define c89atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order)
14216 #define c89atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order)
14217 #define c89atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order)
14218 #define c89atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order)
14219 #define c89atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order)
14220 #define c89atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order)
14221 #define c89atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order)
14222 #define c89atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order)
14223 #define c89atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order)
14224 #define c89atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order)
14225 #define c89atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order)
14226 #define c89atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order)
14227 #define c89atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order)
14228 #define c89atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order)
14229 #define c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
14230 #define c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
14231 #define c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
14232 #define c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
14233 #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
14234 #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
14235 #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
14236 #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
14237 #define c89atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order)
14238 #define c89atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order)
14239 #define c89atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order)
14240 #define c89atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order)
14241 #define c89atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order)
14242 #define c89atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order)
14243 #define c89atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order)
14244 #define c89atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order)
14245 #define c89atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order)
14246 #define c89atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order)
14247 #define c89atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order)
14248 #define c89atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order)
14249 #define c89atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order)
14250 #define c89atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order)
14251 #define c89atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order)
14252 #define c89atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order)
14253 #define c89atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order)
14254 #define c89atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order)
14255 #define c89atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order)
14256 #define c89atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order)
14257 #define c89atomic_compare_and_swap_8 (dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
14258 #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
14259 #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
14260 #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
14261 typedef c89atomic_uint8 c89atomic_flag;
14262 #define c89atomic_flag_test_and_set_explicit(dst, order) (c89atomic_bool)__atomic_test_and_set(dst, order)
14263 #define c89atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order)
14264 #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order)
14265#else
14266 #define c89atomic_memory_order_relaxed 1
14267 #define c89atomic_memory_order_consume 2
14268 #define c89atomic_memory_order_acquire 3
14269 #define c89atomic_memory_order_release 4
14270 #define c89atomic_memory_order_acq_rel 5
14271 #define c89atomic_memory_order_seq_cst 6
14272 #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory")
14273 #if defined(__GNUC__)
14274 #define c89atomic_thread_fence(order) __sync_synchronize(), (void)order
14275 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14276 {
14277 if (order > c89atomic_memory_order_acquire) {
14278 __sync_synchronize();
14279 }
14280 return __sync_lock_test_and_set(dst, src);
14281 }
14282 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14283 {
14284 c89atomic_uint16 oldValue;
14285 do {
14286 oldValue = *dst;
14287 } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);
14288 (void)order;
14289 return oldValue;
14290 }
14291 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14292 {
14293 c89atomic_uint32 oldValue;
14294 do {
14295 oldValue = *dst;
14296 } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);
14297 (void)order;
14298 return oldValue;
14299 }
14300 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14301 {
14302 c89atomic_uint64 oldValue;
14303 do {
14304 oldValue = *dst;
14305 } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);
14306 (void)order;
14307 return oldValue;
14308 }
14309 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14310 {
14311 (void)order;
14312 return __sync_fetch_and_add(dst, src);
14313 }
14314 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14315 {
14316 (void)order;
14317 return __sync_fetch_and_add(dst, src);
14318 }
14319 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14320 {
14321 (void)order;
14322 return __sync_fetch_and_add(dst, src);
14323 }
14324 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14325 {
14326 (void)order;
14327 return __sync_fetch_and_add(dst, src);
14328 }
14329 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14330 {
14331 (void)order;
14332 return __sync_fetch_and_sub(dst, src);
14333 }
14334 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14335 {
14336 (void)order;
14337 return __sync_fetch_and_sub(dst, src);
14338 }
14339 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14340 {
14341 (void)order;
14342 return __sync_fetch_and_sub(dst, src);
14343 }
14344 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14345 {
14346 (void)order;
14347 return __sync_fetch_and_sub(dst, src);
14348 }
14349 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14350 {
14351 (void)order;
14352 return __sync_fetch_and_or(dst, src);
14353 }
14354 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14355 {
14356 (void)order;
14357 return __sync_fetch_and_or(dst, src);
14358 }
14359 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14360 {
14361 (void)order;
14362 return __sync_fetch_and_or(dst, src);
14363 }
14364 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14365 {
14366 (void)order;
14367 return __sync_fetch_and_or(dst, src);
14368 }
14369 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14370 {
14371 (void)order;
14372 return __sync_fetch_and_xor(dst, src);
14373 }
14374 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14375 {
14376 (void)order;
14377 return __sync_fetch_and_xor(dst, src);
14378 }
14379 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14380 {
14381 (void)order;
14382 return __sync_fetch_and_xor(dst, src);
14383 }
14384 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14385 {
14386 (void)order;
14387 return __sync_fetch_and_xor(dst, src);
14388 }
14389 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14390 {
14391 (void)order;
14392 return __sync_fetch_and_and(dst, src);
14393 }
14394 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14395 {
14396 (void)order;
14397 return __sync_fetch_and_and(dst, src);
14398 }
14399 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14400 {
14401 (void)order;
14402 return __sync_fetch_and_and(dst, src);
14403 }
14404 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14405 {
14406 (void)order;
14407 return __sync_fetch_and_and(dst, src);
14408 }
14409 #define c89atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
14410 #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
14411 #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
14412 #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
14413 #else
14414 #if defined(C89ATOMIC_X86)
14415 #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc")
14416 #elif defined(C89ATOMIC_X64)
14417 #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc")
14418 #else
14419 #error Unsupported architecture. Please submit a feature request.
14420 #endif
14421 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired)
14422 {
14423 c89atomic_uint8 result;
14424 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
14425 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
14426 #else
14427 #error Unsupported architecture. Please submit a feature request.
14428 #endif
14429 return result;
14430 }
14431 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired)
14432 {
14433 c89atomic_uint16 result;
14434 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
14435 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
14436 #else
14437 #error Unsupported architecture. Please submit a feature request.
14438 #endif
14439 return result;
14440 }
14441 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired)
14442 {
14443 c89atomic_uint32 result;
14444 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
14445 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
14446 #else
14447 #error Unsupported architecture. Please submit a feature request.
14448 #endif
14449 return result;
14450 }
14451 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired)
14452 {
14453 volatile c89atomic_uint64 result;
14454 #if defined(C89ATOMIC_X86)
14455 c89atomic_uint32 resultEAX;
14456 c89atomic_uint32 resultEDX;
14457 __asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc");
14458 result = ((c89atomic_uint64)resultEDX << 32) | resultEAX;
14459 #elif defined(C89ATOMIC_X64)
14460 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
14461 #else
14462 #error Unsupported architecture. Please submit a feature request.
14463 #endif
14464 return result;
14465 }
14466 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14467 {
14468 c89atomic_uint8 result = 0;
14469 (void)order;
14470 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
14471 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
14472 #else
14473 #error Unsupported architecture. Please submit a feature request.
14474 #endif
14475 return result;
14476 }
14477 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14478 {
14479 c89atomic_uint16 result = 0;
14480 (void)order;
14481 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
14482 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
14483 #else
14484 #error Unsupported architecture. Please submit a feature request.
14485 #endif
14486 return result;
14487 }
14488 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14489 {
14490 c89atomic_uint32 result;
14491 (void)order;
14492 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
14493 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
14494 #else
14495 #error Unsupported architecture. Please submit a feature request.
14496 #endif
14497 return result;
14498 }
14499 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14500 {
14501 c89atomic_uint64 result;
14502 (void)order;
14503 #if defined(C89ATOMIC_X86)
14504 do {
14505 result = *dst;
14506 } while (c89atomic_compare_and_swap_64(dst, result, src) != result);
14507 #elif defined(C89ATOMIC_X64)
14508 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
14509 #else
14510 #error Unsupported architecture. Please submit a feature request.
14511 #endif
14512 return result;
14513 }
14514 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14515 {
14516 c89atomic_uint8 result;
14517 (void)order;
14518 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
14519 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc");
14520 #else
14521 #error Unsupported architecture. Please submit a feature request.
14522 #endif
14523 return result;
14524 }
14525 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14526 {
14527 c89atomic_uint16 result;
14528 (void)order;
14529 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
14530 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc");
14531 #else
14532 #error Unsupported architecture. Please submit a feature request.
14533 #endif
14534 return result;
14535 }
14536 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14537 {
14538 c89atomic_uint32 result;
14539 (void)order;
14540 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
14541 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc");
14542 #else
14543 #error Unsupported architecture. Please submit a feature request.
14544 #endif
14545 return result;
14546 }
14547 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14548 {
14549 #if defined(C89ATOMIC_X86)
14550 c89atomic_uint64 oldValue;
14551 c89atomic_uint64 newValue;
14552 (void)order;
14553 do {
14554 oldValue = *dst;
14555 newValue = oldValue + src;
14556 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
14557 return oldValue;
14558 #elif defined(C89ATOMIC_X64)
14559 c89atomic_uint64 result;
14560 (void)order;
14561 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc");
14562 return result;
14563 #endif
14564 }
14565 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14566 {
14567 c89atomic_uint8 oldValue;
14568 c89atomic_uint8 newValue;
14569 do {
14570 oldValue = *dst;
14571 newValue = (c89atomic_uint8)(oldValue - src);
14572 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
14573 (void)order;
14574 return oldValue;
14575 }
14576 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14577 {
14578 c89atomic_uint16 oldValue;
14579 c89atomic_uint16 newValue;
14580 do {
14581 oldValue = *dst;
14582 newValue = (c89atomic_uint16)(oldValue - src);
14583 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
14584 (void)order;
14585 return oldValue;
14586 }
14587 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14588 {
14589 c89atomic_uint32 oldValue;
14590 c89atomic_uint32 newValue;
14591 do {
14592 oldValue = *dst;
14593 newValue = oldValue - src;
14594 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
14595 (void)order;
14596 return oldValue;
14597 }
14598 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14599 {
14600 c89atomic_uint64 oldValue;
14601 c89atomic_uint64 newValue;
14602 do {
14603 oldValue = *dst;
14604 newValue = oldValue - src;
14605 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
14606 (void)order;
14607 return oldValue;
14608 }
14609 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14610 {
14611 c89atomic_uint8 oldValue;
14612 c89atomic_uint8 newValue;
14613 do {
14614 oldValue = *dst;
14615 newValue = (c89atomic_uint8)(oldValue & src);
14616 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
14617 (void)order;
14618 return oldValue;
14619 }
14620 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14621 {
14622 c89atomic_uint16 oldValue;
14623 c89atomic_uint16 newValue;
14624 do {
14625 oldValue = *dst;
14626 newValue = (c89atomic_uint16)(oldValue & src);
14627 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
14628 (void)order;
14629 return oldValue;
14630 }
14631 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14632 {
14633 c89atomic_uint32 oldValue;
14634 c89atomic_uint32 newValue;
14635 do {
14636 oldValue = *dst;
14637 newValue = oldValue & src;
14638 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
14639 (void)order;
14640 return oldValue;
14641 }
14642 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14643 {
14644 c89atomic_uint64 oldValue;
14645 c89atomic_uint64 newValue;
14646 do {
14647 oldValue = *dst;
14648 newValue = oldValue & src;
14649 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
14650 (void)order;
14651 return oldValue;
14652 }
14653 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14654 {
14655 c89atomic_uint8 oldValue;
14656 c89atomic_uint8 newValue;
14657 do {
14658 oldValue = *dst;
14659 newValue = (c89atomic_uint8)(oldValue ^ src);
14660 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
14661 (void)order;
14662 return oldValue;
14663 }
14664 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14665 {
14666 c89atomic_uint16 oldValue;
14667 c89atomic_uint16 newValue;
14668 do {
14669 oldValue = *dst;
14670 newValue = (c89atomic_uint16)(oldValue ^ src);
14671 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
14672 (void)order;
14673 return oldValue;
14674 }
14675 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14676 {
14677 c89atomic_uint32 oldValue;
14678 c89atomic_uint32 newValue;
14679 do {
14680 oldValue = *dst;
14681 newValue = oldValue ^ src;
14682 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
14683 (void)order;
14684 return oldValue;
14685 }
14686 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14687 {
14688 c89atomic_uint64 oldValue;
14689 c89atomic_uint64 newValue;
14690 do {
14691 oldValue = *dst;
14692 newValue = oldValue ^ src;
14693 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
14694 (void)order;
14695 return oldValue;
14696 }
14697 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14698 {
14699 c89atomic_uint8 oldValue;
14700 c89atomic_uint8 newValue;
14701 do {
14702 oldValue = *dst;
14703 newValue = (c89atomic_uint8)(oldValue | src);
14704 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
14705 (void)order;
14706 return oldValue;
14707 }
14708 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14709 {
14710 c89atomic_uint16 oldValue;
14711 c89atomic_uint16 newValue;
14712 do {
14713 oldValue = *dst;
14714 newValue = (c89atomic_uint16)(oldValue | src);
14715 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
14716 (void)order;
14717 return oldValue;
14718 }
14719 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14720 {
14721 c89atomic_uint32 oldValue;
14722 c89atomic_uint32 newValue;
14723 do {
14724 oldValue = *dst;
14725 newValue = oldValue | src;
14726 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
14727 (void)order;
14728 return oldValue;
14729 }
14730 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14731 {
14732 c89atomic_uint64 oldValue;
14733 c89atomic_uint64 newValue;
14734 do {
14735 oldValue = *dst;
14736 newValue = oldValue | src;
14737 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
14738 (void)order;
14739 return oldValue;
14740 }
14741 #endif
14742 #define c89atomic_signal_fence(order) c89atomic_thread_fence(order)
14743 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order)
14744 {
14745 (void)order;
14746 return c89atomic_compare_and_swap_8((c89atomic_uint8*)ptr, 0, 0);
14747 }
14748 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order)
14749 {
14750 (void)order;
14751 return c89atomic_compare_and_swap_16((c89atomic_uint16*)ptr, 0, 0);
14752 }
14753 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order)
14754 {
14755 (void)order;
14756 return c89atomic_compare_and_swap_32((c89atomic_uint32*)ptr, 0, 0);
14757 }
14758 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order)
14759 {
14760 (void)order;
14761 return c89atomic_compare_and_swap_64((c89atomic_uint64*)ptr, 0, 0);
14762 }
14763 #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order)
14764 #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order)
14765 #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order)
14766 #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order)
14767 #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order)
14768 #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order)
14769 #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order)
14770 #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order)
14771 #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order)
14772 #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order)
14773 #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order)
14774 #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order)
14775 typedef c89atomic_uint8 c89atomic_flag;
14776 #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order)
14777 #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order)
14778 #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order)
14779#endif
14780#if !defined(C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE)
14781 #if defined(C89ATOMIC_HAS_8)
14782 c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
14783 {
14784 c89atomic_uint8 expectedValue;
14785 c89atomic_uint8 result;
14786 (void)successOrder;
14787 (void)failureOrder;
14788 expectedValue = c89atomic_load_explicit_8(expected, c89atomic_memory_order_seq_cst);
14789 result = c89atomic_compare_and_swap_8(dst, expectedValue, desired);
14790 if (result == expectedValue) {
14791 return 1;
14792 } else {
14793 c89atomic_store_explicit_8(expected, result, failureOrder);
14794 return 0;
14795 }
14796 }
14797 #endif
14798 #if defined(C89ATOMIC_HAS_16)
14799 c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
14800 {
14801 c89atomic_uint16 expectedValue;
14802 c89atomic_uint16 result;
14803 (void)successOrder;
14804 (void)failureOrder;
14805 expectedValue = c89atomic_load_explicit_16(expected, c89atomic_memory_order_seq_cst);
14806 result = c89atomic_compare_and_swap_16(dst, expectedValue, desired);
14807 if (result == expectedValue) {
14808 return 1;
14809 } else {
14810 c89atomic_store_explicit_16(expected, result, failureOrder);
14811 return 0;
14812 }
14813 }
14814 #endif
14815 #if defined(C89ATOMIC_HAS_32)
14816 c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
14817 {
14818 c89atomic_uint32 expectedValue;
14819 c89atomic_uint32 result;
14820 (void)successOrder;
14821 (void)failureOrder;
14822 expectedValue = c89atomic_load_explicit_32(expected, c89atomic_memory_order_seq_cst);
14823 result = c89atomic_compare_and_swap_32(dst, expectedValue, desired);
14824 if (result == expectedValue) {
14825 return 1;
14826 } else {
14827 c89atomic_store_explicit_32(expected, result, failureOrder);
14828 return 0;
14829 }
14830 }
14831 #endif
14832 #if defined(C89ATOMIC_HAS_64)
14833 c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, volatile c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
14834 {
14835 c89atomic_uint64 expectedValue;
14836 c89atomic_uint64 result;
14837 (void)successOrder;
14838 (void)failureOrder;
14839 expectedValue = c89atomic_load_explicit_64(expected, c89atomic_memory_order_seq_cst);
14840 result = c89atomic_compare_and_swap_64(dst, expectedValue, desired);
14841 if (result == expectedValue) {
14842 return 1;
14843 } else {
14844 c89atomic_store_explicit_64(expected, result, failureOrder);
14845 return 0;
14846 }
14847 }
14848 #endif
14849 #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder)
14850 #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder)
14851 #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder)
14852 #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder)
14853#endif
14854#if !defined(C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE)
14855 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_8(volatile void* ptr)
14856 {
14857 (void)ptr;
14858 return 1;
14859 }
14860 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_16(volatile void* ptr)
14861 {
14862 (void)ptr;
14863 return 1;
14864 }
14865 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_32(volatile void* ptr)
14866 {
14867 (void)ptr;
14868 return 1;
14869 }
14870 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_64(volatile void* ptr)
14871 {
14872 (void)ptr;
14873 #if defined(C89ATOMIC_64BIT)
14874 return 1;
14875 #else
14876 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
14877 return 1;
14878 #else
14879 return 0;
14880 #endif
14881 #endif
14882 }
14883#endif
14884#if defined(C89ATOMIC_64BIT)
14885 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr)
14886 {
14887 return c89atomic_is_lock_free_64((volatile c89atomic_uint64*)ptr);
14888 }
14889 static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order)
14890 {
14891 return (void*)c89atomic_load_explicit_64((volatile c89atomic_uint64*)ptr, order);
14892 }
14893 static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order)
14894 {
14895 c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order);
14896 }
14897 static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order)
14898 {
14899 return (void*)c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order);
14900 }
14901 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
14902 {
14903 return c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder);
14904 }
14905 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
14906 {
14907 return c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder);
14908 }
14909 static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired)
14910 {
14911 return (void*)c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)desired);
14912 }
14913#elif defined(C89ATOMIC_32BIT)
14914 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr)
14915 {
14916 return c89atomic_is_lock_free_32((volatile c89atomic_uint32*)ptr);
14917 }
14918 static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order)
14919 {
14920 return (void*)c89atomic_load_explicit_32((volatile c89atomic_uint32*)ptr, order);
14921 }
14922 static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order)
14923 {
14924 c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order);
14925 }
14926 static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order)
14927 {
14928 return (void*)c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order);
14929 }
14930 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
14931 {
14932 return c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder);
14933 }
14934 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
14935 {
14936 return c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder);
14937 }
14938 static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired)
14939 {
14940 return (void*)c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)desired);
14941 }
14942#else
14943 #error Unsupported architecture.
14944#endif
14945#define c89atomic_flag_test_and_set(ptr) c89atomic_flag_test_and_set_explicit(ptr, c89atomic_memory_order_seq_cst)
14946#define c89atomic_flag_clear(ptr) c89atomic_flag_clear_explicit(ptr, c89atomic_memory_order_seq_cst)
14947#define c89atomic_store_ptr(dst, src) c89atomic_store_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst)
14948#define c89atomic_load_ptr(ptr) c89atomic_load_explicit_ptr((volatile void**)ptr, c89atomic_memory_order_seq_cst)
14949#define c89atomic_exchange_ptr(dst, src) c89atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst)
14950#define c89atomic_compare_exchange_strong_ptr(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
14951#define c89atomic_compare_exchange_weak_ptr(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
14952#define c89atomic_test_and_set_8( ptr) c89atomic_test_and_set_explicit_8( ptr, c89atomic_memory_order_seq_cst)
14953#define c89atomic_test_and_set_16(ptr) c89atomic_test_and_set_explicit_16(ptr, c89atomic_memory_order_seq_cst)
14954#define c89atomic_test_and_set_32(ptr) c89atomic_test_and_set_explicit_32(ptr, c89atomic_memory_order_seq_cst)
14955#define c89atomic_test_and_set_64(ptr) c89atomic_test_and_set_explicit_64(ptr, c89atomic_memory_order_seq_cst)
14956#define c89atomic_clear_8( ptr) c89atomic_clear_explicit_8( ptr, c89atomic_memory_order_seq_cst)
14957#define c89atomic_clear_16(ptr) c89atomic_clear_explicit_16(ptr, c89atomic_memory_order_seq_cst)
14958#define c89atomic_clear_32(ptr) c89atomic_clear_explicit_32(ptr, c89atomic_memory_order_seq_cst)
14959#define c89atomic_clear_64(ptr) c89atomic_clear_explicit_64(ptr, c89atomic_memory_order_seq_cst)
14960#define c89atomic_store_8( dst, src) c89atomic_store_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
14961#define c89atomic_store_16(dst, src) c89atomic_store_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
14962#define c89atomic_store_32(dst, src) c89atomic_store_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
14963#define c89atomic_store_64(dst, src) c89atomic_store_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
14964#define c89atomic_load_8( ptr) c89atomic_load_explicit_8( ptr, c89atomic_memory_order_seq_cst)
14965#define c89atomic_load_16(ptr) c89atomic_load_explicit_16(ptr, c89atomic_memory_order_seq_cst)
14966#define c89atomic_load_32(ptr) c89atomic_load_explicit_32(ptr, c89atomic_memory_order_seq_cst)
14967#define c89atomic_load_64(ptr) c89atomic_load_explicit_64(ptr, c89atomic_memory_order_seq_cst)
14968#define c89atomic_exchange_8( dst, src) c89atomic_exchange_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
14969#define c89atomic_exchange_16(dst, src) c89atomic_exchange_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
14970#define c89atomic_exchange_32(dst, src) c89atomic_exchange_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
14971#define c89atomic_exchange_64(dst, src) c89atomic_exchange_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
14972#define c89atomic_compare_exchange_strong_8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
14973#define c89atomic_compare_exchange_strong_16(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
14974#define c89atomic_compare_exchange_strong_32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
14975#define c89atomic_compare_exchange_strong_64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
14976#define c89atomic_compare_exchange_weak_8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
14977#define c89atomic_compare_exchange_weak_16( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
14978#define c89atomic_compare_exchange_weak_32( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
14979#define c89atomic_compare_exchange_weak_64( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
14980#define c89atomic_fetch_add_8( dst, src) c89atomic_fetch_add_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
14981#define c89atomic_fetch_add_16(dst, src) c89atomic_fetch_add_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
14982#define c89atomic_fetch_add_32(dst, src) c89atomic_fetch_add_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
14983#define c89atomic_fetch_add_64(dst, src) c89atomic_fetch_add_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
14984#define c89atomic_fetch_sub_8( dst, src) c89atomic_fetch_sub_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
14985#define c89atomic_fetch_sub_16(dst, src) c89atomic_fetch_sub_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
14986#define c89atomic_fetch_sub_32(dst, src) c89atomic_fetch_sub_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
14987#define c89atomic_fetch_sub_64(dst, src) c89atomic_fetch_sub_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
14988#define c89atomic_fetch_or_8( dst, src) c89atomic_fetch_or_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
14989#define c89atomic_fetch_or_16(dst, src) c89atomic_fetch_or_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
14990#define c89atomic_fetch_or_32(dst, src) c89atomic_fetch_or_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
14991#define c89atomic_fetch_or_64(dst, src) c89atomic_fetch_or_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
14992#define c89atomic_fetch_xor_8( dst, src) c89atomic_fetch_xor_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
14993#define c89atomic_fetch_xor_16(dst, src) c89atomic_fetch_xor_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
14994#define c89atomic_fetch_xor_32(dst, src) c89atomic_fetch_xor_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
14995#define c89atomic_fetch_xor_64(dst, src) c89atomic_fetch_xor_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
14996#define c89atomic_fetch_and_8( dst, src) c89atomic_fetch_and_explicit_8 (dst, src, c89atomic_memory_order_seq_cst)
14997#define c89atomic_fetch_and_16(dst, src) c89atomic_fetch_and_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
14998#define c89atomic_fetch_and_32(dst, src) c89atomic_fetch_and_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
14999#define c89atomic_fetch_and_64(dst, src) c89atomic_fetch_and_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
15000#define c89atomic_test_and_set_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_test_and_set_explicit_8( (c89atomic_uint8* )ptr, order)
15001#define c89atomic_test_and_set_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_test_and_set_explicit_16((c89atomic_uint16*)ptr, order)
15002#define c89atomic_test_and_set_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_test_and_set_explicit_32((c89atomic_uint32*)ptr, order)
15003#define c89atomic_test_and_set_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_test_and_set_explicit_64((c89atomic_uint64*)ptr, order)
15004#define c89atomic_clear_explicit_i8( ptr, order) c89atomic_clear_explicit_8( (c89atomic_uint8* )ptr, order)
15005#define c89atomic_clear_explicit_i16(ptr, order) c89atomic_clear_explicit_16((c89atomic_uint16*)ptr, order)
15006#define c89atomic_clear_explicit_i32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order)
15007#define c89atomic_clear_explicit_i64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order)
15008#define c89atomic_store_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_store_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
15009#define c89atomic_store_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_store_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
15010#define c89atomic_store_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_store_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
15011#define c89atomic_store_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_store_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
15012#define c89atomic_load_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_load_explicit_8( (c89atomic_uint8* )ptr, order)
15013#define c89atomic_load_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_load_explicit_16((c89atomic_uint16*)ptr, order)
15014#define c89atomic_load_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_load_explicit_32((c89atomic_uint32*)ptr, order)
15015#define c89atomic_load_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_load_explicit_64((c89atomic_uint64*)ptr, order)
15016#define c89atomic_exchange_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_exchange_explicit_8 ((c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
15017#define c89atomic_exchange_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_exchange_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
15018#define c89atomic_exchange_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_exchange_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
15019#define c89atomic_exchange_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_exchange_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
15020#define c89atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8* )expected, (c89atomic_uint8 )desired, successOrder, failureOrder)
15021#define c89atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16*)expected, (c89atomic_uint16)desired, successOrder, failureOrder)
15022#define c89atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder)
15023#define c89atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder)
15024#define c89atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8* )expected, (c89atomic_uint8 )desired, successOrder, failureOrder)
15025#define c89atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16*)expected, (c89atomic_uint16)desired, successOrder, failureOrder)
15026#define c89atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder)
15027#define c89atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder)
15028#define c89atomic_fetch_add_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_add_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
15029#define c89atomic_fetch_add_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_add_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
15030#define c89atomic_fetch_add_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_add_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
15031#define c89atomic_fetch_add_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_add_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
15032#define c89atomic_fetch_sub_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_sub_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
15033#define c89atomic_fetch_sub_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_sub_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
15034#define c89atomic_fetch_sub_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_sub_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
15035#define c89atomic_fetch_sub_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_sub_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
15036#define c89atomic_fetch_or_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_or_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
15037#define c89atomic_fetch_or_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_or_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
15038#define c89atomic_fetch_or_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_or_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
15039#define c89atomic_fetch_or_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_or_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
15040#define c89atomic_fetch_xor_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_xor_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
15041#define c89atomic_fetch_xor_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_xor_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
15042#define c89atomic_fetch_xor_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_xor_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
15043#define c89atomic_fetch_xor_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_xor_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
15044#define c89atomic_fetch_and_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_and_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
15045#define c89atomic_fetch_and_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_and_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
15046#define c89atomic_fetch_and_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_and_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
15047#define c89atomic_fetch_and_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_and_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
15048#define c89atomic_test_and_set_i8( ptr) c89atomic_test_and_set_explicit_i8( ptr, c89atomic_memory_order_seq_cst)
15049#define c89atomic_test_and_set_i16(ptr) c89atomic_test_and_set_explicit_i16(ptr, c89atomic_memory_order_seq_cst)
15050#define c89atomic_test_and_set_i32(ptr) c89atomic_test_and_set_explicit_i32(ptr, c89atomic_memory_order_seq_cst)
15051#define c89atomic_test_and_set_i64(ptr) c89atomic_test_and_set_explicit_i64(ptr, c89atomic_memory_order_seq_cst)
15052#define c89atomic_clear_i8( ptr) c89atomic_clear_explicit_i8( ptr, c89atomic_memory_order_seq_cst)
15053#define c89atomic_clear_i16(ptr) c89atomic_clear_explicit_i16(ptr, c89atomic_memory_order_seq_cst)
15054#define c89atomic_clear_i32(ptr) c89atomic_clear_explicit_i32(ptr, c89atomic_memory_order_seq_cst)
15055#define c89atomic_clear_i64(ptr) c89atomic_clear_explicit_i64(ptr, c89atomic_memory_order_seq_cst)
15056#define c89atomic_store_i8( dst, src) c89atomic_store_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
15057#define c89atomic_store_i16(dst, src) c89atomic_store_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
15058#define c89atomic_store_i32(dst, src) c89atomic_store_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
15059#define c89atomic_store_i64(dst, src) c89atomic_store_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
15060#define c89atomic_load_i8( ptr) c89atomic_load_explicit_i8( ptr, c89atomic_memory_order_seq_cst)
15061#define c89atomic_load_i16(ptr) c89atomic_load_explicit_i16(ptr, c89atomic_memory_order_seq_cst)
15062#define c89atomic_load_i32(ptr) c89atomic_load_explicit_i32(ptr, c89atomic_memory_order_seq_cst)
15063#define c89atomic_load_i64(ptr) c89atomic_load_explicit_i64(ptr, c89atomic_memory_order_seq_cst)
15064#define c89atomic_exchange_i8( dst, src) c89atomic_exchange_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
15065#define c89atomic_exchange_i16(dst, src) c89atomic_exchange_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
15066#define c89atomic_exchange_i32(dst, src) c89atomic_exchange_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
15067#define c89atomic_exchange_i64(dst, src) c89atomic_exchange_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
15068#define c89atomic_compare_exchange_strong_i8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15069#define c89atomic_compare_exchange_strong_i16(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15070#define c89atomic_compare_exchange_strong_i32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15071#define c89atomic_compare_exchange_strong_i64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15072#define c89atomic_compare_exchange_weak_i8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15073#define c89atomic_compare_exchange_weak_i16(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15074#define c89atomic_compare_exchange_weak_i32(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15075#define c89atomic_compare_exchange_weak_i64(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15076#define c89atomic_fetch_add_i8( dst, src) c89atomic_fetch_add_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
15077#define c89atomic_fetch_add_i16(dst, src) c89atomic_fetch_add_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
15078#define c89atomic_fetch_add_i32(dst, src) c89atomic_fetch_add_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
15079#define c89atomic_fetch_add_i64(dst, src) c89atomic_fetch_add_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
15080#define c89atomic_fetch_sub_i8( dst, src) c89atomic_fetch_sub_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
15081#define c89atomic_fetch_sub_i16(dst, src) c89atomic_fetch_sub_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
15082#define c89atomic_fetch_sub_i32(dst, src) c89atomic_fetch_sub_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
15083#define c89atomic_fetch_sub_i64(dst, src) c89atomic_fetch_sub_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
15084#define c89atomic_fetch_or_i8( dst, src) c89atomic_fetch_or_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
15085#define c89atomic_fetch_or_i16(dst, src) c89atomic_fetch_or_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
15086#define c89atomic_fetch_or_i32(dst, src) c89atomic_fetch_or_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
15087#define c89atomic_fetch_or_i64(dst, src) c89atomic_fetch_or_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
15088#define c89atomic_fetch_xor_i8( dst, src) c89atomic_fetch_xor_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
15089#define c89atomic_fetch_xor_i16(dst, src) c89atomic_fetch_xor_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
15090#define c89atomic_fetch_xor_i32(dst, src) c89atomic_fetch_xor_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
15091#define c89atomic_fetch_xor_i64(dst, src) c89atomic_fetch_xor_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
15092#define c89atomic_fetch_and_i8( dst, src) c89atomic_fetch_and_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
15093#define c89atomic_fetch_and_i16(dst, src) c89atomic_fetch_and_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
15094#define c89atomic_fetch_and_i32(dst, src) c89atomic_fetch_and_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
15095#define c89atomic_fetch_and_i64(dst, src) c89atomic_fetch_and_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
15096#define c89atomic_compare_and_swap_i8( dst, expected, dedsired) (c89atomic_int8 )c89atomic_compare_and_swap_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )expected, (c89atomic_uint8 )dedsired)
15097#define c89atomic_compare_and_swap_i16(dst, expected, dedsired) (c89atomic_int16)c89atomic_compare_and_swap_16((c89atomic_uint16*)dst, (c89atomic_uint16)expected, (c89atomic_uint16)dedsired)
15098#define c89atomic_compare_and_swap_i32(dst, expected, dedsired) (c89atomic_int32)c89atomic_compare_and_swap_32((c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)dedsired)
15099#define c89atomic_compare_and_swap_i64(dst, expected, dedsired) (c89atomic_int64)c89atomic_compare_and_swap_64((c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)dedsired)
15100typedef union
15101{
15102 c89atomic_uint32 i;
15103 float f;
15104} c89atomic_if32;
15105typedef union
15106{
15107 c89atomic_uint64 i;
15108 double f;
15109} c89atomic_if64;
15110#define c89atomic_clear_explicit_f32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order)
15111#define c89atomic_clear_explicit_f64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order)
15112static C89ATOMIC_INLINE void c89atomic_store_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order)
15113{
15114 c89atomic_if32 x;
15115 x.f = src;
15116 c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, x.i, order);
15117}
15118static C89ATOMIC_INLINE void c89atomic_store_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order)
15119{
15120 c89atomic_if64 x;
15121 x.f = src;
15122 c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, x.i, order);
15123}
15124static C89ATOMIC_INLINE float c89atomic_load_explicit_f32(volatile const float* ptr, c89atomic_memory_order order)
15125{
15126 c89atomic_if32 r;
15127 r.i = c89atomic_load_explicit_32((volatile const c89atomic_uint32*)ptr, order);
15128 return r.f;
15129}
15130static C89ATOMIC_INLINE double c89atomic_load_explicit_f64(volatile const double* ptr, c89atomic_memory_order order)
15131{
15132 c89atomic_if64 r;
15133 r.i = c89atomic_load_explicit_64((volatile const c89atomic_uint64*)ptr, order);
15134 return r.f;
15135}
15136static C89ATOMIC_INLINE float c89atomic_exchange_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order)
15137{
15138 c89atomic_if32 r;
15139 c89atomic_if32 x;
15140 x.f = src;
15141 r.i = c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, x.i, order);
15142 return r.f;
15143}
15144static C89ATOMIC_INLINE double c89atomic_exchange_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order)
15145{
15146 c89atomic_if64 r;
15147 c89atomic_if64 x;
15148 x.f = src;
15149 r.i = c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, x.i, order);
15150 return r.f;
15151}
15152#define c89atomic_clear_f32(ptr) (float )c89atomic_clear_explicit_f32(ptr, c89atomic_memory_order_seq_cst)
15153#define c89atomic_clear_f64(ptr) (double)c89atomic_clear_explicit_f64(ptr, c89atomic_memory_order_seq_cst)
15154#define c89atomic_store_f32(dst, src) c89atomic_store_explicit_f32(dst, src, c89atomic_memory_order_seq_cst)
15155#define c89atomic_store_f64(dst, src) c89atomic_store_explicit_f64(dst, src, c89atomic_memory_order_seq_cst)
15156#define c89atomic_load_f32(ptr) (float )c89atomic_load_explicit_f32(ptr, c89atomic_memory_order_seq_cst)
15157#define c89atomic_load_f64(ptr) (double)c89atomic_load_explicit_f64(ptr, c89atomic_memory_order_seq_cst)
15158#define c89atomic_exchange_f32(dst, src) (float )c89atomic_exchange_explicit_f32(dst, src, c89atomic_memory_order_seq_cst)
15159#define c89atomic_exchange_f64(dst, src) (double)c89atomic_exchange_explicit_f64(dst, src, c89atomic_memory_order_seq_cst)
15160typedef c89atomic_flag c89atomic_spinlock;
15161static C89ATOMIC_INLINE void c89atomic_spinlock_lock(volatile c89atomic_spinlock* pSpinlock)
15162{
15163 for (;;) {
15164 if (c89atomic_flag_test_and_set_explicit(pSpinlock, c89atomic_memory_order_acquire) == 0) {
15165 break;
15166 }
15167 while (c89atoimc_flag_load_explicit(pSpinlock, c89atomic_memory_order_relaxed) == 1) {
15168 }
15169 }
15170}
15171static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlock* pSpinlock)
15172{
15173 c89atomic_flag_clear_explicit(pSpinlock, c89atomic_memory_order_release);
15174}
15175#if defined(__cplusplus)
15176}
15177#endif
15178#endif
15179/* c89atomic.h end */
15180
15181
15182
15183MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn)
15184{
15185 /* This is based on the calculation in ma_linear_resampler_get_expected_output_frame_count(). */
15186 ma_uint64 outputFrameCount;
15187 ma_uint64 preliminaryInputFrameCountFromFrac;
15188 ma_uint64 preliminaryInputFrameCount;
15189
15190 if (sampleRateIn == 0 || sampleRateOut == 0 || frameCountIn == 0) {
15191 return 0;
15192 }
15193
15194 if (sampleRateOut == sampleRateIn) {
15195 return frameCountIn;
15196 }
15197
15198 outputFrameCount = (frameCountIn * sampleRateOut) / sampleRateIn;
15199
15200 preliminaryInputFrameCountFromFrac = (outputFrameCount * (sampleRateIn / sampleRateOut)) / sampleRateOut;
15201 preliminaryInputFrameCount = (outputFrameCount * (sampleRateIn % sampleRateOut)) + preliminaryInputFrameCountFromFrac;
15202
15203 if (preliminaryInputFrameCount <= frameCountIn) {
15204 outputFrameCount += 1;
15205 }
15206
15207 return outputFrameCount;
15208}
15209
15210#ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE
15211#define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096
15212#endif
15213
15214
15215
15216#if defined(MA_WIN32)
15217static ma_result ma_result_from_GetLastError(DWORD error)
15218{
15219 switch (error)
15220 {
15221 case ERROR_SUCCESS: return MA_SUCCESS;
15222 case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST;
15223 case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES;
15224 case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY;
15225 case ERROR_DISK_FULL: return MA_NO_SPACE;
15226 case ERROR_HANDLE_EOF: return MA_AT_END;
15227 case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK;
15228 case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS;
15229 case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED;
15230 case ERROR_SEM_TIMEOUT: return MA_TIMEOUT;
15231 case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST;
15232 default: break;
15233 }
15234
15235 return MA_ERROR;
15236}
15237#endif /* MA_WIN32 */
15238
15239
15240
15245static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield)
15246{
15247 if (pSpinlock == NULL) {
15248 return MA_INVALID_ARGS;
15249 }
15250
15251 for (;;) {
15252 if (c89atomic_exchange_explicit_32(pSpinlock, 1, c89atomic_memory_order_acquire) == 0) {
15253 break;
15254 }
15255
15256 while (c89atomic_load_explicit_32(pSpinlock, c89atomic_memory_order_relaxed) == 1) {
15257 if (yield) {
15258 ma_yield();
15259 }
15260 }
15261 }
15262
15263 return MA_SUCCESS;
15264}
15265
15266MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock)
15267{
15268 return ma_spinlock_lock_ex(pSpinlock, MA_TRUE);
15269}
15270
15272{
15273 return ma_spinlock_lock_ex(pSpinlock, MA_FALSE);
15274}
15275
15277{
15278 if (pSpinlock == NULL) {
15279 return MA_INVALID_ARGS;
15280 }
15281
15282 c89atomic_store_explicit_32(pSpinlock, 0, c89atomic_memory_order_release);
15283 return MA_SUCCESS;
15284}
15285
15286
15287#ifndef MA_NO_THREADING
15288#ifdef MA_WIN32
15289 #define MA_THREADCALL WINAPI
15290 typedef unsigned long ma_thread_result;
15291#else
15292 #define MA_THREADCALL
15293 typedef void* ma_thread_result;
15294#endif
15295typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData);
15296
15297#ifdef MA_WIN32
15298static int ma_thread_priority_to_win32(ma_thread_priority priority)
15299{
15300 switch (priority) {
15301 case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE;
15302 case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST;
15303 case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL;
15304 case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL;
15305 case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL;
15306 case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST;
15307 case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL;
15308 default: return THREAD_PRIORITY_NORMAL;
15309 }
15310}
15311
15312static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)
15313{
15314 *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, NULL);
15315 if (*pThread == NULL) {
15316 return ma_result_from_GetLastError(GetLastError());
15317 }
15318
15319 SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority));
15320
15321 return MA_SUCCESS;
15322}
15323
15324static void ma_thread_wait__win32(ma_thread* pThread)
15325{
15326 WaitForSingleObject((HANDLE)*pThread, INFINITE);
15327 CloseHandle((HANDLE)*pThread);
15328}
15329
15330
15331static ma_result ma_mutex_init__win32(ma_mutex* pMutex)
15332{
15333 *pMutex = CreateEventW(NULL, FALSE, TRUE, NULL);
15334 if (*pMutex == NULL) {
15335 return ma_result_from_GetLastError(GetLastError());
15336 }
15337
15338 return MA_SUCCESS;
15339}
15340
15341static void ma_mutex_uninit__win32(ma_mutex* pMutex)
15342{
15343 CloseHandle((HANDLE)*pMutex);
15344}
15345
15346static void ma_mutex_lock__win32(ma_mutex* pMutex)
15347{
15348 WaitForSingleObject((HANDLE)*pMutex, INFINITE);
15349}
15350
15351static void ma_mutex_unlock__win32(ma_mutex* pMutex)
15352{
15353 SetEvent((HANDLE)*pMutex);
15354}
15355
15356
15357static ma_result ma_event_init__win32(ma_event* pEvent)
15358{
15359 *pEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
15360 if (*pEvent == NULL) {
15361 return ma_result_from_GetLastError(GetLastError());
15362 }
15363
15364 return MA_SUCCESS;
15365}
15366
15367static void ma_event_uninit__win32(ma_event* pEvent)
15368{
15369 CloseHandle((HANDLE)*pEvent);
15370}
15371
15372static ma_result ma_event_wait__win32(ma_event* pEvent)
15373{
15374 DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE);
15375 if (result == WAIT_OBJECT_0) {
15376 return MA_SUCCESS;
15377 }
15378
15379 if (result == WAIT_TIMEOUT) {
15380 return MA_TIMEOUT;
15381 }
15382
15383 return ma_result_from_GetLastError(GetLastError());
15384}
15385
15386static ma_result ma_event_signal__win32(ma_event* pEvent)
15387{
15388 BOOL result = SetEvent((HANDLE)*pEvent);
15389 if (result == 0) {
15390 return ma_result_from_GetLastError(GetLastError());
15391 }
15392
15393 return MA_SUCCESS;
15394}
15395
15396
15397static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore)
15398{
15399 *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL);
15400 if (*pSemaphore == NULL) {
15401 return ma_result_from_GetLastError(GetLastError());
15402 }
15403
15404 return MA_SUCCESS;
15405}
15406
15407static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore)
15408{
15409 CloseHandle((HANDLE)*pSemaphore);
15410}
15411
15412static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore)
15413{
15414 DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE);
15415 if (result == WAIT_OBJECT_0) {
15416 return MA_SUCCESS;
15417 }
15418
15419 if (result == WAIT_TIMEOUT) {
15420 return MA_TIMEOUT;
15421 }
15422
15423 return ma_result_from_GetLastError(GetLastError());
15424}
15425
15426static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore)
15427{
15428 BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL);
15429 if (result == 0) {
15430 return ma_result_from_GetLastError(GetLastError());
15431 }
15432
15433 return MA_SUCCESS;
15434}
15435#endif
15436
15437
15438#ifdef MA_POSIX
15439static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)
15440{
15441 int result;
15442 pthread_attr_t* pAttr = NULL;
15443
15444#if !defined(__EMSCRIPTEN__)
15445 /* Try setting the thread priority. It's not critical if anything fails here. */
15446 pthread_attr_t attr;
15447 if (pthread_attr_init(&attr) == 0) {
15448 int scheduler = -1;
15449 if (priority == ma_thread_priority_idle) {
15450#ifdef SCHED_IDLE
15451 if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) {
15452 scheduler = SCHED_IDLE;
15453 }
15454#endif
15455 } else if (priority == ma_thread_priority_realtime) {
15456#ifdef SCHED_FIFO
15457 if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) {
15458 scheduler = SCHED_FIFO;
15459 }
15460#endif
15461#ifdef MA_LINUX
15462 } else {
15463 scheduler = sched_getscheduler(0);
15464#endif
15465 }
15466
15467 if (stackSize > 0) {
15468 pthread_attr_setstacksize(&attr, stackSize);
15469 }
15470
15471 if (scheduler != -1) {
15472 int priorityMin = sched_get_priority_min(scheduler);
15473 int priorityMax = sched_get_priority_max(scheduler);
15474 int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */
15475
15476 struct sched_param sched;
15477 if (pthread_attr_getschedparam(&attr, &sched) == 0) {
15478 if (priority == ma_thread_priority_idle) {
15479 sched.sched_priority = priorityMin;
15480 } else if (priority == ma_thread_priority_realtime) {
15481 sched.sched_priority = priorityMax;
15482 } else {
15483 sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */
15484 if (sched.sched_priority < priorityMin) {
15485 sched.sched_priority = priorityMin;
15486 }
15487 if (sched.sched_priority > priorityMax) {
15488 sched.sched_priority = priorityMax;
15489 }
15490 }
15491
15492 if (pthread_attr_setschedparam(&attr, &sched) == 0) {
15493 pAttr = &attr;
15494 }
15495 }
15496 }
15497 }
15498#else
15499 /* It's the emscripten build. We'll have a few unused parameters. */
15500 (void)priority;
15501 (void)stackSize;
15502#endif
15503
15504 result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData);
15505
15506 /* The thread attributes object is no longer required. */
15507 if (pAttr != NULL) {
15508 pthread_attr_destroy(pAttr);
15509 }
15510
15511 if (result != 0) {
15512 return ma_result_from_errno(result);
15513 }
15514
15515 return MA_SUCCESS;
15516}
15517
15518static void ma_thread_wait__posix(ma_thread* pThread)
15519{
15520 pthread_join((pthread_t)*pThread, NULL);
15521}
15522
15523
15524static ma_result ma_mutex_init__posix(ma_mutex* pMutex)
15525{
15526 int result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL);
15527 if (result != 0) {
15528 return ma_result_from_errno(result);
15529 }
15530
15531 return MA_SUCCESS;
15532}
15533
15534static void ma_mutex_uninit__posix(ma_mutex* pMutex)
15535{
15536 pthread_mutex_destroy((pthread_mutex_t*)pMutex);
15537}
15538
15539static void ma_mutex_lock__posix(ma_mutex* pMutex)
15540{
15541 pthread_mutex_lock((pthread_mutex_t*)pMutex);
15542}
15543
15544static void ma_mutex_unlock__posix(ma_mutex* pMutex)
15545{
15546 pthread_mutex_unlock((pthread_mutex_t*)pMutex);
15547}
15548
15549
15550static ma_result ma_event_init__posix(ma_event* pEvent)
15551{
15552 int result;
15553
15554 result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL);
15555 if (result != 0) {
15556 return ma_result_from_errno(result);
15557 }
15558
15559 result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL);
15560 if (result != 0) {
15561 pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock);
15562 return ma_result_from_errno(result);
15563 }
15564
15565 pEvent->value = 0;
15566 return MA_SUCCESS;
15567}
15568
15569static void ma_event_uninit__posix(ma_event* pEvent)
15570{
15571 pthread_cond_destroy((pthread_cond_t*)&pEvent->cond);
15572 pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock);
15573}
15574
15575static ma_result ma_event_wait__posix(ma_event* pEvent)
15576{
15577 pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock);
15578 {
15579 while (pEvent->value == 0) {
15580 pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock);
15581 }
15582 pEvent->value = 0; /* Auto-reset. */
15583 }
15584 pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock);
15585
15586 return MA_SUCCESS;
15587}
15588
15589static ma_result ma_event_signal__posix(ma_event* pEvent)
15590{
15591 pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock);
15592 {
15593 pEvent->value = 1;
15594 pthread_cond_signal((pthread_cond_t*)&pEvent->cond);
15595 }
15596 pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock);
15597
15598 return MA_SUCCESS;
15599}
15600
15601
15602static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore)
15603{
15604 int result;
15605
15606 if (pSemaphore == NULL) {
15607 return MA_INVALID_ARGS;
15608 }
15609
15610 pSemaphore->value = initialValue;
15611
15612 result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL);
15613 if (result != 0) {
15614 return ma_result_from_errno(result); /* Failed to create mutex. */
15615 }
15616
15617 result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL);
15618 if (result != 0) {
15619 pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock);
15620 return ma_result_from_errno(result); /* Failed to create condition variable. */
15621 }
15622
15623 return MA_SUCCESS;
15624}
15625
15626static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore)
15627{
15628 if (pSemaphore == NULL) {
15629 return;
15630 }
15631
15632 pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond);
15633 pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock);
15634}
15635
15636static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore)
15637{
15638 if (pSemaphore == NULL) {
15639 return MA_INVALID_ARGS;
15640 }
15641
15642 pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock);
15643 {
15644 /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */
15645 while (pSemaphore->value == 0) {
15646 pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock);
15647 }
15648
15649 pSemaphore->value -= 1;
15650 }
15651 pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock);
15652
15653 return MA_SUCCESS;
15654}
15655
15656static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore)
15657{
15658 if (pSemaphore == NULL) {
15659 return MA_INVALID_ARGS;
15660 }
15661
15662 pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock);
15663 {
15664 pSemaphore->value += 1;
15665 pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond);
15666 }
15667 pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock);
15668
15669 return MA_SUCCESS;
15670}
15671#endif
15672
15673typedef struct
15674{
15675 ma_thread_entry_proc entryProc;
15676 void* pData;
15677 ma_allocation_callbacks allocationCallbacks;
15678} ma_thread_proxy_data;
15679
15680static ma_thread_result MA_THREADCALL ma_thread_entry_proxy(void* pData)
15681{
15682 ma_thread_proxy_data* pProxyData = (ma_thread_proxy_data*)pData;
15683 ma_thread_entry_proc entryProc;
15684 void* pEntryProcData;
15685 ma_thread_result result;
15686
15687 #if defined(MA_ON_THREAD_ENTRY)
15688 MA_ON_THREAD_ENTRY
15689 #endif
15690
15691 entryProc = pProxyData->entryProc;
15692 pEntryProcData = pProxyData->pData;
15693
15694 /* Free the proxy data before getting into the real thread entry proc. */
15695 ma_free(pProxyData, &pProxyData->allocationCallbacks);
15696
15697 result = entryProc(pEntryProcData);
15698
15699 #if defined(MA_ON_THREAD_EXIT)
15700 MA_ON_THREAD_EXIT
15701 #endif
15702
15703 return result;
15704}
15705
15706static 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)
15707{
15708 ma_result result;
15709 ma_thread_proxy_data* pProxyData;
15710
15711 if (pThread == NULL || entryProc == NULL) {
15712 return MA_INVALID_ARGS;
15713 }
15714
15715 pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks); /* Will be freed by the proxy entry proc. */
15716 if (pProxyData == NULL) {
15717 return MA_OUT_OF_MEMORY;
15718 }
15719
15720 pProxyData->entryProc = entryProc;
15721 pProxyData->pData = pData;
15722 ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks);
15723
15724#ifdef MA_WIN32
15725 result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData);
15726#endif
15727#ifdef MA_POSIX
15728 result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData);
15729#endif
15730
15731 if (result != MA_SUCCESS) {
15732 ma_free(pProxyData, pAllocationCallbacks);
15733 return result;
15734 }
15735
15736 return MA_SUCCESS;
15737}
15738
15739static void ma_thread_wait(ma_thread* pThread)
15740{
15741 if (pThread == NULL) {
15742 return;
15743 }
15744
15745#ifdef MA_WIN32
15746 ma_thread_wait__win32(pThread);
15747#endif
15748#ifdef MA_POSIX
15749 ma_thread_wait__posix(pThread);
15750#endif
15751}
15752
15753
15755{
15756 if (pMutex == NULL) {
15757 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
15758 return MA_INVALID_ARGS;
15759 }
15760
15761#ifdef MA_WIN32
15762 return ma_mutex_init__win32(pMutex);
15763#endif
15764#ifdef MA_POSIX
15765 return ma_mutex_init__posix(pMutex);
15766#endif
15767}
15768
15769MA_API void ma_mutex_uninit(ma_mutex* pMutex)
15770{
15771 if (pMutex == NULL) {
15772 return;
15773 }
15774
15775#ifdef MA_WIN32
15776 ma_mutex_uninit__win32(pMutex);
15777#endif
15778#ifdef MA_POSIX
15779 ma_mutex_uninit__posix(pMutex);
15780#endif
15781}
15782
15783MA_API void ma_mutex_lock(ma_mutex* pMutex)
15784{
15785 if (pMutex == NULL) {
15786 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
15787 return;
15788 }
15789
15790#ifdef MA_WIN32
15791 ma_mutex_lock__win32(pMutex);
15792#endif
15793#ifdef MA_POSIX
15794 ma_mutex_lock__posix(pMutex);
15795#endif
15796}
15797
15798MA_API void ma_mutex_unlock(ma_mutex* pMutex)
15799{
15800 if (pMutex == NULL) {
15801 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
15802 return;
15803}
15804
15805#ifdef MA_WIN32
15806 ma_mutex_unlock__win32(pMutex);
15807#endif
15808#ifdef MA_POSIX
15809 ma_mutex_unlock__posix(pMutex);
15810#endif
15811}
15812
15813
15815{
15816 if (pEvent == NULL) {
15817 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
15818 return MA_INVALID_ARGS;
15819 }
15820
15821#ifdef MA_WIN32
15822 return ma_event_init__win32(pEvent);
15823#endif
15824#ifdef MA_POSIX
15825 return ma_event_init__posix(pEvent);
15826#endif
15827}
15828
15829#if 0
15830static ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callbacks* pAllocationCallbacks)
15831{
15832 ma_result result;
15833 ma_event* pEvent;
15834
15835 if (ppEvent == NULL) {
15836 return MA_INVALID_ARGS;
15837 }
15838
15839 *ppEvent = NULL;
15840
15841 pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks);
15842 if (pEvent == NULL) {
15843 return MA_OUT_OF_MEMORY;
15844 }
15845
15846 result = ma_event_init(pEvent);
15847 if (result != MA_SUCCESS) {
15848 ma_free(pEvent, pAllocationCallbacks);
15849 return result;
15850 }
15851
15852 *ppEvent = pEvent;
15853 return result;
15854}
15855#endif
15856
15857MA_API void ma_event_uninit(ma_event* pEvent)
15858{
15859 if (pEvent == NULL) {
15860 return;
15861 }
15862
15863#ifdef MA_WIN32
15864 ma_event_uninit__win32(pEvent);
15865#endif
15866#ifdef MA_POSIX
15867 ma_event_uninit__posix(pEvent);
15868#endif
15869}
15870
15871#if 0
15872static void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* pAllocationCallbacks)
15873{
15874 if (pEvent == NULL) {
15875 return;
15876 }
15877
15878 ma_event_uninit(pEvent);
15879 ma_free(pEvent, pAllocationCallbacks);
15880}
15881#endif
15882
15884{
15885 if (pEvent == NULL) {
15886 MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */
15887 return MA_INVALID_ARGS;
15888 }
15889
15890#ifdef MA_WIN32
15891 return ma_event_wait__win32(pEvent);
15892#endif
15893#ifdef MA_POSIX
15894 return ma_event_wait__posix(pEvent);
15895#endif
15896}
15897
15899{
15900 if (pEvent == NULL) {
15901 MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */
15902 return MA_INVALID_ARGS;
15903 }
15904
15905#ifdef MA_WIN32
15906 return ma_event_signal__win32(pEvent);
15907#endif
15908#ifdef MA_POSIX
15909 return ma_event_signal__posix(pEvent);
15910#endif
15911}
15912
15913
15914MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore)
15915{
15916 if (pSemaphore == NULL) {
15917 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
15918 return MA_INVALID_ARGS;
15919 }
15920
15921#ifdef MA_WIN32
15922 return ma_semaphore_init__win32(initialValue, pSemaphore);
15923#endif
15924#ifdef MA_POSIX
15925 return ma_semaphore_init__posix(initialValue, pSemaphore);
15926#endif
15927}
15928
15929MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore)
15930{
15931 if (pSemaphore == NULL) {
15932 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
15933 return;
15934 }
15935
15936#ifdef MA_WIN32
15937 ma_semaphore_uninit__win32(pSemaphore);
15938#endif
15939#ifdef MA_POSIX
15940 ma_semaphore_uninit__posix(pSemaphore);
15941#endif
15942}
15943
15944MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore)
15945{
15946 if (pSemaphore == NULL) {
15947 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
15948 return MA_INVALID_ARGS;
15949 }
15950
15951#ifdef MA_WIN32
15952 return ma_semaphore_wait__win32(pSemaphore);
15953#endif
15954#ifdef MA_POSIX
15955 return ma_semaphore_wait__posix(pSemaphore);
15956#endif
15957}
15958
15959MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore)
15960{
15961 if (pSemaphore == NULL) {
15962 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
15963 return MA_INVALID_ARGS;
15964 }
15965
15966#ifdef MA_WIN32
15967 return ma_semaphore_release__win32(pSemaphore);
15968#endif
15969#ifdef MA_POSIX
15970 return ma_semaphore_release__posix(pSemaphore);
15971#endif
15972}
15973#else
15974/* 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. */
15975#ifndef MA_NO_DEVICE_IO
15976#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO";
15977#endif
15978#endif /* MA_NO_THREADING */
15979
15980
15981
15982#define MA_FENCE_COUNTER_MAX 0x7FFFFFFF
15983
15985{
15986 if (pFence == NULL) {
15987 return MA_INVALID_ARGS;
15988 }
15989
15990 MA_ZERO_OBJECT(pFence);
15991 pFence->counter = 0;
15992
15993 #ifndef MA_NO_THREADING
15994 {
15995 ma_result result;
15996
15997 result = ma_event_init(&pFence->e);
15998 if (result != MA_SUCCESS) {
15999 return result;
16000 }
16001 }
16002 #endif
16003
16004 return MA_SUCCESS;
16005}
16006
16007MA_API void ma_fence_uninit(ma_fence* pFence)
16008{
16009 if (pFence == NULL) {
16010 return;
16011 }
16012
16013 #ifndef MA_NO_THREADING
16014 {
16015 ma_event_uninit(&pFence->e);
16016 }
16017 #endif
16018
16019 MA_ZERO_OBJECT(pFence);
16020}
16021
16023{
16024 if (pFence == NULL) {
16025 return MA_INVALID_ARGS;
16026 }
16027
16028 for (;;) {
16029 ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter);
16030 ma_uint32 newCounter = oldCounter + 1;
16031
16032 /* Make sure we're not about to exceed our maximum value. */
16033 if (newCounter > MA_FENCE_COUNTER_MAX) {
16034 MA_ASSERT(MA_FALSE);
16035 return MA_OUT_OF_RANGE;
16036 }
16037
16038 if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) {
16039 return MA_SUCCESS;
16040 } else {
16041 if (oldCounter == MA_FENCE_COUNTER_MAX) {
16042 MA_ASSERT(MA_FALSE);
16043 return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */
16044 }
16045 }
16046 }
16047
16048 /* Should never get here. */
16049 /*return MA_SUCCESS;*/
16050}
16051
16053{
16054 if (pFence == NULL) {
16055 return MA_INVALID_ARGS;
16056 }
16057
16058 for (;;) {
16059 ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter);
16060 ma_uint32 newCounter = oldCounter - 1;
16061
16062 if (oldCounter == 0) {
16063 MA_ASSERT(MA_FALSE);
16064 return MA_INVALID_OPERATION; /* Acquire/release mismatch. */
16065 }
16066
16067 if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) {
16068 #ifndef MA_NO_THREADING
16069 {
16070 if (newCounter == 0) {
16071 ma_event_signal(&pFence->e); /* <-- ma_fence_wait() will be waiting on this. */
16072 }
16073 }
16074 #endif
16075
16076 return MA_SUCCESS;
16077 } else {
16078 if (oldCounter == 0) {
16079 MA_ASSERT(MA_FALSE);
16080 return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */
16081 }
16082 }
16083 }
16084
16085 /* Should never get here. */
16086 /*return MA_SUCCESS;*/
16087}
16088
16090{
16091 if (pFence == NULL) {
16092 return MA_INVALID_ARGS;
16093 }
16094
16095 for (;;) {
16096 ma_uint32 counter;
16097
16098 counter = c89atomic_load_32(&pFence->counter);
16099 if (counter == 0) {
16100 /*
16101 Counter has hit zero. By the time we get here some other thread may have acquired the
16102 fence again, but that is where the caller needs to take care with how they se the fence.
16103 */
16104 return MA_SUCCESS;
16105 }
16106
16107 /* Getting here means the counter is > 0. We'll need to wait for something to happen. */
16108 #ifndef MA_NO_THREADING
16109 {
16110 ma_result result;
16111
16112 result = ma_event_wait(&pFence->e);
16113 if (result != MA_SUCCESS) {
16114 return result;
16115 }
16116 }
16117 #endif
16118 }
16119
16120 /* Should never get here. */
16121 /*return MA_INVALID_OPERATION;*/
16122}
16123
16124
16126{
16127 ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification;
16128
16129 if (pNotification == NULL) {
16130 return MA_INVALID_ARGS;
16131 }
16132
16133 if (pNotificationCallbacks->onSignal == NULL) {
16134 return MA_NOT_IMPLEMENTED;
16135 }
16136
16137 pNotificationCallbacks->onSignal(pNotification);
16138 return MA_INVALID_ARGS;
16139}
16140
16141
16142static void ma_async_notification_poll__on_signal(ma_async_notification* pNotification)
16143{
16144 ((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE;
16145}
16146
16148{
16149 if (pNotificationPoll == NULL) {
16150 return MA_INVALID_ARGS;
16151 }
16152
16153 pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal;
16154 pNotificationPoll->signalled = MA_FALSE;
16155
16156 return MA_SUCCESS;
16157}
16158
16160{
16161 if (pNotificationPoll == NULL) {
16162 return MA_FALSE;
16163 }
16164
16165 return pNotificationPoll->signalled;
16166}
16167
16168
16169static void ma_async_notification_event__on_signal(ma_async_notification* pNotification)
16170{
16172}
16173
16175{
16176 if (pNotificationEvent == NULL) {
16177 return MA_INVALID_ARGS;
16178 }
16179
16180 pNotificationEvent->cb.onSignal = ma_async_notification_event__on_signal;
16181
16182 #ifndef MA_NO_THREADING
16183 {
16184 ma_result result;
16185
16186 result = ma_event_init(&pNotificationEvent->e);
16187 if (result != MA_SUCCESS) {
16188 return result;
16189 }
16190
16191 return MA_SUCCESS;
16192 }
16193 #else
16194 {
16195 return MA_NOT_IMPLEMENTED; /* Threading is disabled. */
16196 }
16197 #endif
16198}
16199
16201{
16202 if (pNotificationEvent == NULL) {
16203 return MA_INVALID_ARGS;
16204 }
16205
16206 #ifndef MA_NO_THREADING
16207 {
16208 ma_event_uninit(&pNotificationEvent->e);
16209 return MA_SUCCESS;
16210 }
16211 #else
16212 {
16213 return MA_NOT_IMPLEMENTED; /* Threading is disabled. */
16214 }
16215 #endif
16216}
16217
16219{
16220 if (pNotificationEvent == NULL) {
16221 return MA_INVALID_ARGS;
16222 }
16223
16224 #ifndef MA_NO_THREADING
16225 {
16226 return ma_event_wait(&pNotificationEvent->e);
16227 }
16228 #else
16229 {
16230 return MA_NOT_IMPLEMENTED; /* Threading is disabled. */
16231 }
16232 #endif
16233}
16234
16236{
16237 if (pNotificationEvent == NULL) {
16238 return MA_INVALID_ARGS;
16239 }
16240
16241 #ifndef MA_NO_THREADING
16242 {
16243 return ma_event_signal(&pNotificationEvent->e);
16244 }
16245 #else
16246 {
16247 return MA_NOT_IMPLEMENTED; /* Threading is disabled. */
16248 }
16249 #endif
16250}
16251
16252
16253
16254
16260{
16262
16263 MA_ZERO_OBJECT(&config);
16264 config.capacity = capacity;
16265
16266 return config;
16267}
16268
16269
16270static MA_INLINE ma_uint32 ma_slot_allocator_calculate_group_capacity(ma_uint32 slotCapacity)
16271{
16272 ma_uint32 cap = slotCapacity / 32;
16273 if ((slotCapacity % 32) != 0) {
16274 cap += 1;
16275 }
16276
16277 return cap;
16278}
16279
16280static MA_INLINE ma_uint32 ma_slot_allocator_group_capacity(const ma_slot_allocator* pAllocator)
16281{
16282 return ma_slot_allocator_calculate_group_capacity(pAllocator->capacity);
16283}
16284
16285
16286typedef struct
16287{
16288 size_t sizeInBytes;
16289 size_t groupsOffset;
16290 size_t slotsOffset;
16291} ma_slot_allocator_heap_layout;
16292
16293static ma_result ma_slot_allocator_get_heap_layout(const ma_slot_allocator_config* pConfig, ma_slot_allocator_heap_layout* pHeapLayout)
16294{
16295 MA_ASSERT(pHeapLayout != NULL);
16296
16297 MA_ZERO_OBJECT(pHeapLayout);
16298
16299 if (pConfig == NULL) {
16300 return MA_INVALID_ARGS;
16301 }
16302
16303 if (pConfig->capacity == 0) {
16304 return MA_INVALID_ARGS;
16305 }
16306
16307 pHeapLayout->sizeInBytes = 0;
16308
16309 /* Groups. */
16310 pHeapLayout->groupsOffset = pHeapLayout->sizeInBytes;
16311 pHeapLayout->sizeInBytes += ma_align_64(ma_slot_allocator_calculate_group_capacity(pConfig->capacity) * sizeof(ma_slot_allocator_group));
16312
16313 /* Slots. */
16314 pHeapLayout->slotsOffset = pHeapLayout->sizeInBytes;
16315 pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_uint32));
16316
16317 return MA_SUCCESS;
16318}
16319
16320MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes)
16321{
16322 ma_result result;
16323 ma_slot_allocator_heap_layout layout;
16324
16325 if (pHeapSizeInBytes == NULL) {
16326 return MA_INVALID_ARGS;
16327 }
16328
16329 *pHeapSizeInBytes = 0;
16330
16331 result = ma_slot_allocator_get_heap_layout(pConfig, &layout);
16332 if (result != MA_SUCCESS) {
16333 return result;
16334 }
16335
16336 *pHeapSizeInBytes = layout.sizeInBytes;
16337
16338 return result;
16339}
16340
16342{
16343 ma_result result;
16344 ma_slot_allocator_heap_layout heapLayout;
16345
16346 if (pAllocator == NULL) {
16347 return MA_INVALID_ARGS;
16348 }
16349
16350 MA_ZERO_OBJECT(pAllocator);
16351
16352 if (pHeap == NULL) {
16353 return MA_INVALID_ARGS;
16354 }
16355
16356 result = ma_slot_allocator_get_heap_layout(pConfig, &heapLayout);
16357 if (result != MA_SUCCESS) {
16358 return result;
16359 }
16360
16361 pAllocator->_pHeap = pHeap;
16362 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
16363
16364 pAllocator->pGroups = (ma_slot_allocator_group*)ma_offset_ptr(pHeap, heapLayout.groupsOffset);
16365 pAllocator->pSlots = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.slotsOffset);
16366 pAllocator->capacity = pConfig->capacity;
16367
16368 return MA_SUCCESS;
16369}
16370
16371MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator)
16372{
16373 ma_result result;
16374 size_t heapSizeInBytes;
16375 void* pHeap;
16376
16377 result = ma_slot_allocator_get_heap_size(pConfig, &heapSizeInBytes);
16378 if (result != MA_SUCCESS) {
16379 return result; /* Failed to retrieve the size of the heap allocation. */
16380 }
16381
16382 if (heapSizeInBytes > 0) {
16383 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
16384 if (pHeap == NULL) {
16385 return MA_OUT_OF_MEMORY;
16386 }
16387 } else {
16388 pHeap = NULL;
16389 }
16390
16391 result = ma_slot_allocator_init_preallocated(pConfig, pHeap, pAllocator);
16392 if (result != MA_SUCCESS) {
16393 ma_free(pHeap, pAllocationCallbacks);
16394 return result;
16395 }
16396
16397 pAllocator->_ownsHeap = MA_TRUE;
16398 return MA_SUCCESS;
16399}
16400
16401MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks)
16402{
16403 if (pAllocator == NULL) {
16404 return;
16405 }
16406
16407 if (pAllocator->_ownsHeap) {
16408 ma_free(pAllocator->_pHeap, pAllocationCallbacks);
16409 }
16410}
16411
16413{
16414 ma_uint32 iAttempt;
16415 const ma_uint32 maxAttempts = 2; /* The number of iterations to perform until returning MA_OUT_OF_MEMORY if no slots can be found. */
16416
16417 if (pAllocator == NULL || pSlot == NULL) {
16418 return MA_INVALID_ARGS;
16419 }
16420
16421 for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) {
16422 /* We need to acquire a suitable bitfield first. This is a bitfield that's got an available slot within it. */
16423 ma_uint32 iGroup;
16424 for (iGroup = 0; iGroup < ma_slot_allocator_group_capacity(pAllocator); iGroup += 1) {
16425 /* CAS */
16426 for (;;) {
16427 ma_uint32 oldBitfield;
16428 ma_uint32 newBitfield;
16429 ma_uint32 bitOffset;
16430
16431 oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */
16432
16433 /* Fast check to see if anything is available. */
16434 if (oldBitfield == 0xFFFFFFFF) {
16435 break; /* No available bits in this bitfield. */
16436 }
16437
16438 bitOffset = ma_ffs_32(~oldBitfield);
16439 MA_ASSERT(bitOffset < 32);
16440
16441 newBitfield = oldBitfield | (1 << bitOffset);
16442
16443 if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) {
16444 ma_uint32 slotIndex;
16445
16446 /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */
16447 c89atomic_fetch_add_32(&pAllocator->count, 1);
16448
16449 /* The slot index is required for constructing the output value. */
16450 slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */
16451 if (slotIndex >= pAllocator->capacity) {
16452 return MA_OUT_OF_MEMORY;
16453 }
16454
16455 /* Increment the reference count before constructing the output value. */
16456 pAllocator->pSlots[slotIndex] += 1;
16457
16458 /* Construct the output value. */
16459 *pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex);
16460
16461 return MA_SUCCESS;
16462 }
16463 }
16464 }
16465
16466 /* 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. */
16467 if (pAllocator->count < pAllocator->capacity) {
16468 ma_yield();
16469 } else {
16470 return MA_OUT_OF_MEMORY;
16471 }
16472 }
16473
16474 /* We couldn't find a slot within the maximum number of attempts. */
16475 return MA_OUT_OF_MEMORY;
16476}
16477
16479{
16480 ma_uint32 iGroup;
16481 ma_uint32 iBit;
16482
16483 if (pAllocator == NULL) {
16484 return MA_INVALID_ARGS;
16485 }
16486
16487 iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5); /* slot / 32 */
16488 iBit = (ma_uint32)((slot & 0xFFFFFFFF) & 31); /* slot % 32 */
16489
16490 if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) {
16491 return MA_INVALID_ARGS;
16492 }
16493
16494 MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */
16495
16496 while (c89atomic_load_32(&pAllocator->count) > 0) {
16497 /* CAS */
16498 ma_uint32 oldBitfield;
16499 ma_uint32 newBitfield;
16500
16501 oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */
16502 newBitfield = oldBitfield & ~(1 << iBit);
16503
16504 /* Debugging for checking for double-frees. */
16505 #if defined(MA_DEBUG_OUTPUT)
16506 {
16507 if ((oldBitfield & (1 << iBit)) == 0) {
16508 MA_ASSERT(MA_FALSE); /* Double free detected.*/
16509 }
16510 }
16511 #endif
16512
16513 if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) {
16514 c89atomic_fetch_sub_32(&pAllocator->count, 1);
16515 return MA_SUCCESS;
16516 }
16517 }
16518
16519 /* Getting here means there are no allocations available for freeing. */
16520 return MA_INVALID_OPERATION;
16521}
16522
16523
16524#define MA_JOB_ID_NONE ~((ma_uint64)0)
16525#define MA_JOB_SLOT_NONE (ma_uint16)(~0)
16526
16527static MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc)
16528{
16529 return (ma_uint32)(toc >> 32);
16530}
16531
16532static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc)
16533{
16534 return (ma_uint16)(toc & 0x0000FFFF);
16535}
16536
16537static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc)
16538{
16539 return (ma_uint16)((toc & 0xFFFF0000) >> 16);
16540}
16541
16542static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc)
16543{
16544 return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc);
16545}
16546
16547static MA_INLINE ma_uint64 ma_job_set_refcount(ma_uint64 toc, ma_uint32 refcount)
16548{
16549 /* Clear the reference count first. */
16550 toc = toc & ~((ma_uint64)0xFFFFFFFF << 32);
16551 toc = toc | ((ma_uint64)refcount << 32);
16552
16553 return toc;
16554}
16555
16556
16558{
16559 ma_job job;
16560
16561 MA_ZERO_OBJECT(&job);
16562 job.toc.breakup.code = code;
16563 job.toc.breakup.slot = MA_JOB_SLOT_NONE; /* Temp value. Will be allocated when posted to a queue. */
16564 job.next = MA_JOB_ID_NONE;
16565
16566 return job;
16567}
16568
16569
16570static ma_result ma_job_process__noop(ma_job* pJob);
16571static ma_result ma_job_process__quit(ma_job* pJob);
16572static ma_result ma_job_process__custom(ma_job* pJob);
16573static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob);
16574static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob);
16575static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob);
16576static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob);
16577static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob);
16578static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob);
16579static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob);
16580static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob);
16581static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob);
16582
16583#if !defined(MA_NO_DEVICE_IO)
16584static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob);
16585#endif
16586
16587static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] =
16588{
16589 /* Miscellaneous. */
16590 ma_job_process__quit, /* MA_JOB_TYPE_QUIT */
16591 ma_job_process__custom, /* MA_JOB_TYPE_CUSTOM */
16592
16593 /* Resource Manager. */
16594 ma_job_process__resource_manager__load_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE */
16595 ma_job_process__resource_manager__free_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE */
16596 ma_job_process__resource_manager__page_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE */
16597 ma_job_process__resource_manager__load_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER */
16598 ma_job_process__resource_manager__free_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER */
16599 ma_job_process__resource_manager__load_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM */
16600 ma_job_process__resource_manager__free_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM */
16601 ma_job_process__resource_manager__page_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM */
16602 ma_job_process__resource_manager__seek_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM */
16603
16604 /* Device. */
16605#if !defined(MA_NO_DEVICE_IO)
16606 ma_job_process__device__aaudio_reroute /*MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE*/
16607#endif
16608};
16609
16611{
16612 if (pJob == NULL) {
16613 return MA_INVALID_ARGS;
16614 }
16615
16616 if (pJob->toc.breakup.code > MA_JOB_TYPE_COUNT) {
16617 return MA_INVALID_OPERATION;
16618 }
16619
16620 return g_jobVTable[pJob->toc.breakup.code](pJob);
16621}
16622
16623static ma_result ma_job_process__noop(ma_job* pJob)
16624{
16625 MA_ASSERT(pJob != NULL);
16626
16627 /* No-op. */
16628 (void)pJob;
16629
16630 return MA_SUCCESS;
16631}
16632
16633static ma_result ma_job_process__quit(ma_job* pJob)
16634{
16635 return ma_job_process__noop(pJob);
16636}
16637
16638static ma_result ma_job_process__custom(ma_job* pJob)
16639{
16640 MA_ASSERT(pJob != NULL);
16641
16642 /* No-op if there's no callback. */
16643 if (pJob->data.custom.proc == NULL) {
16644 return MA_SUCCESS;
16645 }
16646
16647 return pJob->data.custom.proc(pJob);
16648}
16649
16650
16651
16653{
16654 ma_job_queue_config config;
16655
16656 config.flags = flags;
16657 config.capacity = capacity;
16658
16659 return config;
16660}
16661
16662
16663typedef struct
16664{
16665 size_t sizeInBytes;
16666 size_t allocatorOffset;
16667 size_t jobsOffset;
16668} ma_job_queue_heap_layout;
16669
16670static ma_result ma_job_queue_get_heap_layout(const ma_job_queue_config* pConfig, ma_job_queue_heap_layout* pHeapLayout)
16671{
16672 ma_result result;
16673
16674 MA_ASSERT(pHeapLayout != NULL);
16675
16676 MA_ZERO_OBJECT(pHeapLayout);
16677
16678 if (pConfig == NULL) {
16679 return MA_INVALID_ARGS;
16680 }
16681
16682 if (pConfig->capacity == 0) {
16683 return MA_INVALID_ARGS;
16684 }
16685
16686 pHeapLayout->sizeInBytes = 0;
16687
16688 /* Allocator. */
16689 {
16690 ma_slot_allocator_config allocatorConfig;
16691 size_t allocatorHeapSizeInBytes;
16692
16693 allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity);
16694 result = ma_slot_allocator_get_heap_size(&allocatorConfig, &allocatorHeapSizeInBytes);
16695 if (result != MA_SUCCESS) {
16696 return result;
16697 }
16698
16699 pHeapLayout->allocatorOffset = pHeapLayout->sizeInBytes;
16700 pHeapLayout->sizeInBytes += allocatorHeapSizeInBytes;
16701 }
16702
16703 /* Jobs. */
16704 pHeapLayout->jobsOffset = pHeapLayout->sizeInBytes;
16705 pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_job));
16706
16707 return MA_SUCCESS;
16708}
16709
16710MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes)
16711{
16712 ma_result result;
16713 ma_job_queue_heap_layout layout;
16714
16715 if (pHeapSizeInBytes == NULL) {
16716 return MA_INVALID_ARGS;
16717 }
16718
16719 *pHeapSizeInBytes = 0;
16720
16721 result = ma_job_queue_get_heap_layout(pConfig, &layout);
16722 if (result != MA_SUCCESS) {
16723 return result;
16724 }
16725
16726 *pHeapSizeInBytes = layout.sizeInBytes;
16727
16728 return MA_SUCCESS;
16729}
16730
16732{
16733 ma_result result;
16734 ma_job_queue_heap_layout heapLayout;
16735 ma_slot_allocator_config allocatorConfig;
16736
16737 if (pQueue == NULL) {
16738 return MA_INVALID_ARGS;
16739 }
16740
16741 MA_ZERO_OBJECT(pQueue);
16742
16743 result = ma_job_queue_get_heap_layout(pConfig, &heapLayout);
16744 if (result != MA_SUCCESS) {
16745 return result;
16746 }
16747
16748 pQueue->_pHeap = pHeap;
16749 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
16750
16751 pQueue->flags = pConfig->flags;
16752 pQueue->capacity = pConfig->capacity;
16753 pQueue->pJobs = (ma_job*)ma_offset_ptr(pHeap, heapLayout.jobsOffset);
16754
16755 allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity);
16756 result = ma_slot_allocator_init_preallocated(&allocatorConfig, ma_offset_ptr(pHeap, heapLayout.allocatorOffset), &pQueue->allocator);
16757 if (result != MA_SUCCESS) {
16758 return result;
16759 }
16760
16761 /* We need a semaphore if we're running in non-blocking mode. If threading is disabled we need to return an error. */
16762 if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
16763 #ifndef MA_NO_THREADING
16764 {
16765 ma_semaphore_init(0, &pQueue->sem);
16766 }
16767 #else
16768 {
16769 /* Threading is disabled and we've requested non-blocking mode. */
16770 return MA_INVALID_OPERATION;
16771 }
16772 #endif
16773 }
16774
16775 /*
16776 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
16777 just a dummy item for giving us the first item in the list which is stored in the "next" member.
16778 */
16779 ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head); /* Will never fail. */
16780 pQueue->pJobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE;
16781 pQueue->tail = pQueue->head;
16782
16783 return MA_SUCCESS;
16784}
16785
16786MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue)
16787{
16788 ma_result result;
16789 size_t heapSizeInBytes;
16790 void* pHeap;
16791
16792 result = ma_job_queue_get_heap_size(pConfig, &heapSizeInBytes);
16793 if (result != MA_SUCCESS) {
16794 return result;
16795 }
16796
16797 if (heapSizeInBytes > 0) {
16798 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
16799 if (pHeap == NULL) {
16800 return MA_OUT_OF_MEMORY;
16801 }
16802 } else {
16803 pHeap = NULL;
16804 }
16805
16806 result = ma_job_queue_init_preallocated(pConfig, pHeap, pQueue);
16807 if (result != MA_SUCCESS) {
16808 ma_free(pHeap, pAllocationCallbacks);
16809 return result;
16810 }
16811
16812 pQueue->_ownsHeap = MA_TRUE;
16813 return MA_SUCCESS;
16814}
16815
16816MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks)
16817{
16818 if (pQueue == NULL) {
16819 return;
16820 }
16821
16822 /* All we need to do is uninitialize the semaphore. */
16823 if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
16824 #ifndef MA_NO_THREADING
16825 {
16826 ma_semaphore_uninit(&pQueue->sem);
16827 }
16828 #else
16829 {
16830 MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */
16831 }
16832 #endif
16833 }
16834
16835 ma_slot_allocator_uninit(&pQueue->allocator, pAllocationCallbacks);
16836
16837 if (pQueue->_ownsHeap) {
16838 ma_free(pQueue->_pHeap, pAllocationCallbacks);
16839 }
16840}
16841
16842static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired)
16843{
16844 /* The new counter is taken from the expected value. */
16845 return c89atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected;
16846}
16847
16849{
16850 /*
16851 Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors
16852 */
16853 ma_result result;
16854 ma_uint64 slot;
16855 ma_uint64 tail;
16856 ma_uint64 next;
16857
16858 if (pQueue == NULL || pJob == NULL) {
16859 return MA_INVALID_ARGS;
16860 }
16861
16862 /* We need a new slot. */
16863 result = ma_slot_allocator_alloc(&pQueue->allocator, &slot);
16864 if (result != MA_SUCCESS) {
16865 return result; /* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */
16866 }
16867
16868 /* At this point we should have a slot to place the job. */
16869 MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity);
16870
16871 /* We need to put the job into memory before we do anything. */
16872 pQueue->pJobs[ma_job_extract_slot(slot)] = *pJob;
16873 pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation = slot; /* This will overwrite the job code. */
16874 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. */
16875 pQueue->pJobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE; /* Reset for safety. */
16876
16877 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
16878 ma_spinlock_lock(&pQueue->lock);
16879 #endif
16880 {
16881 /* 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. */
16882 for (;;) {
16883 tail = c89atomic_load_64(&pQueue->tail);
16884 next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next);
16885
16886 if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->tail))) {
16887 if (ma_job_extract_slot(next) == 0xFFFF) {
16888 if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) {
16889 break;
16890 }
16891 } else {
16892 ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next));
16893 }
16894 }
16895 }
16896 ma_job_queue_cas(&pQueue->tail, tail, slot);
16897 }
16898 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
16899 ma_spinlock_unlock(&pQueue->lock);
16900 #endif
16901
16902
16903 /* Signal the semaphore as the last step if we're using synchronous mode. */
16904 if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
16905 #ifndef MA_NO_THREADING
16906 {
16907 ma_semaphore_release(&pQueue->sem);
16908 }
16909 #else
16910 {
16911 MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */
16912 }
16913 #endif
16914 }
16915
16916 return MA_SUCCESS;
16917}
16918
16920{
16921 ma_uint64 head;
16922 ma_uint64 tail;
16923 ma_uint64 next;
16924
16925 if (pQueue == NULL || pJob == NULL) {
16926 return MA_INVALID_ARGS;
16927 }
16928
16929 /* If we're running in synchronous mode we'll need to wait on a semaphore. */
16930 if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
16931 #ifndef MA_NO_THREADING
16932 {
16933 ma_semaphore_wait(&pQueue->sem);
16934 }
16935 #else
16936 {
16937 MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */
16938 }
16939 #endif
16940 }
16941
16942 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
16943 ma_spinlock_lock(&pQueue->lock);
16944 #endif
16945 {
16946 /*
16947 BUG: In lock-free mode, multiple threads can be in this section of code. The "head" variable in the loop below
16948 is stored. One thread can fall through to the freeing of this item while another is still using "head" for the
16949 retrieval of the "next" variable.
16950
16951 The slot allocator might need to make use of some reference counting to ensure it's only truely freed when
16952 there are no more references to the item. This must be fixed before removing these locks.
16953 */
16954
16955 /* Now we need to remove the root item from the list. */
16956 for (;;) {
16957 head = c89atomic_load_64(&pQueue->head);
16958 tail = c89atomic_load_64(&pQueue->tail);
16959 next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next);
16960
16961 if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->head))) {
16962 if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) {
16963 if (ma_job_extract_slot(next) == 0xFFFF) {
16964 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
16965 ma_spinlock_unlock(&pQueue->lock);
16966 #endif
16967 return MA_NO_DATA_AVAILABLE;
16968 }
16969 ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next));
16970 } else {
16971 *pJob = pQueue->pJobs[ma_job_extract_slot(next)];
16972 if (ma_job_queue_cas(&pQueue->head, head, ma_job_extract_slot(next))) {
16973 break;
16974 }
16975 }
16976 }
16977 }
16978 }
16979 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
16980 ma_spinlock_unlock(&pQueue->lock);
16981 #endif
16982
16983 ma_slot_allocator_free(&pQueue->allocator, head);
16984
16985 /*
16986 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
16987 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
16988 possible.
16989 */
16990 if (pJob->toc.breakup.code == MA_JOB_TYPE_QUIT) {
16991 ma_job_queue_post(pQueue, pJob);
16992 return MA_CANCELLED; /* Return a cancelled status just in case the thread is checking return codes and not properly checking for a quit job. */
16993 }
16994
16995 return MA_SUCCESS;
16996}
16997
16998
16999
17000
17001
17009#ifndef MA_NO_DEVICE_IO
17010#ifdef MA_WIN32
17011 #include <objbase.h>
17012 #include <mmreg.h>
17013 #include <mmsystem.h>
17014#endif
17015
17016#if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
17017 #include <mach/mach_time.h> /* For mach_absolute_time() */
17018#endif
17019
17020#ifdef MA_POSIX
17021 #include <sys/types.h>
17022 #include <unistd.h>
17023 #include <dlfcn.h>
17024#endif
17025
17026/*
17027Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When
17028using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing
17029compile-time linking fixes this. I'm not sure why this happens, but the safest way I can think of to fix this is to simply
17030disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am
17031not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere.
17032*/
17033/*#define MA_USE_RUNTIME_LINKING_FOR_PTHREAD*/
17034
17035/* Disable run-time linking on certain backends. */
17036#ifndef MA_NO_RUNTIME_LINKING
17037 #if defined(MA_EMSCRIPTEN)
17038 #define MA_NO_RUNTIME_LINKING
17039 #endif
17040#endif
17041
17042
17043MA_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)
17044{
17045 if (pDeviceInfo == NULL) {
17046 return;
17047 }
17048
17049 if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) {
17050 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
17051 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
17052 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
17053 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
17054 pDeviceInfo->nativeDataFormatCount += 1;
17055 }
17056}
17057
17058
17059MA_API const char* ma_get_backend_name(ma_backend backend)
17060{
17061 switch (backend)
17062 {
17063 case ma_backend_wasapi: return "WASAPI";
17064 case ma_backend_dsound: return "DirectSound";
17065 case ma_backend_winmm: return "WinMM";
17066 case ma_backend_coreaudio: return "Core Audio";
17067 case ma_backend_sndio: return "sndio";
17068 case ma_backend_audio4: return "audio(4)";
17069 case ma_backend_oss: return "OSS";
17070 case ma_backend_pulseaudio: return "PulseAudio";
17071 case ma_backend_alsa: return "ALSA";
17072 case ma_backend_jack: return "JACK";
17073 case ma_backend_aaudio: return "AAudio";
17074 case ma_backend_opensl: return "OpenSL|ES";
17075 case ma_backend_webaudio: return "Web Audio";
17076 case ma_backend_custom: return "Custom";
17077 case ma_backend_null: return "Null";
17078 default: return "Unknown";
17079 }
17080}
17081
17083{
17084 /*
17085 This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers
17086 about some enums not being handled by the switch statement.
17087 */
17088 switch (backend)
17089 {
17090 case ma_backend_wasapi:
17091 #if defined(MA_HAS_WASAPI)
17092 return MA_TRUE;
17093 #else
17094 return MA_FALSE;
17095 #endif
17096 case ma_backend_dsound:
17097 #if defined(MA_HAS_DSOUND)
17098 return MA_TRUE;
17099 #else
17100 return MA_FALSE;
17101 #endif
17102 case ma_backend_winmm:
17103 #if defined(MA_HAS_WINMM)
17104 return MA_TRUE;
17105 #else
17106 return MA_FALSE;
17107 #endif
17109 #if defined(MA_HAS_COREAUDIO)
17110 return MA_TRUE;
17111 #else
17112 return MA_FALSE;
17113 #endif
17114 case ma_backend_sndio:
17115 #if defined(MA_HAS_SNDIO)
17116 return MA_TRUE;
17117 #else
17118 return MA_FALSE;
17119 #endif
17120 case ma_backend_audio4:
17121 #if defined(MA_HAS_AUDIO4)
17122 return MA_TRUE;
17123 #else
17124 return MA_FALSE;
17125 #endif
17126 case ma_backend_oss:
17127 #if defined(MA_HAS_OSS)
17128 return MA_TRUE;
17129 #else
17130 return MA_FALSE;
17131 #endif
17133 #if defined(MA_HAS_PULSEAUDIO)
17134 return MA_TRUE;
17135 #else
17136 return MA_FALSE;
17137 #endif
17138 case ma_backend_alsa:
17139 #if defined(MA_HAS_ALSA)
17140 return MA_TRUE;
17141 #else
17142 return MA_FALSE;
17143 #endif
17144 case ma_backend_jack:
17145 #if defined(MA_HAS_JACK)
17146 return MA_TRUE;
17147 #else
17148 return MA_FALSE;
17149 #endif
17150 case ma_backend_aaudio:
17151 #if defined(MA_HAS_AAUDIO)
17152 return MA_TRUE;
17153 #else
17154 return MA_FALSE;
17155 #endif
17156 case ma_backend_opensl:
17157 #if defined(MA_HAS_OPENSL)
17158 return MA_TRUE;
17159 #else
17160 return MA_FALSE;
17161 #endif
17163 #if defined(MA_HAS_WEBAUDIO)
17164 return MA_TRUE;
17165 #else
17166 return MA_FALSE;
17167 #endif
17168 case ma_backend_custom:
17169 #if defined(MA_HAS_CUSTOM)
17170 return MA_TRUE;
17171 #else
17172 return MA_FALSE;
17173 #endif
17174 case ma_backend_null:
17175 #if defined(MA_HAS_NULL)
17176 return MA_TRUE;
17177 #else
17178 return MA_FALSE;
17179 #endif
17180
17181 default: return MA_FALSE;
17182 }
17183}
17184
17185MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount)
17186{
17187 size_t backendCount;
17188 size_t iBackend;
17189 ma_result result = MA_SUCCESS;
17190
17191 if (pBackendCount == NULL) {
17192 return MA_INVALID_ARGS;
17193 }
17194
17195 backendCount = 0;
17196
17197 for (iBackend = 0; iBackend <= ma_backend_null; iBackend += 1) {
17198 ma_backend backend = (ma_backend)iBackend;
17199
17200 if (ma_is_backend_enabled(backend)) {
17201 /* The backend is enabled. Try adding it to the list. If there's no room, MA_NO_SPACE needs to be returned. */
17202 if (backendCount == backendCap) {
17203 result = MA_NO_SPACE;
17204 break;
17205 } else {
17206 pBackends[backendCount] = backend;
17207 backendCount += 1;
17208 }
17209 }
17210 }
17211
17212 if (pBackendCount != NULL) {
17213 *pBackendCount = backendCount;
17214 }
17215
17216 return result;
17217}
17218
17220{
17221 switch (backend)
17222 {
17223 case ma_backend_wasapi: return MA_TRUE;
17224 case ma_backend_dsound: return MA_FALSE;
17225 case ma_backend_winmm: return MA_FALSE;
17226 case ma_backend_coreaudio: return MA_FALSE;
17227 case ma_backend_sndio: return MA_FALSE;
17228 case ma_backend_audio4: return MA_FALSE;
17229 case ma_backend_oss: return MA_FALSE;
17230 case ma_backend_pulseaudio: return MA_FALSE;
17231 case ma_backend_alsa: return MA_FALSE;
17232 case ma_backend_jack: return MA_FALSE;
17233 case ma_backend_aaudio: return MA_FALSE;
17234 case ma_backend_opensl: return MA_FALSE;
17235 case ma_backend_webaudio: return MA_FALSE;
17236 case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */
17237 case ma_backend_null: return MA_FALSE;
17238 default: return MA_FALSE;
17239 }
17240}
17241
17242
17243
17244#ifdef MA_WIN32
17245/* WASAPI error codes. */
17246#define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001)
17247#define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002)
17248#define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003)
17249#define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004)
17250#define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005)
17251#define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006)
17252#define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007)
17253#define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008)
17254#define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009)
17255#define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A)
17256#define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B)
17257#define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C)
17258#define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D)
17259#define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E)
17260#define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F)
17261#define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010)
17262#define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011)
17263#define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012)
17264#define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013)
17265#define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014)
17266#define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015)
17267#define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016)
17268#define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017)
17269#define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018)
17270#define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019)
17271#define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020)
17272#define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021)
17273#define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022)
17274#define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023)
17275#define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024)
17276#define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025)
17277#define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026)
17278#define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027)
17279#define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028)
17280#define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029)
17281#define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030)
17282#define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040)
17283#define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001)
17284#define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002)
17285#define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003)
17286
17287#define MA_DS_OK ((HRESULT)0)
17288#define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A)
17289#define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A)
17290#define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E)
17291#define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/
17292#define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032)
17293#define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/
17294#define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046)
17295#define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/
17296#define MA_DSERR_BADFORMAT ((HRESULT)0x88780064)
17297#define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/
17298#define MA_DSERR_NODRIVER ((HRESULT)0x88780078)
17299#define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082)
17300#define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/
17301#define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096)
17302#define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0)
17303#define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA)
17304#define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/
17305#define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/
17306#define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4)
17307#define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE)
17308#define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8)
17309#define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2)
17310#define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161)
17311#define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC)
17312
17313static ma_result ma_result_from_HRESULT(HRESULT hr)
17314{
17315 switch (hr)
17316 {
17317 case NOERROR: return MA_SUCCESS;
17318 /*case S_OK: return MA_SUCCESS;*/
17319
17320 case E_POINTER: return MA_INVALID_ARGS;
17321 case E_UNEXPECTED: return MA_ERROR;
17322 case E_NOTIMPL: return MA_NOT_IMPLEMENTED;
17323 case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY;
17324 case E_INVALIDARG: return MA_INVALID_ARGS;
17325 case E_NOINTERFACE: return MA_API_NOT_FOUND;
17326 case E_HANDLE: return MA_INVALID_ARGS;
17327 case E_ABORT: return MA_ERROR;
17328 case E_FAIL: return MA_ERROR;
17329 case E_ACCESSDENIED: return MA_ACCESS_DENIED;
17330
17331 /* WASAPI */
17332 case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
17333 case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
17334 case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS;
17335 case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE;
17336 case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED;
17337 case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG;
17338 case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION;
17339 case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED;
17340 case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS;
17341 case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY;
17342 case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION;
17343 case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST;
17344 case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION;
17345 case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED;
17346 case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
17347 case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED;
17348 case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS;
17349 case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED;
17350 case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS;
17351 case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS;
17352 case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS;
17353 case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS;
17354 case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR;
17355 case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR;
17356 case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS;
17357 case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS;
17358 case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS;
17359 case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION;
17360 case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY;
17361 case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
17362 case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
17363 case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA;
17364 case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION;
17365 case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION;
17366 case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION;
17367 case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION;
17368 case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION;
17369 case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE;
17370 case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS;
17371 case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR;
17372
17373 /* DirectSound */
17374 /*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */
17375 case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS;
17376 case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE;
17377 case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION;
17378 /*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */
17379 case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION;
17380 /*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */
17381 case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION;
17382 /*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */
17383 case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED;
17384 /*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */
17385 case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND;
17386 case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
17387 case MA_DSERR_NOAGGREGATION: return MA_ERROR;
17388 case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE;
17389 case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED;
17390 case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
17391 /*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */
17392 /*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */
17393 case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE;
17394 case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION;
17395 case MA_DSERR_SENDLOOP: return MA_DEADLOCK;
17396 case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS;
17397 case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE;
17398 case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE;
17399
17400 default: return MA_ERROR;
17401 }
17402}
17403
17404typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit);
17405typedef void (WINAPI * MA_PFN_CoUninitialize)(void);
17406typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv);
17407typedef void (WINAPI * MA_PFN_CoTaskMemFree)(LPVOID pv);
17408typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(PROPVARIANT *pvar);
17409typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax);
17410
17411typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void);
17412typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void);
17413
17414#if defined(MA_WIN32_DESKTOP)
17415/* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */
17416typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult);
17417typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey);
17418typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData);
17419#endif /* MA_WIN32_DESKTOP */
17420#endif /* MA_WIN32 */
17421
17422
17423#define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device"
17424#define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device"
17425
17426
17427
17428
17429
17434#ifdef MA_WIN32
17435 static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */
17436 void ma_timer_init(ma_timer* pTimer)
17437 {
17438 LARGE_INTEGER counter;
17439
17440 if (g_ma_TimerFrequency.QuadPart == 0) {
17441 QueryPerformanceFrequency(&g_ma_TimerFrequency);
17442 }
17443
17444 QueryPerformanceCounter(&counter);
17445 pTimer->counter = counter.QuadPart;
17446 }
17447
17448 double ma_timer_get_time_in_seconds(ma_timer* pTimer)
17449 {
17450 LARGE_INTEGER counter;
17451 if (!QueryPerformanceCounter(&counter)) {
17452 return 0;
17453 }
17454
17455 return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;
17456 }
17457#elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
17458 static ma_uint64 g_ma_TimerFrequency = 0;
17459 static void ma_timer_init(ma_timer* pTimer)
17460 {
17461 mach_timebase_info_data_t baseTime;
17462 mach_timebase_info(&baseTime);
17463 g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;
17464
17465 pTimer->counter = mach_absolute_time();
17466 }
17467
17468 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
17469 {
17470 ma_uint64 newTimeCounter = mach_absolute_time();
17471 ma_uint64 oldTimeCounter = pTimer->counter;
17472
17473 return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;
17474 }
17475#elif defined(MA_EMSCRIPTEN)
17476 static MA_INLINE void ma_timer_init(ma_timer* pTimer)
17477 {
17478 pTimer->counterD = emscripten_get_now();
17479 }
17480
17481 static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
17482 {
17483 return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */
17484 }
17485#else
17486 #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L
17487 #if defined(CLOCK_MONOTONIC)
17488 #define MA_CLOCK_ID CLOCK_MONOTONIC
17489 #else
17490 #define MA_CLOCK_ID CLOCK_REALTIME
17491 #endif
17492
17493 static void ma_timer_init(ma_timer* pTimer)
17494 {
17495 struct timespec newTime;
17496 clock_gettime(MA_CLOCK_ID, &newTime);
17497
17498 pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
17499 }
17500
17501 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
17502 {
17503 ma_uint64 newTimeCounter;
17504 ma_uint64 oldTimeCounter;
17505
17506 struct timespec newTime;
17507 clock_gettime(MA_CLOCK_ID, &newTime);
17508
17509 newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
17510 oldTimeCounter = pTimer->counter;
17511
17512 return (newTimeCounter - oldTimeCounter) / 1000000000.0;
17513 }
17514 #else
17515 static void ma_timer_init(ma_timer* pTimer)
17516 {
17517 struct timeval newTime;
17518 gettimeofday(&newTime, NULL);
17519
17520 pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
17521 }
17522
17523 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
17524 {
17525 ma_uint64 newTimeCounter;
17526 ma_uint64 oldTimeCounter;
17527
17528 struct timeval newTime;
17529 gettimeofday(&newTime, NULL);
17530
17531 newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
17532 oldTimeCounter = pTimer->counter;
17533
17534 return (newTimeCounter - oldTimeCounter) / 1000000.0;
17535 }
17536 #endif
17537#endif
17538
17539
17540
17545MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename)
17546{
17547 ma_handle handle;
17548
17549 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename);
17550
17551#ifdef _WIN32
17552#ifdef MA_WIN32_DESKTOP
17553 handle = (ma_handle)LoadLibraryA(filename);
17554#else
17555 /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */
17556 WCHAR filenameW[4096];
17557 if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) {
17558 handle = NULL;
17559 } else {
17560 handle = (ma_handle)LoadPackagedLibrary(filenameW, 0);
17561 }
17562#endif
17563#else
17564 handle = (ma_handle)dlopen(filename, RTLD_NOW);
17565#endif
17566
17567 /*
17568 I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority
17569 backend is a deliberate design choice. Instead I'm logging it as an informational message.
17570 */
17571 if (handle == NULL) {
17572 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename);
17573 }
17574
17575 (void)pContext; /* It's possible for pContext to be unused. */
17576 return handle;
17577}
17578
17579MA_API void ma_dlclose(ma_context* pContext, ma_handle handle)
17580{
17581#ifdef _WIN32
17582 FreeLibrary((HMODULE)handle);
17583#else
17584 dlclose((void*)handle);
17585#endif
17586
17587 (void)pContext;
17588}
17589
17590MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol)
17591{
17592 ma_proc proc;
17593
17594 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol);
17595
17596#ifdef _WIN32
17597 proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol);
17598#else
17599#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
17600 #pragma GCC diagnostic push
17601 #pragma GCC diagnostic ignored "-Wpedantic"
17602#endif
17603 proc = (ma_proc)dlsym((void*)handle, symbol);
17604#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
17605 #pragma GCC diagnostic pop
17606#endif
17607#endif
17608
17609 if (proc == NULL) {
17610 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol);
17611 }
17612
17613 (void)pContext; /* It's possible for pContext to be unused. */
17614 return proc;
17615}
17616
17617
17618#if 0
17619static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn)
17620{
17621 ma_uint32 closestRate = 0;
17622 ma_uint32 closestDiff = 0xFFFFFFFF;
17623 size_t iStandardRate;
17624
17625 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
17626 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
17627 ma_uint32 diff;
17628
17629 if (sampleRateIn > standardRate) {
17630 diff = sampleRateIn - standardRate;
17631 } else {
17632 diff = standardRate - sampleRateIn;
17633 }
17634
17635 if (diff == 0) {
17636 return standardRate; /* The input sample rate is a standard rate. */
17637 }
17638
17639 if (closestDiff > diff) {
17640 closestDiff = diff;
17641 closestRate = standardRate;
17642 }
17643 }
17644
17645 return closestRate;
17646}
17647#endif
17648
17649
17650static MA_INLINE unsigned int ma_device_disable_denormals(ma_device* pDevice)
17651{
17652 MA_ASSERT(pDevice != NULL);
17653
17654 if (!pDevice->noDisableDenormals) {
17655 return ma_disable_denormals();
17656 } else {
17657 return 0;
17658 }
17659}
17660
17661static MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned int prevState)
17662{
17663 MA_ASSERT(pDevice != NULL);
17664
17665 if (!pDevice->noDisableDenormals) {
17666 ma_restore_denormals(prevState);
17667 } else {
17668 /* Do nothing. */
17669 (void)prevState;
17670 }
17671}
17672
17673static ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type)
17674{
17675 ma_device_notification notification;
17676
17677 MA_ZERO_OBJECT(&notification);
17678 notification.pDevice = pDevice;
17679 notification.type = type;
17680
17681 return notification;
17682}
17683
17684static void ma_device__on_notification(ma_device_notification notification)
17685{
17686 MA_ASSERT(notification.pDevice != NULL);
17687
17688 if (notification.pDevice->onNotification != NULL) {
17689 notification.pDevice->onNotification(&notification);
17690 }
17691
17692 /* 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. */
17693 if (notification.pDevice->onStop != NULL && notification.type == ma_device_notification_type_stopped) {
17694 notification.pDevice->onStop(notification.pDevice);
17695 }
17696}
17697
17698void ma_device__on_notification_started(ma_device* pDevice)
17699{
17700 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started));
17701}
17702
17703void ma_device__on_notification_stopped(ma_device* pDevice)
17704{
17705 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped));
17706}
17707
17708void ma_device__on_notification_rerouted(ma_device* pDevice)
17709{
17710 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted));
17711}
17712
17713void ma_device__on_notification_interruption_began(ma_device* pDevice)
17714{
17715 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began));
17716}
17717
17718void ma_device__on_notification_interruption_ended(ma_device* pDevice)
17719{
17720 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended));
17721}
17722
17723
17724static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
17725{
17726 MA_ASSERT(pDevice != NULL);
17727 MA_ASSERT(pDevice->onData != NULL);
17728
17729 if (!pDevice->noPreSilencedOutputBuffer && pFramesOut != NULL) {
17730 ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels);
17731 }
17732
17733 pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount);
17734}
17735
17736static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
17737{
17738 MA_ASSERT(pDevice != NULL);
17739
17740 if (pDevice->noFixedSizedCallback) {
17741 /* Fast path. Not using a fixed sized callback. Process directly from the specified buffers. */
17742 ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount);
17743 } else {
17744 /* Slow path. Using a fixed sized callback. Need to use the intermediary buffer. */
17745 ma_uint32 totalFramesProcessed = 0;
17746
17747 while (totalFramesProcessed < frameCount) {
17748 ma_uint32 totalFramesRemaining = frameCount - totalFramesProcessed;
17749 ma_uint32 framesToProcessThisIteration = 0;
17750
17751 if (pFramesIn != NULL) {
17752 /* Capturing. Write to the intermediary buffer. If there's no room, fire the callback to empty it. */
17754 /* There's some room left in the intermediary buffer. Write to it without firing the callback. */
17755 framesToProcessThisIteration = totalFramesRemaining;
17756 if (framesToProcessThisIteration > pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen) {
17757 framesToProcessThisIteration = pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen;
17758 }
17759
17762 ma_offset_pcm_frames_const_ptr(pFramesIn, totalFramesProcessed, pDevice->capture.format, pDevice->capture.channels),
17763 framesToProcessThisIteration,
17764 pDevice->capture.format, pDevice->capture.channels);
17765
17766 pDevice->capture.intermediaryBufferLen += framesToProcessThisIteration;
17767 }
17768
17770 /* No room left in the intermediary buffer. Fire the data callback. */
17771 if (pDevice->type == ma_device_type_duplex) {
17772 /* We'll do the duplex data callback later after we've processed the playback data. */
17773 } else {
17774 ma_device__on_data_inner(pDevice, NULL, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap);
17775
17776 /* The intermediary buffer has just been drained. */
17777 pDevice->capture.intermediaryBufferLen = 0;
17778 }
17779 }
17780 }
17781
17782 if (pFramesOut != NULL) {
17783 /* Playing back. Read from the intermediary buffer. If there's nothing in it, fire the callback to fill it. */
17784 if (pDevice->playback.intermediaryBufferLen > 0) {
17785 /* There's some content in the intermediary buffer. Read from that without firing the callback. */
17786 if (pDevice->type == ma_device_type_duplex) {
17787 /* The frames processed this iteration for a duplex device will always be based on the capture side. Leave it unmodified. */
17788 } else {
17789 framesToProcessThisIteration = totalFramesRemaining;
17790 if (framesToProcessThisIteration > pDevice->playback.intermediaryBufferLen) {
17791 framesToProcessThisIteration = pDevice->playback.intermediaryBufferLen;
17792 }
17793 }
17794
17796 ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, pDevice->playback.format, pDevice->playback.channels),
17798 framesToProcessThisIteration,
17799 pDevice->playback.format, pDevice->playback.channels);
17800
17801 pDevice->playback.intermediaryBufferLen -= framesToProcessThisIteration;
17802 }
17803
17804 if (pDevice->playback.intermediaryBufferLen == 0) {
17805 /* There's nothing in the intermediary buffer. Fire the data callback to fill it. */
17806 if (pDevice->type == ma_device_type_duplex) {
17807 /* In duplex mode, the data callback will be fired later. Nothing to do here. */
17808 } else {
17809 ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, NULL, pDevice->playback.intermediaryBufferCap);
17810
17811 /* The intermediary buffer has just been filled. */
17813 }
17814 }
17815 }
17816
17817 /* If we're in duplex mode we might need to do a refill of the data. */
17818 if (pDevice->type == ma_device_type_duplex) {
17820 ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap);
17821
17822 pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; /* The playback buffer will have just been filled. */
17823 pDevice->capture.intermediaryBufferLen = 0; /* The intermediary buffer has just been drained. */
17824 }
17825 }
17826
17827 /* Make sure this is only incremented once in the duplex case. */
17828 totalFramesProcessed += framesToProcessThisIteration;
17829 }
17830 }
17831}
17832
17833static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
17834{
17835 float masterVolumeFactor;
17836
17837 ma_device_get_master_volume(pDevice, &masterVolumeFactor); /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */
17838
17839 if (pDevice->onData) {
17840 unsigned int prevDenormalState = ma_device_disable_denormals(pDevice);
17841 {
17842 /* 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. */
17843 if (pFramesIn != NULL && masterVolumeFactor < 1) {
17844 ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
17845 ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
17846 ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
17847 ma_uint32 totalFramesProcessed = 0;
17848 while (totalFramesProcessed < frameCount) {
17849 ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed;
17850 if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) {
17851 framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture;
17852 }
17853
17854 ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor);
17855
17856 ma_device__on_data(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration);
17857
17858 totalFramesProcessed += framesToProcessThisIteration;
17859 }
17860 } else {
17861 ma_device__on_data(pDevice, pFramesOut, pFramesIn, frameCount);
17862 }
17863
17864 /* Volume control and clipping for playback devices. */
17865 if (pFramesOut != NULL) {
17866 if (masterVolumeFactor < 1) {
17867 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. */
17868 ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor);
17869 }
17870 }
17871
17872 if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) {
17873 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. */
17874 }
17875 }
17876 }
17877 ma_device_restore_denormals(pDevice, prevDenormalState);
17878 }
17879}
17880
17881
17882
17883/* A helper function for reading sample data from the client. */
17884static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut)
17885{
17886 MA_ASSERT(pDevice != NULL);
17887 MA_ASSERT(frameCount > 0);
17888 MA_ASSERT(pFramesOut != NULL);
17889
17890 if (pDevice->playback.converter.isPassthrough) {
17891 ma_device__handle_data_callback(pDevice, pFramesOut, NULL, frameCount);
17892 } else {
17893 ma_result result;
17894 ma_uint64 totalFramesReadOut;
17895 void* pRunningFramesOut;
17896
17897 totalFramesReadOut = 0;
17898 pRunningFramesOut = pFramesOut;
17899
17900 /*
17901 We run slightly different logic depending on whether or not we're using a heap-allocated
17902 buffer for caching input data. This will be the case if the data converter does not have
17903 the ability to retrieve the required input frame count for a given output frame count.
17904 */
17905 if (pDevice->playback.pInputCache != NULL) {
17906 while (totalFramesReadOut < frameCount) {
17907 ma_uint64 framesToReadThisIterationIn;
17908 ma_uint64 framesToReadThisIterationOut;
17909
17910 /* If there's any data available in the cache, that needs to get processed first. */
17911 if (pDevice->playback.inputCacheRemaining > 0) {
17912 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
17913 framesToReadThisIterationIn = framesToReadThisIterationOut;
17914 if (framesToReadThisIterationIn > pDevice->playback.inputCacheRemaining) {
17915 framesToReadThisIterationIn = pDevice->playback.inputCacheRemaining;
17916 }
17917
17918 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);
17919 if (result != MA_SUCCESS) {
17920 break;
17921 }
17922
17923 pDevice->playback.inputCacheConsumed += framesToReadThisIterationIn;
17924 pDevice->playback.inputCacheRemaining -= framesToReadThisIterationIn;
17925
17926 totalFramesReadOut += framesToReadThisIterationOut;
17927 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
17928
17929 if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) {
17930 break; /* We're done. */
17931 }
17932 }
17933
17934 /* Getting here means there's no data in the cache and we need to fill it up with data from the client. */
17935 if (pDevice->playback.inputCacheRemaining == 0) {
17936 ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, NULL, (ma_uint32)pDevice->playback.inputCacheCap);
17937
17938 pDevice->playback.inputCacheConsumed = 0;
17940 }
17941 }
17942 } else {
17943 while (totalFramesReadOut < frameCount) {
17944 ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */
17945 ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
17946 ma_uint64 framesToReadThisIterationIn;
17947 ma_uint64 framesReadThisIterationIn;
17948 ma_uint64 framesToReadThisIterationOut;
17949 ma_uint64 framesReadThisIterationOut;
17950 ma_uint64 requiredInputFrameCount;
17951
17952 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
17953 framesToReadThisIterationIn = framesToReadThisIterationOut;
17954 if (framesToReadThisIterationIn > intermediaryBufferCap) {
17955 framesToReadThisIterationIn = intermediaryBufferCap;
17956 }
17957
17958 ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut, &requiredInputFrameCount);
17959 if (framesToReadThisIterationIn > requiredInputFrameCount) {
17960 framesToReadThisIterationIn = requiredInputFrameCount;
17961 }
17962
17963 if (framesToReadThisIterationIn > 0) {
17964 ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn);
17965 }
17966
17967 /*
17968 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
17969 input frames, we still want to try processing frames because there may some output frames generated from cached input data.
17970 */
17971 framesReadThisIterationIn = framesToReadThisIterationIn;
17972 framesReadThisIterationOut = framesToReadThisIterationOut;
17973 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
17974 if (result != MA_SUCCESS) {
17975 break;
17976 }
17977
17978 totalFramesReadOut += framesReadThisIterationOut;
17979 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
17980
17981 if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
17982 break; /* We're done. */
17983 }
17984 }
17985 }
17986 }
17987}
17988
17989/* A helper for sending sample data to the client. */
17990static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat)
17991{
17992 MA_ASSERT(pDevice != NULL);
17993 MA_ASSERT(frameCountInDeviceFormat > 0);
17994 MA_ASSERT(pFramesInDeviceFormat != NULL);
17995
17996 if (pDevice->capture.converter.isPassthrough) {
17997 ma_device__handle_data_callback(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat);
17998 } else {
17999 ma_result result;
18000 ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18001 ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
18002 ma_uint64 totalDeviceFramesProcessed = 0;
18003 ma_uint64 totalClientFramesProcessed = 0;
18004 const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
18005
18006 /* We just keep going until we've exhaused all of our input frames and cannot generate any more output frames. */
18007 for (;;) {
18008 ma_uint64 deviceFramesProcessedThisIteration;
18009 ma_uint64 clientFramesProcessedThisIteration;
18010
18011 deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
18012 clientFramesProcessedThisIteration = framesInClientFormatCap;
18013
18014 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration);
18015 if (result != MA_SUCCESS) {
18016 break;
18017 }
18018
18019 if (clientFramesProcessedThisIteration > 0) {
18020 ma_device__handle_data_callback(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */
18021 }
18022
18023 pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
18024 totalDeviceFramesProcessed += deviceFramesProcessedThisIteration;
18025 totalClientFramesProcessed += clientFramesProcessedThisIteration;
18026
18027 if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) {
18028 break; /* We're done. */
18029 }
18030 }
18031 }
18032}
18033
18034static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB)
18035{
18036 ma_result result;
18037 ma_uint32 totalDeviceFramesProcessed = 0;
18038 const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
18039
18040 MA_ASSERT(pDevice != NULL);
18041 MA_ASSERT(frameCountInDeviceFormat > 0);
18042 MA_ASSERT(pFramesInDeviceFormat != NULL);
18043 MA_ASSERT(pRB != NULL);
18044
18045 /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */
18046 for (;;) {
18047 ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
18048 ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
18049 ma_uint64 framesProcessedInDeviceFormat;
18050 ma_uint64 framesProcessedInClientFormat;
18051 void* pFramesInClientFormat;
18052
18053 result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat);
18054 if (result != MA_SUCCESS) {
18055 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer.");
18056 break;
18057 }
18058
18059 if (framesToProcessInClientFormat == 0) {
18061 break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */
18062 }
18063 }
18064
18065 /* Convert. */
18066 framesProcessedInDeviceFormat = framesToProcessInDeviceFormat;
18067 framesProcessedInClientFormat = framesToProcessInClientFormat;
18068 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat);
18069 if (result != MA_SUCCESS) {
18070 break;
18071 }
18072
18073 result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat); /* Safe cast. */
18074 if (result != MA_SUCCESS) {
18075 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer.");
18076 break;
18077 }
18078
18079 pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
18080 totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */
18081
18082 /* We're done when we're unable to process any client nor device frames. */
18083 if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) {
18084 break; /* Done. */
18085 }
18086 }
18087
18088 return MA_SUCCESS;
18089}
18090
18091static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB)
18092{
18093 ma_result result;
18094 ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18095 ma_uint32 totalFramesReadOut = 0;
18096
18097 MA_ASSERT(pDevice != NULL);
18098 MA_ASSERT(frameCount > 0);
18099 MA_ASSERT(pFramesInInternalFormat != NULL);
18100 MA_ASSERT(pRB != NULL);
18101 MA_ASSERT(pDevice->playback.pInputCache != NULL);
18102
18103 /*
18104 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
18105 the whole frameCount frames we just use silence instead for the input data.
18106 */
18107 MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames));
18108
18109 while (totalFramesReadOut < frameCount && ma_device_is_started(pDevice)) {
18110 /*
18111 We should have a buffer allocated on the heap. Any playback frames still sitting in there
18112 need to be sent to the internal device before we process any more data from the client.
18113 */
18114 if (pDevice->playback.inputCacheRemaining > 0) {
18115 ma_uint64 framesConvertedIn = pDevice->playback.inputCacheRemaining;
18116 ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut);
18117 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);
18118
18119 pDevice->playback.inputCacheConsumed += framesConvertedIn;
18120 pDevice->playback.inputCacheRemaining -= framesConvertedIn;
18121
18122 totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */
18123 pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
18124 }
18125
18126 /* If there's no more data in the cache we'll need to fill it with some. */
18127 if (totalFramesReadOut < frameCount && pDevice->playback.inputCacheRemaining == 0) {
18128 ma_uint32 inputFrameCount;
18129 void* pInputFrames;
18130
18131 inputFrameCount = (ma_uint32)pDevice->playback.inputCacheCap;
18132 result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames);
18133 if (result == MA_SUCCESS) {
18134 if (inputFrameCount > 0) {
18135 ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount);
18136 } else {
18137 if (ma_pcm_rb_pointer_distance(pRB) == 0) {
18138 break; /* Underrun. */
18139 }
18140 }
18141 } else {
18142 /* No capture data available. Feed in silence. */
18143 inputFrameCount = (ma_uint32)ma_min(pDevice->playback.inputCacheCap, sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
18144 ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, silentInputFrames, inputFrameCount);
18145 }
18146
18147 pDevice->playback.inputCacheConsumed = 0;
18148 pDevice->playback.inputCacheRemaining = inputFrameCount;
18149
18150 result = ma_pcm_rb_commit_read(pRB, inputFrameCount);
18151 if (result != MA_SUCCESS) {
18152 return result; /* Should never happen. */
18153 }
18154 }
18155 }
18156
18157 return MA_SUCCESS;
18158}
18159
18160/* A helper for changing the state of the device. */
18161static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState)
18162{
18163 c89atomic_exchange_i32((ma_int32*)&pDevice->state, (ma_int32)newState);
18164}
18165
18166
18167#ifdef MA_WIN32
18168 GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
18169 GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
18170 /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
18171 /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
18172#endif
18173
18174
18175
18176MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */
18177{
18178 ma_uint32 i;
18179 for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) {
18180 if (g_maFormatPriorities[i] == format) {
18181 return i;
18182 }
18183 }
18184
18185 /* Getting here means the format could not be found or is equal to ma_format_unknown. */
18186 return (ma_uint32)-1;
18187}
18188
18189static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType);
18190
18191static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor)
18192{
18193 if (pDeviceDescriptor == NULL) {
18194 return MA_FALSE;
18195 }
18196
18197 if (pDeviceDescriptor->format == ma_format_unknown) {
18198 return MA_FALSE;
18199 }
18200
18201 if (pDeviceDescriptor->channels == 0 || pDeviceDescriptor->channels > MA_MAX_CHANNELS) {
18202 return MA_FALSE;
18203 }
18204
18205 if (pDeviceDescriptor->sampleRate == 0) {
18206 return MA_FALSE;
18207 }
18208
18209 return MA_TRUE;
18210}
18211
18212
18213static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice)
18214{
18215 ma_result result = MA_SUCCESS;
18216 ma_bool32 exitLoop = MA_FALSE;
18217 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18218 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18219 ma_uint32 capturedDeviceDataCapInFrames = 0;
18220 ma_uint32 playbackDeviceDataCapInFrames = 0;
18221
18222 MA_ASSERT(pDevice != NULL);
18223
18224 /* Just some quick validation on the device type and the available callbacks. */
18225 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
18226 if (pDevice->pContext->callbacks.onDeviceRead == NULL) {
18227 return MA_NOT_IMPLEMENTED;
18228 }
18229
18230 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
18231 }
18232
18233 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
18234 if (pDevice->pContext->callbacks.onDeviceWrite == NULL) {
18235 return MA_NOT_IMPLEMENTED;
18236 }
18237
18238 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
18239 }
18240
18241 /* NOTE: The device was started outside of this function, in the worker thread. */
18242
18243 while (ma_device_get_state(pDevice) == ma_device_state_started && !exitLoop) {
18244 switch (pDevice->type) {
18246 {
18247 /* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */
18248 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
18249 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
18250
18251 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
18252 ma_uint32 capturedDeviceFramesRemaining;
18253 ma_uint32 capturedDeviceFramesProcessed;
18254 ma_uint32 capturedDeviceFramesToProcess;
18255 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
18256 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
18257 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
18258 }
18259
18260 result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
18261 if (result != MA_SUCCESS) {
18262 exitLoop = MA_TRUE;
18263 break;
18264 }
18265
18266 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
18267 capturedDeviceFramesProcessed = 0;
18268
18269 /* At this point we have our captured data in device format and we now need to convert it to client format. */
18270 for (;;) {
18271 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18272 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18273 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
18274 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
18275 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
18276 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
18277 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
18278
18279 /* Convert capture data from device format to client format. */
18280 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
18281 if (result != MA_SUCCESS) {
18282 break;
18283 }
18284
18285 /*
18286 If we weren't able to generate any output frames it must mean we've exhaused all of our input. The only time this would not be the case is if capturedClientData was too small
18287 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
18288 */
18289 if (capturedClientFramesToProcessThisIteration == 0) {
18290 break;
18291 }
18292
18293 ma_device__handle_data_callback(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
18294
18295 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
18296 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
18297
18298 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
18299 for (;;) {
18300 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
18301 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
18302 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
18303 if (result != MA_SUCCESS) {
18304 break;
18305 }
18306
18307 result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
18308 if (result != MA_SUCCESS) {
18309 exitLoop = MA_TRUE;
18310 break;
18311 }
18312
18313 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
18314 if (capturedClientFramesToProcessThisIteration == 0) {
18315 break;
18316 }
18317 }
18318
18319 /* In case an error happened from ma_device_write__null()... */
18320 if (result != MA_SUCCESS) {
18321 exitLoop = MA_TRUE;
18322 break;
18323 }
18324 }
18325
18326 /* Make sure we don't get stuck in the inner loop. */
18327 if (capturedDeviceFramesProcessed == 0) {
18328 break;
18329 }
18330
18331 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
18332 }
18333 } break;
18334
18337 {
18338 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
18339 ma_uint32 framesReadThisPeriod = 0;
18340 while (framesReadThisPeriod < periodSizeInFrames) {
18341 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
18342 ma_uint32 framesProcessed;
18343 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
18344 if (framesToReadThisIteration > capturedDeviceDataCapInFrames) {
18345 framesToReadThisIteration = capturedDeviceDataCapInFrames;
18346 }
18347
18348 result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed);
18349 if (result != MA_SUCCESS) {
18350 exitLoop = MA_TRUE;
18351 break;
18352 }
18353
18354 /* Make sure we don't get stuck in the inner loop. */
18355 if (framesProcessed == 0) {
18356 break;
18357 }
18358
18359 ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData);
18360
18361 framesReadThisPeriod += framesProcessed;
18362 }
18363 } break;
18364
18366 {
18367 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
18368 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
18369 ma_uint32 framesWrittenThisPeriod = 0;
18370 while (framesWrittenThisPeriod < periodSizeInFrames) {
18371 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
18372 ma_uint32 framesProcessed;
18373 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
18374 if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) {
18375 framesToWriteThisIteration = playbackDeviceDataCapInFrames;
18376 }
18377
18378 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData);
18379
18380 result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed);
18381 if (result != MA_SUCCESS) {
18382 exitLoop = MA_TRUE;
18383 break;
18384 }
18385
18386 /* Make sure we don't get stuck in the inner loop. */
18387 if (framesProcessed == 0) {
18388 break;
18389 }
18390
18391 framesWrittenThisPeriod += framesProcessed;
18392 }
18393 } break;
18394
18395 /* Should never get here. */
18396 default: break;
18397 }
18398 }
18399
18400 return result;
18401}
18402
18403
18404
18405
18410#ifdef MA_HAS_NULL
18411
18412#define MA_DEVICE_OP_NONE__NULL 0
18413#define MA_DEVICE_OP_START__NULL 1
18414#define MA_DEVICE_OP_SUSPEND__NULL 2
18415#define MA_DEVICE_OP_KILL__NULL 3
18416
18417static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData)
18418{
18419 ma_device* pDevice = (ma_device*)pData;
18420 MA_ASSERT(pDevice != NULL);
18421
18422 for (;;) { /* Keep the thread alive until the device is uninitialized. */
18423 ma_uint32 operation;
18424
18425 /* Wait for an operation to be requested. */
18427
18428 /* At this point an event should have been triggered. */
18429 operation = pDevice->null_device.operation;
18430
18431 /* Starting the device needs to put the thread into a loop. */
18432 if (operation == MA_DEVICE_OP_START__NULL) {
18433 /* Reset the timer just in case. */
18434 ma_timer_init(&pDevice->null_device.timer);
18435
18436 /* Getting here means a suspend or kill operation has been requested. */
18439 ma_semaphore_release(&pDevice->null_device.operationSemaphore);
18440 continue;
18441 }
18442
18443 /* Suspending the device means we need to stop the timer and just continue the loop. */
18444 if (operation == MA_DEVICE_OP_SUSPEND__NULL) {
18445 /* We need to add the current run time to the prior run time, then reset the timer. */
18446 pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);
18447 ma_timer_init(&pDevice->null_device.timer);
18448
18449 /* We're done. */
18452 ma_semaphore_release(&pDevice->null_device.operationSemaphore);
18453 continue;
18454 }
18455
18456 /* Killing the device means we need to get out of this loop so that this thread can terminate. */
18457 if (operation == MA_DEVICE_OP_KILL__NULL) {
18460 ma_semaphore_release(&pDevice->null_device.operationSemaphore);
18461 break;
18462 }
18463
18464 /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */
18465 if (operation == MA_DEVICE_OP_NONE__NULL) {
18466 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). */
18469 ma_semaphore_release(&pDevice->null_device.operationSemaphore);
18470 continue; /* Continue the loop. Don't terminate. */
18471 }
18472 }
18473
18474 return (ma_thread_result)0;
18475}
18476
18477static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation)
18478{
18479 ma_result result;
18480
18481 /*
18482 TODO: Need to review this and consider just using mutual exclusion. I think the original motivation
18483 for this was to just post the event to a queue and return immediately, but that has since changed
18484 and now this function is synchronous. I think this can be simplified to just use a mutex.
18485 */
18486
18487 /*
18488 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
18489 to support queing of operations.
18490 */
18491 result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore);
18492 if (result != MA_SUCCESS) {
18493 return result; /* Failed to wait for the event. */
18494 }
18495
18496 /*
18497 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
18498 signal an event to the worker thread to let it know that it can start work.
18499 */
18500 pDevice->null_device.operation = operation;
18501
18502 /* Once the operation code has been set, the worker thread can start work. */
18504 return MA_ERROR;
18505 }
18506
18507 /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */
18509 return MA_ERROR;
18510 }
18511
18512 return pDevice->null_device.operationResult;
18513}
18514
18515static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)
18516{
18517 ma_uint32 internalSampleRate;
18518 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
18519 internalSampleRate = pDevice->capture.internalSampleRate;
18520 } else {
18521 internalSampleRate = pDevice->playback.internalSampleRate;
18522 }
18523
18524 return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);
18525}
18526
18527static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
18528{
18529 ma_bool32 cbResult = MA_TRUE;
18530
18531 MA_ASSERT(pContext != NULL);
18532 MA_ASSERT(callback != NULL);
18533
18534 /* Playback. */
18535 if (cbResult) {
18536 ma_device_info deviceInfo;
18537 MA_ZERO_OBJECT(&deviceInfo);
18538 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1);
18539 deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
18540 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
18541 }
18542
18543 /* Capture. */
18544 if (cbResult) {
18545 ma_device_info deviceInfo;
18546 MA_ZERO_OBJECT(&deviceInfo);
18547 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1);
18548 deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
18549 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
18550 }
18551
18552 (void)cbResult; /* Silence a static analysis warning. */
18553
18554 return MA_SUCCESS;
18555}
18556
18557static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
18558{
18559 MA_ASSERT(pContext != NULL);
18560
18561 if (pDeviceID != NULL && pDeviceID->nullbackend != 0) {
18562 return MA_NO_DEVICE; /* Don't know the device. */
18563 }
18564
18565 /* Name / Description */
18566 if (deviceType == ma_device_type_playback) {
18567 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1);
18568 } else {
18569 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1);
18570 }
18571
18572 pDeviceInfo->isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
18573
18574 /* Support everything on the null backend. */
18575 pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown;
18576 pDeviceInfo->nativeDataFormats[0].channels = 0;
18577 pDeviceInfo->nativeDataFormats[0].sampleRate = 0;
18578 pDeviceInfo->nativeDataFormats[0].flags = 0;
18579 pDeviceInfo->nativeDataFormatCount = 1;
18580
18581 (void)pContext;
18582 return MA_SUCCESS;
18583}
18584
18585
18586static ma_result ma_device_uninit__null(ma_device* pDevice)
18587{
18588 MA_ASSERT(pDevice != NULL);
18589
18590 /* Keep it clean and wait for the device thread to finish before returning. */
18591 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL);
18592
18593 /* Wait for the thread to finish before continuing. */
18594 ma_thread_wait(&pDevice->null_device.deviceThread);
18595
18596 /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */
18597 ma_semaphore_uninit(&pDevice->null_device.operationSemaphore);
18600
18601 return MA_SUCCESS;
18602}
18603
18604static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
18605{
18606 ma_result result;
18607
18608 MA_ASSERT(pDevice != NULL);
18609
18610 MA_ZERO_OBJECT(&pDevice->null_device);
18611
18612 if (pConfig->deviceType == ma_device_type_loopback) {
18614 }
18615
18616 /* The null backend supports everything exactly as we specify it. */
18617 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
18618 pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT;
18619 pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS;
18620 pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE;
18621
18622 if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) {
18623 ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
18624 }
18625
18626 pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
18627 }
18628
18629 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
18630 pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT;
18631 pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS;
18632 pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE;
18633
18634 if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) {
18635 ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels);
18636 }
18637
18638 pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
18639 }
18640
18641 /*
18642 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
18643 first period is "written" to it, and then stopped in ma_device_stop__null().
18644 */
18645 result = ma_event_init(&pDevice->null_device.operationEvent);
18646 if (result != MA_SUCCESS) {
18647 return result;
18648 }
18649
18651 if (result != MA_SUCCESS) {
18652 return result;
18653 }
18654
18655 result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */
18656 if (result != MA_SUCCESS) {
18657 return result;
18658 }
18659
18660 result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks);
18661 if (result != MA_SUCCESS) {
18662 return result;
18663 }
18664
18665 return MA_SUCCESS;
18666}
18667
18668static ma_result ma_device_start__null(ma_device* pDevice)
18669{
18670 MA_ASSERT(pDevice != NULL);
18671
18672 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL);
18673
18674 c89atomic_exchange_32(&pDevice->null_device.isStarted, MA_TRUE);
18675 return MA_SUCCESS;
18676}
18677
18678static ma_result ma_device_stop__null(ma_device* pDevice)
18679{
18680 MA_ASSERT(pDevice != NULL);
18681
18682 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL);
18683
18684 c89atomic_exchange_32(&pDevice->null_device.isStarted, MA_FALSE);
18685 return MA_SUCCESS;
18686}
18687
18688static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
18689{
18690 ma_result result = MA_SUCCESS;
18691 ma_uint32 totalPCMFramesProcessed;
18692 ma_bool32 wasStartedOnEntry;
18693
18694 if (pFramesWritten != NULL) {
18695 *pFramesWritten = 0;
18696 }
18697
18698 wasStartedOnEntry = c89atomic_load_32(&pDevice->null_device.isStarted);
18699
18700 /* Keep going until everything has been read. */
18701 totalPCMFramesProcessed = 0;
18702 while (totalPCMFramesProcessed < frameCount) {
18703 ma_uint64 targetFrame;
18704
18705 /* If there are any frames remaining in the current period, consume those first. */
18707 ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
18709 if (framesToProcess > framesRemaining) {
18710 framesToProcess = framesRemaining;
18711 }
18712
18713 /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */
18714 (void)pPCMFrames;
18715
18716 pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess;
18717 totalPCMFramesProcessed += framesToProcess;
18718 }
18719
18720 /* 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. */
18723
18724 if (!c89atomic_load_32(&pDevice->null_device.isStarted) && !wasStartedOnEntry) {
18725 result = ma_device_start__null(pDevice);
18726 if (result != MA_SUCCESS) {
18727 break;
18728 }
18729 }
18730 }
18731
18732 /* If we've consumed the whole buffer we can return now. */
18733 MA_ASSERT(totalPCMFramesProcessed <= frameCount);
18734 if (totalPCMFramesProcessed == frameCount) {
18735 break;
18736 }
18737
18738 /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
18739 targetFrame = pDevice->null_device.lastProcessedFramePlayback;
18740 for (;;) {
18741 ma_uint64 currentFrame;
18742
18743 /* Stop waiting if the device has been stopped. */
18744 if (!c89atomic_load_32(&pDevice->null_device.isStarted)) {
18745 break;
18746 }
18747
18748 currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
18749 if (currentFrame >= targetFrame) {
18750 break;
18751 }
18752
18753 /* Getting here means we haven't yet reached the target sample, so continue waiting. */
18754 ma_sleep(10);
18755 }
18756
18759 }
18760
18761 if (pFramesWritten != NULL) {
18762 *pFramesWritten = totalPCMFramesProcessed;
18763 }
18764
18765 return result;
18766}
18767
18768static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
18769{
18770 ma_result result = MA_SUCCESS;
18771 ma_uint32 totalPCMFramesProcessed;
18772
18773 if (pFramesRead != NULL) {
18774 *pFramesRead = 0;
18775 }
18776
18777 /* Keep going until everything has been read. */
18778 totalPCMFramesProcessed = 0;
18779 while (totalPCMFramesProcessed < frameCount) {
18780 ma_uint64 targetFrame;
18781
18782 /* If there are any frames remaining in the current period, consume those first. */
18784 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
18785 ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
18787 if (framesToProcess > framesRemaining) {
18788 framesToProcess = framesRemaining;
18789 }
18790
18791 /* We need to ensure the output buffer is zeroed. */
18792 MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf);
18793
18794 pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess;
18795 totalPCMFramesProcessed += framesToProcess;
18796 }
18797
18798 /* 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. */
18801 }
18802
18803 /* If we've consumed the whole buffer we can return now. */
18804 MA_ASSERT(totalPCMFramesProcessed <= frameCount);
18805 if (totalPCMFramesProcessed == frameCount) {
18806 break;
18807 }
18808
18809 /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
18811 for (;;) {
18812 ma_uint64 currentFrame;
18813
18814 /* Stop waiting if the device has been stopped. */
18815 if (!c89atomic_load_32(&pDevice->null_device.isStarted)) {
18816 break;
18817 }
18818
18819 currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
18820 if (currentFrame >= targetFrame) {
18821 break;
18822 }
18823
18824 /* Getting here means we haven't yet reached the target sample, so continue waiting. */
18825 ma_sleep(10);
18826 }
18827
18830 }
18831
18832 if (pFramesRead != NULL) {
18833 *pFramesRead = totalPCMFramesProcessed;
18834 }
18835
18836 return result;
18837}
18838
18839static ma_result ma_context_uninit__null(ma_context* pContext)
18840{
18841 MA_ASSERT(pContext != NULL);
18842 MA_ASSERT(pContext->backend == ma_backend_null);
18843
18844 (void)pContext;
18845 return MA_SUCCESS;
18846}
18847
18848static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
18849{
18850 MA_ASSERT(pContext != NULL);
18851
18852 (void)pConfig;
18853 (void)pContext;
18854
18855 pCallbacks->onContextInit = ma_context_init__null;
18856 pCallbacks->onContextUninit = ma_context_uninit__null;
18857 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null;
18858 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null;
18859 pCallbacks->onDeviceInit = ma_device_init__null;
18860 pCallbacks->onDeviceUninit = ma_device_uninit__null;
18861 pCallbacks->onDeviceStart = ma_device_start__null;
18862 pCallbacks->onDeviceStop = ma_device_stop__null;
18863 pCallbacks->onDeviceRead = ma_device_read__null;
18864 pCallbacks->onDeviceWrite = ma_device_write__null;
18865 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. */
18866
18867 /* The null backend always works. */
18868 return MA_SUCCESS;
18869}
18870#endif
18871
18872
18873
18874
18879#if defined(MA_WIN32)
18880#if defined(MA_WIN32_DESKTOP)
18881 #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit)
18882 #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)()
18883 #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv)
18884 #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv)
18885 #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar)
18886#else
18887 #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit)
18888 #define ma_CoUninitialize(pContext) CoUninitialize()
18889 #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv)
18890 #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv)
18891 #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar)
18892#endif
18893
18894#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__)
18895typedef size_t DWORD_PTR;
18896#endif
18897
18898#if !defined(WAVE_FORMAT_44M08)
18899#define WAVE_FORMAT_44M08 0x00000100
18900#define WAVE_FORMAT_44S08 0x00000200
18901#define WAVE_FORMAT_44M16 0x00000400
18902#define WAVE_FORMAT_44S16 0x00000800
18903#define WAVE_FORMAT_48M08 0x00001000
18904#define WAVE_FORMAT_48S08 0x00002000
18905#define WAVE_FORMAT_48M16 0x00004000
18906#define WAVE_FORMAT_48S16 0x00008000
18907#define WAVE_FORMAT_96M08 0x00010000
18908#define WAVE_FORMAT_96S08 0x00020000
18909#define WAVE_FORMAT_96M16 0x00040000
18910#define WAVE_FORMAT_96S16 0x00080000
18911#endif
18912
18913#ifndef SPEAKER_FRONT_LEFT
18914#define SPEAKER_FRONT_LEFT 0x1
18915#define SPEAKER_FRONT_RIGHT 0x2
18916#define SPEAKER_FRONT_CENTER 0x4
18917#define SPEAKER_LOW_FREQUENCY 0x8
18918#define SPEAKER_BACK_LEFT 0x10
18919#define SPEAKER_BACK_RIGHT 0x20
18920#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
18921#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
18922#define SPEAKER_BACK_CENTER 0x100
18923#define SPEAKER_SIDE_LEFT 0x200
18924#define SPEAKER_SIDE_RIGHT 0x400
18925#define SPEAKER_TOP_CENTER 0x800
18926#define SPEAKER_TOP_FRONT_LEFT 0x1000
18927#define SPEAKER_TOP_FRONT_CENTER 0x2000
18928#define SPEAKER_TOP_FRONT_RIGHT 0x4000
18929#define SPEAKER_TOP_BACK_LEFT 0x8000
18930#define SPEAKER_TOP_BACK_CENTER 0x10000
18931#define SPEAKER_TOP_BACK_RIGHT 0x20000
18932#endif
18933
18934/*
18935The SDK that comes with old versions of MSVC (VC6, for example) does not appear to define WAVEFORMATEXTENSIBLE. We
18936define our own implementation in this case.
18937*/
18938#if (defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_)) || defined(__DMC__)
18939typedef struct
18940{
18941 WAVEFORMATEX Format;
18942 union
18943 {
18944 WORD wValidBitsPerSample;
18945 WORD wSamplesPerBlock;
18946 WORD wReserved;
18947 } Samples;
18948 DWORD dwChannelMask;
18949 GUID SubFormat;
18950} WAVEFORMATEXTENSIBLE;
18951#endif
18952
18953#ifndef WAVE_FORMAT_EXTENSIBLE
18954#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
18955#endif
18956
18957#ifndef WAVE_FORMAT_IEEE_FLOAT
18958#define WAVE_FORMAT_IEEE_FLOAT 0x0003
18959#endif
18960
18961/* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
18962static ma_uint8 ma_channel_id_to_ma__win32(DWORD id)
18963{
18964 switch (id)
18965 {
18966 case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
18967 case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
18968 case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
18969 case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
18970 case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
18971 case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
18972 case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
18973 case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
18974 case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
18975 case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
18976 case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
18977 case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
18978 case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
18979 case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
18980 case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
18981 case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
18982 case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
18983 case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
18984 default: return 0;
18985 }
18986}
18987
18988/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */
18989static DWORD ma_channel_id_to_win32(DWORD id)
18990{
18991 switch (id)
18992 {
18993 case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER;
18994 case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT;
18995 case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT;
18996 case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER;
18997 case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY;
18998 case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT;
18999 case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT;
19000 case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER;
19001 case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER;
19002 case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER;
19003 case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT;
19004 case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT;
19005 case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER;
19006 case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT;
19007 case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER;
19008 case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT;
19009 case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT;
19010 case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER;
19011 case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT;
19012 default: return 0;
19013 }
19014}
19015
19016/* Converts a channel mapping to a Win32-style channel mask. */
19017static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels)
19018{
19019 DWORD dwChannelMask = 0;
19020 ma_uint32 iChannel;
19021
19022 for (iChannel = 0; iChannel < channels; ++iChannel) {
19023 dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]);
19024 }
19025
19026 return dwChannelMask;
19027}
19028
19029/* Converts a Win32-style channel mask to a miniaudio channel map. */
19030static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap)
19031{
19032 if (channels == 1 && dwChannelMask == 0) {
19033 pChannelMap[0] = MA_CHANNEL_MONO;
19034 } else if (channels == 2 && dwChannelMask == 0) {
19035 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
19036 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
19037 } else {
19038 if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) {
19039 pChannelMap[0] = MA_CHANNEL_MONO;
19040 } else {
19041 /* Just iterate over each bit. */
19042 ma_uint32 iChannel = 0;
19043 ma_uint32 iBit;
19044
19045 for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {
19046 DWORD bitValue = (dwChannelMask & (1UL << iBit));
19047 if (bitValue != 0) {
19048 /* The bit is set. */
19049 pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue);
19050 iChannel += 1;
19051 }
19052 }
19053 }
19054 }
19055}
19056
19057#ifdef __cplusplus
19058static ma_bool32 ma_is_guid_equal(const void* a, const void* b)
19059{
19060 return IsEqualGUID(*(const GUID*)a, *(const GUID*)b);
19061}
19062#else
19063#define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b)
19064#endif
19065
19066static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid)
19067{
19068 static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
19069 return ma_is_guid_equal(guid, &nullguid);
19070}
19071
19072static ma_format ma_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF)
19073{
19074 MA_ASSERT(pWF != NULL);
19075
19076 if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
19077 const WAVEFORMATEXTENSIBLE* pWFEX = (const WAVEFORMATEXTENSIBLE*)pWF;
19078 if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) {
19079 if (pWFEX->Samples.wValidBitsPerSample == 32) {
19080 return ma_format_s32;
19081 }
19082 if (pWFEX->Samples.wValidBitsPerSample == 24) {
19083 if (pWFEX->Format.wBitsPerSample == 32) {
19084 /*return ma_format_s24_32;*/
19085 }
19086 if (pWFEX->Format.wBitsPerSample == 24) {
19087 return ma_format_s24;
19088 }
19089 }
19090 if (pWFEX->Samples.wValidBitsPerSample == 16) {
19091 return ma_format_s16;
19092 }
19093 if (pWFEX->Samples.wValidBitsPerSample == 8) {
19094 return ma_format_u8;
19095 }
19096 }
19097 if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
19098 if (pWFEX->Samples.wValidBitsPerSample == 32) {
19099 return ma_format_f32;
19100 }
19101 /*
19102 if (pWFEX->Samples.wValidBitsPerSample == 64) {
19103 return ma_format_f64;
19104 }
19105 */
19106 }
19107 } else {
19108 if (pWF->wFormatTag == WAVE_FORMAT_PCM) {
19109 if (pWF->wBitsPerSample == 32) {
19110 return ma_format_s32;
19111 }
19112 if (pWF->wBitsPerSample == 24) {
19113 return ma_format_s24;
19114 }
19115 if (pWF->wBitsPerSample == 16) {
19116 return ma_format_s16;
19117 }
19118 if (pWF->wBitsPerSample == 8) {
19119 return ma_format_u8;
19120 }
19121 }
19122 if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
19123 if (pWF->wBitsPerSample == 32) {
19124 return ma_format_f32;
19125 }
19126 if (pWF->wBitsPerSample == 64) {
19127 /*return ma_format_f64;*/
19128 }
19129 }
19130 }
19131
19132 return ma_format_unknown;
19133}
19134#endif
19135
19136
19137
19142#ifdef MA_HAS_WASAPI
19143#if 0
19144#if defined(_MSC_VER)
19145 #pragma warning(push)
19146 #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */
19147#endif
19148#include <audioclient.h>
19149#include <mmdeviceapi.h>
19150#if defined(_MSC_VER)
19151 #pragma warning(pop)
19152#endif
19153#endif /* 0 */
19154
19155static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType);
19156
19157/* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */
19158#define MA_WIN32_WINNT_VISTA 0x0600
19159#define MA_VER_MINORVERSION 0x01
19160#define MA_VER_MAJORVERSION 0x02
19161#define MA_VER_SERVICEPACKMAJOR 0x20
19162#define MA_VER_GREATER_EQUAL 0x03
19163
19164typedef struct {
19165 DWORD dwOSVersionInfoSize;
19166 DWORD dwMajorVersion;
19167 DWORD dwMinorVersion;
19168 DWORD dwBuildNumber;
19169 DWORD dwPlatformId;
19170 WCHAR szCSDVersion[128];
19171 WORD wServicePackMajor;
19172 WORD wServicePackMinor;
19173 WORD wSuiteMask;
19174 BYTE wProductType;
19175 BYTE wReserved;
19176} ma_OSVERSIONINFOEXW;
19177
19178typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
19179typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask);
19180
19181
19182#ifndef PROPERTYKEY_DEFINED
19183#define PROPERTYKEY_DEFINED
19184#ifndef __WATCOMC__
19185typedef struct
19186{
19187 GUID fmtid;
19188 DWORD pid;
19189} PROPERTYKEY;
19190#endif
19191#endif
19192
19193/* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */
19194static MA_INLINE void ma_PropVariantInit(PROPVARIANT* pProp)
19195{
19196 MA_ZERO_OBJECT(pProp);
19197}
19198
19199
19200static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14};
19201static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0};
19202
19203static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */
19204#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK)
19205static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */
19206#endif
19207
19208static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */
19209static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */
19210static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */
19211static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */
19212static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */
19213static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */
19214#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK)
19215static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */
19216static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */
19217static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */
19218#endif
19219
19220static const IID MA_CLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */
19221static const IID MA_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */
19222#ifdef __cplusplus
19223#define MA_CLSID_MMDeviceEnumerator MA_CLSID_MMDeviceEnumerator_Instance
19224#define MA_IID_IMMDeviceEnumerator MA_IID_IMMDeviceEnumerator_Instance
19225#else
19226#define MA_CLSID_MMDeviceEnumerator &MA_CLSID_MMDeviceEnumerator_Instance
19227#define MA_IID_IMMDeviceEnumerator &MA_IID_IMMDeviceEnumerator_Instance
19228#endif
19229
19230typedef struct ma_IUnknown ma_IUnknown;
19231#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
19232#define MA_MM_DEVICE_STATE_ACTIVE 1
19233#define MA_MM_DEVICE_STATE_DISABLED 2
19234#define MA_MM_DEVICE_STATE_NOTPRESENT 4
19235#define MA_MM_DEVICE_STATE_UNPLUGGED 8
19236
19237typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator;
19238typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection;
19239typedef struct ma_IMMDevice ma_IMMDevice;
19240#else
19241typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler;
19242typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation;
19243#endif
19244typedef struct ma_IPropertyStore ma_IPropertyStore;
19245typedef struct ma_IAudioClient ma_IAudioClient;
19246typedef struct ma_IAudioClient2 ma_IAudioClient2;
19247typedef struct ma_IAudioClient3 ma_IAudioClient3;
19248typedef struct ma_IAudioRenderClient ma_IAudioRenderClient;
19249typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient;
19250
19251typedef ma_int64 MA_REFERENCE_TIME;
19252
19253#define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000
19254#define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000
19255#define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000
19256#define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000
19257#define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
19258#define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
19259#define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
19260#define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000
19261#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000
19262#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000
19263
19264/* Buffer flags. */
19265#define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1
19266#define MA_AUDCLNT_BUFFERFLAGS_SILENT 2
19267#define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4
19268
19269typedef enum
19270{
19271 ma_eRender = 0,
19272 ma_eCapture = 1,
19273 ma_eAll = 2
19274} ma_EDataFlow;
19275
19276typedef enum
19277{
19278 ma_eConsole = 0,
19279 ma_eMultimedia = 1,
19280 ma_eCommunications = 2
19281} ma_ERole;
19282
19283typedef enum
19284{
19285 MA_AUDCLNT_SHAREMODE_SHARED,
19286 MA_AUDCLNT_SHAREMODE_EXCLUSIVE
19287} MA_AUDCLNT_SHAREMODE;
19288
19289typedef enum
19290{
19291 MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */
19292} MA_AUDIO_STREAM_CATEGORY;
19293
19294typedef struct
19295{
19296 ma_uint32 cbSize;
19297 BOOL bIsOffload;
19298 MA_AUDIO_STREAM_CATEGORY eCategory;
19299} ma_AudioClientProperties;
19300
19301/* IUnknown */
19302typedef struct
19303{
19304 /* IUnknown */
19305 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject);
19306 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis);
19307 ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis);
19308} ma_IUnknownVtbl;
19309struct ma_IUnknown
19310{
19311 ma_IUnknownVtbl* lpVtbl;
19312};
19313static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
19314static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); }
19315static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); }
19316
19317#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
19318 /* IMMNotificationClient */
19319 typedef struct
19320 {
19321 /* IUnknown */
19322 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject);
19323 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis);
19324 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis);
19325
19326 /* IMMNotificationClient */
19327 HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState);
19328 HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID);
19329 HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID);
19330 HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID);
19331 HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key);
19332 } ma_IMMNotificationClientVtbl;
19333
19334 /* IMMDeviceEnumerator */
19335 typedef struct
19336 {
19337 /* IUnknown */
19338 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject);
19339 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis);
19340 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis);
19341
19342 /* IMMDeviceEnumerator */
19343 HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices);
19344 HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint);
19345 HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice);
19346 HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
19347 HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
19348 } ma_IMMDeviceEnumeratorVtbl;
19349 struct ma_IMMDeviceEnumerator
19350 {
19351 ma_IMMDeviceEnumeratorVtbl* lpVtbl;
19352 };
19353 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
19354 static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); }
19355 static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); }
19356 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); }
19357 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); }
19358 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); }
19359 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); }
19360 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); }
19361
19362
19363 /* IMMDeviceCollection */
19364 typedef struct
19365 {
19366 /* IUnknown */
19367 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject);
19368 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis);
19369 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis);
19370
19371 /* IMMDeviceCollection */
19372 HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices);
19373 HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice);
19374 } ma_IMMDeviceCollectionVtbl;
19375 struct ma_IMMDeviceCollection
19376 {
19377 ma_IMMDeviceCollectionVtbl* lpVtbl;
19378 };
19379 static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
19380 static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); }
19381 static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); }
19382 static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); }
19383 static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); }
19384
19385
19386 /* IMMDevice */
19387 typedef struct
19388 {
19389 /* IUnknown */
19390 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject);
19391 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis);
19392 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis);
19393
19394 /* IMMDevice */
19395 HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface);
19396 HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties);
19397 HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, LPWSTR *pID);
19398 HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState);
19399 } ma_IMMDeviceVtbl;
19400 struct ma_IMMDevice
19401 {
19402 ma_IMMDeviceVtbl* lpVtbl;
19403 };
19404 static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
19405 static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); }
19406 static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); }
19407 static MA_INLINE HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); }
19408 static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); }
19409 static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, LPWSTR *pID) { return pThis->lpVtbl->GetId(pThis, pID); }
19410 static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); }
19411#else
19412 /* IActivateAudioInterfaceAsyncOperation */
19413 typedef struct
19414 {
19415 /* IUnknown */
19416 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject);
19417 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
19418 ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
19419
19420 /* IActivateAudioInterfaceAsyncOperation */
19421 HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface);
19422 } ma_IActivateAudioInterfaceAsyncOperationVtbl;
19423 struct ma_IActivateAudioInterfaceAsyncOperation
19424 {
19425 ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl;
19426 };
19427 static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
19428 static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); }
19429 static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); }
19430 static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); }
19431#endif
19432
19433/* IPropertyStore */
19434typedef struct
19435{
19436 /* IUnknown */
19437 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject);
19438 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis);
19439 ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis);
19440
19441 /* IPropertyStore */
19442 HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount);
19443 HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey);
19444 HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar);
19445 HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar);
19446 HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis);
19447} ma_IPropertyStoreVtbl;
19448struct ma_IPropertyStore
19449{
19450 ma_IPropertyStoreVtbl* lpVtbl;
19451};
19452static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
19453static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); }
19454static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); }
19455static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); }
19456static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); }
19457static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); }
19458static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); }
19459static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); }
19460
19461
19462/* IAudioClient */
19463typedef struct
19464{
19465 /* IUnknown */
19466 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject);
19467 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis);
19468 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis);
19469
19470 /* IAudioClient */
19471 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
19472 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames);
19473 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency);
19474 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames);
19475 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
19476 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat);
19477 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
19478 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis);
19479 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis);
19480 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis);
19481 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle);
19482 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp);
19483} ma_IAudioClientVtbl;
19484struct ma_IAudioClient
19485{
19486 ma_IAudioClientVtbl* lpVtbl;
19487};
19488static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
19489static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
19490static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); }
19491static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }
19492static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
19493static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
19494static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
19495static MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }
19496static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
19497static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
19498static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); }
19499static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); }
19500static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); }
19501static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
19502static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
19503
19504/* IAudioClient2 */
19505typedef struct
19506{
19507 /* IUnknown */
19508 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject);
19509 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis);
19510 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis);
19511
19512 /* IAudioClient */
19513 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
19514 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames);
19515 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency);
19516 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames);
19517 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
19518 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat);
19519 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
19520 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis);
19521 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis);
19522 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis);
19523 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle);
19524 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp);
19525
19526 /* IAudioClient2 */
19527 HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
19528 HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties);
19529 HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
19530} ma_IAudioClient2Vtbl;
19531struct ma_IAudioClient2
19532{
19533 ma_IAudioClient2Vtbl* lpVtbl;
19534};
19535static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
19536static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); }
19537static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); }
19538static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }
19539static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
19540static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
19541static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
19542static MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }
19543static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
19544static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
19545static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); }
19546static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); }
19547static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); }
19548static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
19549static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
19550static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
19551static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
19552static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); }
19553
19554
19555/* IAudioClient3 */
19556typedef struct
19557{
19558 /* IUnknown */
19559 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject);
19560 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis);
19561 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis);
19562
19563 /* IAudioClient */
19564 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
19565 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames);
19566 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency);
19567 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames);
19568 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
19569 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat);
19570 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
19571 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis);
19572 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis);
19573 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis);
19574 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle);
19575 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp);
19576
19577 /* IAudioClient2 */
19578 HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
19579 HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties);
19580 HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
19581
19582 /* IAudioClient3 */
19583 HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames);
19584 HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames);
19585 HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
19586} ma_IAudioClient3Vtbl;
19587struct ma_IAudioClient3
19588{
19589 ma_IAudioClient3Vtbl* lpVtbl;
19590};
19591static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
19592static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); }
19593static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); }
19594static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }
19595static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
19596static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
19597static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
19598static MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }
19599static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
19600static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
19601static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); }
19602static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); }
19603static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); }
19604static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
19605static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
19606static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
19607static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
19608static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); }
19609static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); }
19610static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); }
19611static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); }
19612
19613
19614/* IAudioRenderClient */
19615typedef struct
19616{
19617 /* IUnknown */
19618 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject);
19619 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis);
19620 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis);
19621
19622 /* IAudioRenderClient */
19623 HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData);
19624 HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags);
19625} ma_IAudioRenderClientVtbl;
19626struct ma_IAudioRenderClient
19627{
19628 ma_IAudioRenderClientVtbl* lpVtbl;
19629};
19630static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
19631static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
19632static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); }
19633static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); }
19634static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); }
19635
19636
19637/* IAudioCaptureClient */
19638typedef struct
19639{
19640 /* IUnknown */
19641 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject);
19642 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis);
19643 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis);
19644
19645 /* IAudioRenderClient */
19646 HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition);
19647 HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead);
19648 HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket);
19649} ma_IAudioCaptureClientVtbl;
19650struct ma_IAudioCaptureClient
19651{
19652 ma_IAudioCaptureClientVtbl* lpVtbl;
19653};
19654static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
19655static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
19656static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); }
19657static 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); }
19658static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); }
19659static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); }
19660
19661#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK)
19662#include <mmdeviceapi.h>
19663typedef struct ma_completion_handler_uwp ma_completion_handler_uwp;
19664
19665typedef struct
19666{
19667 /* IUnknown */
19668 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject);
19669 ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis);
19670 ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis);
19671
19672 /* IActivateAudioInterfaceCompletionHandler */
19673 HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation);
19674} ma_completion_handler_uwp_vtbl;
19675struct ma_completion_handler_uwp
19676{
19677 ma_completion_handler_uwp_vtbl* lpVtbl;
19678 MA_ATOMIC(4, ma_uint32) counter;
19679 HANDLE hEvent;
19680};
19681
19682static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject)
19683{
19684 /*
19685 We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To
19686 "implement" this, we just make sure we return pThis when the IAgileObject is requested.
19687 */
19688 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)) {
19689 *ppObject = NULL;
19690 return E_NOINTERFACE;
19691 }
19692
19693 /* Getting here means the IID is IUnknown or IMMNotificationClient. */
19694 *ppObject = (void*)pThis;
19695 ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis);
19696 return S_OK;
19697}
19698
19699static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis)
19700{
19701 return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1;
19702}
19703
19704static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis)
19705{
19706 ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1;
19707 if (newRefCount == 0) {
19708 return 0; /* We don't free anything here because we never allocate the object on the heap. */
19709 }
19710
19711 return (ULONG)newRefCount;
19712}
19713
19714static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation)
19715{
19716 (void)pActivateOperation;
19717 SetEvent(pThis->hEvent);
19718 return S_OK;
19719}
19720
19721
19722static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = {
19723 ma_completion_handler_uwp_QueryInterface,
19724 ma_completion_handler_uwp_AddRef,
19725 ma_completion_handler_uwp_Release,
19726 ma_completion_handler_uwp_ActivateCompleted
19727};
19728
19729static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler)
19730{
19731 MA_ASSERT(pHandler != NULL);
19732 MA_ZERO_OBJECT(pHandler);
19733
19734 pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance;
19735 pHandler->counter = 1;
19736 pHandler->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
19737 if (pHandler->hEvent == NULL) {
19738 return ma_result_from_GetLastError(GetLastError());
19739 }
19740
19741 return MA_SUCCESS;
19742}
19743
19744static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler)
19745{
19746 if (pHandler->hEvent != NULL) {
19747 CloseHandle(pHandler->hEvent);
19748 }
19749}
19750
19751static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler)
19752{
19753 WaitForSingleObject(pHandler->hEvent, INFINITE);
19754}
19755#endif /* !MA_WIN32_DESKTOP */
19756
19757/* We need a virtual table for our notification client object that's used for detecting changes to the default device. */
19758#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
19759static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject)
19760{
19761 /*
19762 We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else
19763 we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK.
19764 */
19765 if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) {
19766 *ppObject = NULL;
19767 return E_NOINTERFACE;
19768 }
19769
19770 /* Getting here means the IID is IUnknown or IMMNotificationClient. */
19771 *ppObject = (void*)pThis;
19772 ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis);
19773 return S_OK;
19774}
19775
19776static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis)
19777{
19778 return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1;
19779}
19780
19781static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis)
19782{
19783 ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1;
19784 if (newRefCount == 0) {
19785 return 0; /* We don't free anything here because we never allocate the object on the heap. */
19786 }
19787
19788 return (ULONG)newRefCount;
19789}
19790
19791static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState)
19792{
19793 ma_bool32 isThisDevice = MA_FALSE;
19794 ma_bool32 isCapture = MA_FALSE;
19795 ma_bool32 isPlayback = MA_FALSE;
19796
19797#ifdef MA_DEBUG_OUTPUT
19798 /*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);*/
19799#endif
19800
19801 /*
19802 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
19803 that the device is disabled or has been unplugged.
19804 */
19805 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)) {
19806 isCapture = MA_TRUE;
19807 if (wcscmp(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) {
19808 isThisDevice = MA_TRUE;
19809 }
19810 }
19811
19812 if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) {
19813 isPlayback = MA_TRUE;
19814 if (wcscmp(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) {
19815 isThisDevice = MA_TRUE;
19816 }
19817 }
19818
19819
19820 /*
19821 If the device ID matches our device we need to mark our device as detached and stop it. When a
19822 device is added in OnDeviceAdded(), we'll restart it. We only mark it as detached if the device
19823 was started at the time of being removed.
19824 */
19825 if (isThisDevice) {
19826 if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) {
19827 /*
19828 Unplugged or otherwise unavailable. Mark as detached if we were in a playing state. We'll
19829 use this to determine whether or not we need to automatically start the device when it's
19830 plugged back in again.
19831 */
19832 if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) {
19833 if (isPlayback) {
19834 pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE;
19835 }
19836 if (isCapture) {
19837 pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE;
19838 }
19839
19840 ma_device_stop(pThis->pDevice);
19841 }
19842 }
19843
19844 if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) {
19845 /* The device was activated. If we were detached, we need to start it again. */
19846 ma_bool8 tryRestartingDevice = MA_FALSE;
19847
19848 if (isPlayback) {
19849 if (pThis->pDevice->wasapi.isDetachedPlayback) {
19850 pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
19851 ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback);
19852 tryRestartingDevice = MA_TRUE;
19853 }
19854 }
19855
19856 if (isCapture) {
19857 if (pThis->pDevice->wasapi.isDetachedCapture) {
19858 pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
19859 ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
19860 tryRestartingDevice = MA_TRUE;
19861 }
19862 }
19863
19864 if (tryRestartingDevice) {
19865 if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) {
19866 ma_device_start(pThis->pDevice);
19867 }
19868 }
19869 }
19870 }
19871
19872 return S_OK;
19873}
19874
19875static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
19876{
19877#ifdef MA_DEBUG_OUTPUT
19878 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
19879#endif
19880
19881 /* We don't need to worry about this event for our purposes. */
19882 (void)pThis;
19883 (void)pDeviceID;
19884 return S_OK;
19885}
19886
19887static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
19888{
19889#ifdef MA_DEBUG_OUTPUT
19890 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
19891#endif
19892
19893 /* We don't need to worry about this event for our purposes. */
19894 (void)pThis;
19895 (void)pDeviceID;
19896 return S_OK;
19897}
19898
19899static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID)
19900{
19901#ifdef MA_DEBUG_OUTPUT
19902 /*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)");*/
19903#endif
19904
19905 /* We only ever use the eConsole role in miniaudio. */
19906 if (role != ma_eConsole) {
19907 ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting: role != eConsole\n");
19908 return S_OK;
19909 }
19910
19911 /* We only care about devices with the same data flow and role as the current device. */
19912 if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) ||
19913 (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture)) {
19914 ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n");
19915 return S_OK;
19916 }
19917
19918 /* Don't do automatic stream routing if we're not allowed. */
19919 if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) ||
19920 (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) {
19921 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");
19922 return S_OK;
19923 }
19924
19925 /*
19926 Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to
19927 AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once
19928 it's fixed.
19929 */
19930 if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) ||
19931 (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) {
19932 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");
19933 return S_OK;
19934 }
19935
19936
19937
19938
19939 /*
19940 Second attempt at device rerouting. We're going to retrieve the device's state at the time of
19941 the route change. We're then going to stop the device, reinitialize the device, and then start
19942 it again if the state before stopping was ma_device_state_started.
19943 */
19944 {
19945 ma_uint32 previousState = ma_device_get_state(pThis->pDevice);
19946 ma_bool8 restartDevice = MA_FALSE;
19947
19948 if (previousState == ma_device_state_started) {
19949 ma_device_stop(pThis->pDevice);
19950 restartDevice = MA_TRUE;
19951 }
19952
19953 if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */
19954 if (dataFlow == ma_eRender) {
19955 ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback);
19956
19957 if (pThis->pDevice->wasapi.isDetachedPlayback) {
19958 pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
19959
19960 if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) {
19961 restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */
19962 } else {
19963 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. */
19964 }
19965 }
19966 } else {
19967 ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
19968
19969 if (pThis->pDevice->wasapi.isDetachedCapture) {
19970 pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
19971
19972 if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) {
19973 restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */
19974 } else {
19975 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. */
19976 }
19977 }
19978 }
19979
19980 if (restartDevice) {
19981 ma_device_start(pThis->pDevice);
19982 }
19983 }
19984 }
19985
19986 return S_OK;
19987}
19988
19989static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key)
19990{
19991#ifdef MA_DEBUG_OUTPUT
19992 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
19993#endif
19994
19995 (void)pThis;
19996 (void)pDeviceID;
19997 (void)key;
19998 return S_OK;
19999}
20000
20001static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = {
20002 ma_IMMNotificationClient_QueryInterface,
20003 ma_IMMNotificationClient_AddRef,
20004 ma_IMMNotificationClient_Release,
20005 ma_IMMNotificationClient_OnDeviceStateChanged,
20006 ma_IMMNotificationClient_OnDeviceAdded,
20007 ma_IMMNotificationClient_OnDeviceRemoved,
20008 ma_IMMNotificationClient_OnDefaultDeviceChanged,
20009 ma_IMMNotificationClient_OnPropertyValueChanged
20010};
20011#endif /* MA_WIN32_DESKTOP */
20012
20013#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
20014typedef ma_IMMDevice ma_WASAPIDeviceInterface;
20015#else
20016typedef ma_IUnknown ma_WASAPIDeviceInterface;
20017#endif
20018
20019
20020#define MA_CONTEXT_COMMAND_QUIT__WASAPI 1
20021#define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI 2
20022#define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3
20023
20024static ma_context_command__wasapi ma_context_init_command__wasapi(int code)
20025{
20027
20028 MA_ZERO_OBJECT(&cmd);
20029 cmd.code = code;
20030
20031 return cmd;
20032}
20033
20034static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd)
20035{
20036 /* For now we are doing everything synchronously, but I might relax this later if the need arises. */
20037 ma_result result;
20038 ma_bool32 isUsingLocalEvent = MA_FALSE;
20039 ma_event localEvent;
20040
20041 MA_ASSERT(pContext != NULL);
20042 MA_ASSERT(pCmd != NULL);
20043
20044 if (pCmd->pEvent == NULL) {
20045 isUsingLocalEvent = MA_TRUE;
20046
20047 result = ma_event_init(&localEvent);
20048 if (result != MA_SUCCESS) {
20049 return result; /* Failed to create the event for this command. */
20050 }
20051 }
20052
20053 /* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */
20054 ma_mutex_lock(&pContext->wasapi.commandLock);
20055 {
20056 ma_uint32 index;
20057
20058 /* Spin until we've got some space available. */
20059 while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) {
20060 ma_yield();
20061 }
20062
20063 /* Space is now available. Can safely add to the list. */
20064 index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands);
20065 pContext->wasapi.commands[index] = *pCmd;
20066 pContext->wasapi.commands[index].pEvent = &localEvent;
20067 pContext->wasapi.commandCount += 1;
20068
20069 /* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */
20070 ma_semaphore_release(&pContext->wasapi.commandSem);
20071 }
20072 ma_mutex_unlock(&pContext->wasapi.commandLock);
20073
20074 if (isUsingLocalEvent) {
20075 ma_event_wait(&localEvent);
20076 ma_event_uninit(&localEvent);
20077 }
20078
20079 return MA_SUCCESS;
20080}
20081
20082static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd)
20083{
20084 ma_result result = MA_SUCCESS;
20085
20086 MA_ASSERT(pContext != NULL);
20087 MA_ASSERT(pCmd != NULL);
20088
20089 result = ma_semaphore_wait(&pContext->wasapi.commandSem);
20090 if (result == MA_SUCCESS) {
20091 ma_mutex_lock(&pContext->wasapi.commandLock);
20092 {
20093 *pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex];
20094 pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands);
20095 pContext->wasapi.commandCount -= 1;
20096 }
20097 ma_mutex_unlock(&pContext->wasapi.commandLock);
20098 }
20099
20100 return result;
20101}
20102
20103static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData)
20104{
20105 ma_result result;
20106 ma_context* pContext = (ma_context*)pUserData;
20107 MA_ASSERT(pContext != NULL);
20108
20109 for (;;) {
20111 result = ma_context_next_command__wasapi(pContext, &cmd);
20112 if (result != MA_SUCCESS) {
20113 break;
20114 }
20115
20116 switch (cmd.code)
20117 {
20118 case MA_CONTEXT_COMMAND_QUIT__WASAPI:
20119 {
20120 /* Do nothing. Handled after the switch. */
20121 } break;
20122
20123 case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI:
20124 {
20126 *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService));
20127 } else {
20128 *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService));
20129 }
20130 } break;
20131
20132 case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI:
20133 {
20135 if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) {
20136 ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback);
20137 cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL;
20138 }
20139 }
20140
20142 if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) {
20143 ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture);
20144 cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL;
20145 }
20146 }
20147 } break;
20148
20149 default:
20150 {
20151 /* Unknown command. Ignore it, but trigger an assert in debug mode so we're aware of it. */
20152 MA_ASSERT(MA_FALSE);
20153 } break;
20154 }
20155
20156 if (cmd.pEvent != NULL) {
20158 }
20159
20160 if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) {
20161 break; /* Received a quit message. Get out of here. */
20162 }
20163 }
20164
20165 return (ma_thread_result)0;
20166}
20167
20168static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService)
20169{
20170 ma_result result;
20171 ma_result cmdResult;
20172 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI);
20173 cmd.data.createAudioClient.deviceType = deviceType;
20174 cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient;
20175 cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService;
20176 cmd.data.createAudioClient.pResult = &cmdResult; /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */
20177
20178 result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */
20179 if (result != MA_SUCCESS) {
20180 return result;
20181 }
20182
20183 return *cmd.data.createAudioClient.pResult;
20184}
20185
20186#if 0 /* Not used at the moment, but leaving here for future use. */
20187static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType)
20188{
20189 ma_result result;
20190 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI);
20191 cmd.data.releaseAudioClient.pDevice = pDevice;
20192 cmd.data.releaseAudioClient.deviceType = deviceType;
20193
20194 result = ma_context_post_command__wasapi(pDevice->pContext, &cmd); /* This will not return until the command has actually been run. */
20195 if (result != MA_SUCCESS) {
20196 return result;
20197 }
20198
20199 return MA_SUCCESS;
20200}
20201#endif
20202
20203
20204static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo)
20205{
20206 MA_ASSERT(pWF != NULL);
20207 MA_ASSERT(pInfo != NULL);
20208
20209 if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) {
20210 return; /* Too many data formats. Need to ignore this one. Don't think this should ever happen with WASAPI. */
20211 }
20212
20213 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format = ma_format_from_WAVEFORMATEX(pWF);
20214 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels = pWF->nChannels;
20215 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec;
20217 pInfo->nativeDataFormatCount += 1;
20218}
20219
20220static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo)
20221{
20222 HRESULT hr;
20223 WAVEFORMATEX* pWF = NULL;
20224
20225 MA_ASSERT(pAudioClient != NULL);
20226 MA_ASSERT(pInfo != NULL);
20227
20228 /* Shared Mode. We use GetMixFormat() here. */
20229 hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (WAVEFORMATEX**)&pWF);
20230 if (SUCCEEDED(hr)) {
20231 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo);
20232 } else {
20233 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval.");
20234 return ma_result_from_HRESULT(hr);
20235 }
20236
20237 /*
20238 Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on
20239 UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on
20240 out, MA_SUCCESS is guaranteed to be returned.
20241 */
20242 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
20243 {
20244 ma_IPropertyStore *pProperties;
20245
20246 /*
20247 The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is
20248 correct which will simplify our searching.
20249 */
20250 hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties);
20251 if (SUCCEEDED(hr)) {
20252 PROPVARIANT var;
20253 ma_PropVariantInit(&var);
20254
20255 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var);
20256 if (SUCCEEDED(hr)) {
20257 pWF = (WAVEFORMATEX*)var.blob.pBlobData;
20258
20259 /*
20260 In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format
20261 first. If this fails, fall back to a search.
20262 */
20263 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL);
20264 if (SUCCEEDED(hr)) {
20265 /* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */
20266 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo);
20267 } else {
20268 /*
20269 The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel
20270 count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format.
20271 */
20272 ma_uint32 channels = pWF->nChannels;
20273 ma_channel defaultChannelMap[MA_MAX_CHANNELS];
20274 WAVEFORMATEXTENSIBLE wf;
20275 ma_bool32 found;
20276 ma_uint32 iFormat;
20277
20278 /* Make sure we don't overflow the channel map. */
20279 if (channels > MA_MAX_CHANNELS) {
20280 channels = MA_MAX_CHANNELS;
20281 }
20282
20283 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels);
20284
20285 MA_ZERO_OBJECT(&wf);
20286 wf.Format.cbSize = sizeof(wf);
20287 wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
20288 wf.Format.nChannels = (WORD)channels;
20289 wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels);
20290
20291 found = MA_FALSE;
20292 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) {
20293 ma_format format = g_maFormatPriorities[iFormat];
20294 ma_uint32 iSampleRate;
20295
20296 wf.Format.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8);
20297 wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8);
20298 wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
20299 wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.Format.wBitsPerSample;
20300 if (format == ma_format_f32) {
20301 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
20302 } else {
20303 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
20304 }
20305
20306 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) {
20307 wf.Format.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate];
20308
20309 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL);
20310 if (SUCCEEDED(hr)) {
20311 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo);
20312 found = MA_TRUE;
20313 break;
20314 }
20315 }
20316
20317 if (found) {
20318 break;
20319 }
20320 }
20321
20322 ma_PropVariantClear(pContext, &var);
20323
20324 if (!found) {
20325 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to find suitable device format for device info retrieval.");
20326 }
20327 }
20328 } else {
20329 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to retrieve device format for device info retrieval.");
20330 }
20331
20332 ma_IPropertyStore_Release(pProperties);
20333 } else {
20334 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval.");
20335 }
20336 }
20337 #endif
20338
20339 return MA_SUCCESS;
20340}
20341
20342#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
20343static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType)
20344{
20345 if (deviceType == ma_device_type_playback) {
20346 return ma_eRender;
20347 } else if (deviceType == ma_device_type_capture) {
20348 return ma_eCapture;
20349 } else {
20350 MA_ASSERT(MA_FALSE);
20351 return ma_eRender; /* Should never hit this. */
20352 }
20353}
20354
20355static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator)
20356{
20357 HRESULT hr;
20358 ma_IMMDeviceEnumerator* pDeviceEnumerator;
20359
20360 MA_ASSERT(pContext != NULL);
20361 MA_ASSERT(ppDeviceEnumerator != NULL);
20362
20363 *ppDeviceEnumerator = NULL; /* Safety. */
20364
20365 hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
20366 if (FAILED(hr)) {
20367 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
20368 return ma_result_from_HRESULT(hr);
20369 }
20370
20371 *ppDeviceEnumerator = pDeviceEnumerator;
20372
20373 return MA_SUCCESS;
20374}
20375
20376static LPWSTR ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType)
20377{
20378 HRESULT hr;
20379 ma_IMMDevice* pMMDefaultDevice = NULL;
20380 LPWSTR pDefaultDeviceID = NULL;
20381 ma_EDataFlow dataFlow;
20382 ma_ERole role;
20383
20384 MA_ASSERT(pContext != NULL);
20385 MA_ASSERT(pDeviceEnumerator != NULL);
20386
20387 (void)pContext;
20388
20389 /* Grab the EDataFlow type from the device type. */
20390 dataFlow = ma_device_type_to_EDataFlow(deviceType);
20391
20392 /* The role is always eConsole, but we may make this configurable later. */
20393 role = ma_eConsole;
20394
20395 hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice);
20396 if (FAILED(hr)) {
20397 return NULL;
20398 }
20399
20400 hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID);
20401
20402 ma_IMMDevice_Release(pMMDefaultDevice);
20403 pMMDefaultDevice = NULL;
20404
20405 if (FAILED(hr)) {
20406 return NULL;
20407 }
20408
20409 return pDefaultDeviceID;
20410}
20411
20412static LPWSTR ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */
20413{
20414 ma_result result;
20415 ma_IMMDeviceEnumerator* pDeviceEnumerator;
20416 LPWSTR pDefaultDeviceID = NULL;
20417
20418 MA_ASSERT(pContext != NULL);
20419
20420 result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator);
20421 if (result != MA_SUCCESS) {
20422 return NULL;
20423 }
20424
20425 pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
20426
20427 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
20428 return pDefaultDeviceID;
20429}
20430
20431static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice)
20432{
20433 ma_IMMDeviceEnumerator* pDeviceEnumerator;
20434 HRESULT hr;
20435
20436 MA_ASSERT(pContext != NULL);
20437 MA_ASSERT(ppMMDevice != NULL);
20438
20439 hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
20440 if (FAILED(hr)) {
20441 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.");
20442 return ma_result_from_HRESULT(hr);
20443 }
20444
20445 if (pDeviceID == NULL) {
20446 hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice);
20447 } else {
20448 hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice);
20449 }
20450
20451 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
20452 if (FAILED(hr)) {
20453 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.");
20454 return ma_result_from_HRESULT(hr);
20455 }
20456
20457 return MA_SUCCESS;
20458}
20459
20460static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID)
20461{
20462 LPWSTR pDeviceIDString;
20463 HRESULT hr;
20464
20465 MA_ASSERT(pDeviceID != NULL);
20466
20467 hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString);
20468 if (SUCCEEDED(hr)) {
20469 size_t idlen = wcslen(pDeviceIDString);
20470 if (idlen+1 > ma_countof(pDeviceID->wasapi)) {
20471 ma_CoTaskMemFree(pContext, pDeviceIDString);
20472 MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. */
20473 return MA_ERROR;
20474 }
20475
20476 MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t));
20477 pDeviceID->wasapi[idlen] = '\0';
20478
20479 ma_CoTaskMemFree(pContext, pDeviceIDString);
20480
20481 return MA_SUCCESS;
20482 }
20483
20484 return MA_ERROR;
20485}
20486
20487static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, LPWSTR pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo)
20488{
20489 ma_result result;
20490 HRESULT hr;
20491
20492 MA_ASSERT(pContext != NULL);
20493 MA_ASSERT(pMMDevice != NULL);
20494 MA_ASSERT(pInfo != NULL);
20495
20496 /* ID. */
20497 result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id);
20498 if (result == MA_SUCCESS) {
20499 if (pDefaultDeviceID != NULL) {
20500 if (wcscmp(pInfo->id.wasapi, pDefaultDeviceID) == 0) {
20501 pInfo->isDefault = MA_TRUE;
20502 }
20503 }
20504 }
20505
20506 /* Description / Friendly Name */
20507 {
20508 ma_IPropertyStore *pProperties;
20509 hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties);
20510 if (SUCCEEDED(hr)) {
20511 PROPVARIANT var;
20512
20513 ma_PropVariantInit(&var);
20514 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var);
20515 if (SUCCEEDED(hr)) {
20516 WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);
20517 ma_PropVariantClear(pContext, &var);
20518 }
20519
20520 ma_IPropertyStore_Release(pProperties);
20521 }
20522 }
20523
20524 /* Format */
20525 if (!onlySimpleInfo) {
20526 ma_IAudioClient* pAudioClient;
20527 hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
20528 if (SUCCEEDED(hr)) {
20529 result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo);
20530
20531 ma_IAudioClient_Release(pAudioClient);
20532 return result;
20533 } else {
20534 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval.");
20535 return ma_result_from_HRESULT(hr);
20536 }
20537 }
20538
20539 return MA_SUCCESS;
20540}
20541
20542static 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)
20543{
20544 ma_result result = MA_SUCCESS;
20545 UINT deviceCount;
20546 HRESULT hr;
20547 ma_uint32 iDevice;
20548 LPWSTR pDefaultDeviceID = NULL;
20549 ma_IMMDeviceCollection* pDeviceCollection = NULL;
20550
20551 MA_ASSERT(pContext != NULL);
20552 MA_ASSERT(callback != NULL);
20553
20554 /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */
20555 pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
20556
20557 /* We need to enumerate the devices which returns a device collection. */
20558 hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection);
20559 if (SUCCEEDED(hr)) {
20560 hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount);
20561 if (FAILED(hr)) {
20562 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.");
20563 result = ma_result_from_HRESULT(hr);
20564 goto done;
20565 }
20566
20567 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
20568 ma_device_info deviceInfo;
20569 ma_IMMDevice* pMMDevice;
20570
20571 MA_ZERO_OBJECT(&deviceInfo);
20572
20573 hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice);
20574 if (SUCCEEDED(hr)) {
20575 result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */
20576
20577 ma_IMMDevice_Release(pMMDevice);
20578 if (result == MA_SUCCESS) {
20579 ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
20580 if (cbResult == MA_FALSE) {
20581 break;
20582 }
20583 }
20584 }
20585 }
20586 }
20587
20588done:
20589 if (pDefaultDeviceID != NULL) {
20590 ma_CoTaskMemFree(pContext, pDefaultDeviceID);
20591 pDefaultDeviceID = NULL;
20592 }
20593
20594 if (pDeviceCollection != NULL) {
20595 ma_IMMDeviceCollection_Release(pDeviceCollection);
20596 pDeviceCollection = NULL;
20597 }
20598
20599 return result;
20600}
20601
20602static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice)
20603{
20604 ma_result result;
20605 HRESULT hr;
20606
20607 MA_ASSERT(pContext != NULL);
20608 MA_ASSERT(ppAudioClient != NULL);
20609 MA_ASSERT(ppMMDevice != NULL);
20610
20611 result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice);
20612 if (result != MA_SUCCESS) {
20613 return result;
20614 }
20615
20616 hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)ppAudioClient);
20617 if (FAILED(hr)) {
20618 return ma_result_from_HRESULT(hr);
20619 }
20620
20621 return MA_SUCCESS;
20622}
20623#else
20624static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface)
20625{
20626 ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
20627 ma_completion_handler_uwp completionHandler;
20628 IID iid;
20629 LPOLESTR iidStr;
20630 HRESULT hr;
20631 ma_result result;
20632 HRESULT activateResult;
20633 ma_IUnknown* pActivatedInterface;
20634
20635 MA_ASSERT(pContext != NULL);
20636 MA_ASSERT(ppAudioClient != NULL);
20637
20638 if (pDeviceID != NULL) {
20639 MA_COPY_MEMORY(&iid, pDeviceID->wasapi, sizeof(iid));
20640 } else {
20641 if (deviceType == ma_device_type_playback) {
20642 iid = MA_IID_DEVINTERFACE_AUDIO_RENDER;
20643 } else {
20644 iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE;
20645 }
20646 }
20647
20648#if defined(__cplusplus)
20649 hr = StringFromIID(iid, &iidStr);
20650#else
20651 hr = StringFromIID(&iid, &iidStr);
20652#endif
20653 if (FAILED(hr)) {
20654 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.");
20655 return ma_result_from_HRESULT(hr);
20656 }
20657
20658 result = ma_completion_handler_uwp_init(&completionHandler);
20659 if (result != MA_SUCCESS) {
20660 ma_CoTaskMemFree(pContext, iidStr);
20661 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().");
20662 return result;
20663 }
20664
20665#if defined(__cplusplus)
20666 hr = ActivateAudioInterfaceAsync(iidStr, MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
20667#else
20668 hr = ActivateAudioInterfaceAsync(iidStr, &MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
20669#endif
20670 if (FAILED(hr)) {
20671 ma_completion_handler_uwp_uninit(&completionHandler);
20672 ma_CoTaskMemFree(pContext, iidStr);
20673 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.");
20674 return ma_result_from_HRESULT(hr);
20675 }
20676
20677 ma_CoTaskMemFree(pContext, iidStr);
20678
20679 /* Wait for the async operation for finish. */
20680 ma_completion_handler_uwp_wait(&completionHandler);
20681 ma_completion_handler_uwp_uninit(&completionHandler);
20682
20683 hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);
20684 ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
20685
20686 if (FAILED(hr) || FAILED(activateResult)) {
20687 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.");
20688 return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult);
20689 }
20690
20691 /* Here is where we grab the IAudioClient interface. */
20692 hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient);
20693 if (FAILED(hr)) {
20694 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.");
20695 return ma_result_from_HRESULT(hr);
20696 }
20697
20698 if (ppActivatedInterface) {
20699 *ppActivatedInterface = pActivatedInterface;
20700 } else {
20701 ma_IUnknown_Release(pActivatedInterface);
20702 }
20703
20704 return MA_SUCCESS;
20705}
20706#endif
20707
20708static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface)
20709{
20710#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
20711 return ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface);
20712#else
20713 return ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface);
20714#endif
20715}
20716
20717
20718static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
20719{
20720 /* Different enumeration for desktop and UWP. */
20721#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
20722 /* Desktop */
20723 HRESULT hr;
20724 ma_IMMDeviceEnumerator* pDeviceEnumerator;
20725
20726 hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
20727 if (FAILED(hr)) {
20728 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
20729 return ma_result_from_HRESULT(hr);
20730 }
20731
20732 ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData);
20733 ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData);
20734
20735 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
20736#else
20737 /*
20738 UWP
20739
20740 The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate
20741 over devices without using MMDevice, I'm restricting devices to defaults.
20742
20743 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/
20744 */
20745 if (callback) {
20746 ma_bool32 cbResult = MA_TRUE;
20747
20748 /* Playback. */
20749 if (cbResult) {
20750 ma_device_info deviceInfo;
20751 MA_ZERO_OBJECT(&deviceInfo);
20752 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
20753 deviceInfo.isDefault = MA_TRUE;
20754 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
20755 }
20756
20757 /* Capture. */
20758 if (cbResult) {
20759 ma_device_info deviceInfo;
20760 MA_ZERO_OBJECT(&deviceInfo);
20761 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
20762 deviceInfo.isDefault = MA_TRUE;
20763 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
20764 }
20765 }
20766#endif
20767
20768 return MA_SUCCESS;
20769}
20770
20771static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
20772{
20773#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
20774 ma_result result;
20775 ma_IMMDevice* pMMDevice = NULL;
20776 LPWSTR pDefaultDeviceID = NULL;
20777
20778 result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice);
20779 if (result != MA_SUCCESS) {
20780 return result;
20781 }
20782
20783 /* We need the default device ID so we can set the isDefault flag in the device info. */
20784 pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType);
20785
20786 result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */
20787
20788 if (pDefaultDeviceID != NULL) {
20789 ma_CoTaskMemFree(pContext, pDefaultDeviceID);
20790 pDefaultDeviceID = NULL;
20791 }
20792
20793 ma_IMMDevice_Release(pMMDevice);
20794
20795 return result;
20796#else
20797 ma_IAudioClient* pAudioClient;
20798 ma_result result;
20799
20800 /* UWP currently only uses default devices. */
20801 if (deviceType == ma_device_type_playback) {
20802 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
20803 } else {
20804 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
20805 }
20806
20807 result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, &pAudioClient, NULL);
20808 if (result != MA_SUCCESS) {
20809 return result;
20810 }
20811
20812 result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo);
20813
20814 pDeviceInfo->isDefault = MA_TRUE; /* UWP only supports default devices. */
20815
20816 ma_IAudioClient_Release(pAudioClient);
20817 return result;
20818#endif
20819}
20820
20821static ma_result ma_device_uninit__wasapi(ma_device* pDevice)
20822{
20823 MA_ASSERT(pDevice != NULL);
20824
20825#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
20826 if (pDevice->wasapi.pDeviceEnumerator) {
20827 ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient);
20828 ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator);
20829 }
20830#endif
20831
20832 if (pDevice->wasapi.pRenderClient) {
20833 if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
20834 ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
20835 pDevice->wasapi.pMappedBufferPlayback = NULL;
20836 pDevice->wasapi.mappedBufferPlaybackCap = 0;
20837 pDevice->wasapi.mappedBufferPlaybackLen = 0;
20838 }
20839
20840 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
20841 }
20842 if (pDevice->wasapi.pCaptureClient) {
20843 if (pDevice->wasapi.pMappedBufferCapture != NULL) {
20844 ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
20845 pDevice->wasapi.pMappedBufferCapture = NULL;
20846 pDevice->wasapi.mappedBufferCaptureCap = 0;
20847 pDevice->wasapi.mappedBufferCaptureLen = 0;
20848 }
20849
20850 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
20851 }
20852
20853 if (pDevice->wasapi.pAudioClientPlayback) {
20854 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
20855 }
20856 if (pDevice->wasapi.pAudioClientCapture) {
20857 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
20858 }
20859
20860 if (pDevice->wasapi.hEventPlayback) {
20861 CloseHandle(pDevice->wasapi.hEventPlayback);
20862 }
20863 if (pDevice->wasapi.hEventCapture) {
20864 CloseHandle(pDevice->wasapi.hEventCapture);
20865 }
20866
20867 return MA_SUCCESS;
20868}
20869
20870
20871typedef struct
20872{
20873 /* Input. */
20874 ma_format formatIn;
20875 ma_uint32 channelsIn;
20876 ma_uint32 sampleRateIn;
20877 ma_channel channelMapIn[MA_MAX_CHANNELS];
20878 ma_uint32 periodSizeInFramesIn;
20879 ma_uint32 periodSizeInMillisecondsIn;
20880 ma_uint32 periodsIn;
20881 ma_share_mode shareMode;
20882 ma_performance_profile performanceProfile;
20883 ma_bool32 noAutoConvertSRC;
20884 ma_bool32 noDefaultQualitySRC;
20885 ma_bool32 noHardwareOffloading;
20886
20887 /* Output. */
20888 ma_IAudioClient* pAudioClient;
20889 ma_IAudioRenderClient* pRenderClient;
20890 ma_IAudioCaptureClient* pCaptureClient;
20891 ma_format formatOut;
20892 ma_uint32 channelsOut;
20893 ma_uint32 sampleRateOut;
20894 ma_channel channelMapOut[MA_MAX_CHANNELS];
20895 ma_uint32 periodSizeInFramesOut;
20896 ma_uint32 periodsOut;
20897 ma_bool32 usingAudioClient3;
20898 char deviceName[256];
20900} ma_device_init_internal_data__wasapi;
20901
20902static 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)
20903{
20904 HRESULT hr;
20905 ma_result result = MA_SUCCESS;
20906 const char* errorMsg = "";
20907 MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
20908 DWORD streamFlags = 0;
20909 MA_REFERENCE_TIME periodDurationInMicroseconds;
20910 ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE;
20911 WAVEFORMATEXTENSIBLE wf;
20912 ma_WASAPIDeviceInterface* pDeviceInterface = NULL;
20913 ma_IAudioClient2* pAudioClient2;
20914 ma_uint32 nativeSampleRate;
20915
20916 MA_ASSERT(pContext != NULL);
20917 MA_ASSERT(pData != NULL);
20918
20919 /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */
20920 if (deviceType == ma_device_type_duplex) {
20921 return MA_INVALID_ARGS;
20922 }
20923
20924 pData->pAudioClient = NULL;
20925 pData->pRenderClient = NULL;
20926 pData->pCaptureClient = NULL;
20927
20928 streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
20929 if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */
20930 streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
20931 }
20932 if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) {
20933 streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
20934 }
20935 if (deviceType == ma_device_type_loopback) {
20936 streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK;
20937 }
20938
20939 result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, &pData->pAudioClient, &pDeviceInterface);
20940 if (result != MA_SUCCESS) {
20941 goto done;
20942 }
20943
20944 MA_ZERO_OBJECT(&wf);
20945
20946 /* Try enabling hardware offloading. */
20947 if (!pData->noHardwareOffloading) {
20948 hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2);
20949 if (SUCCEEDED(hr)) {
20950 BOOL isHardwareOffloadingSupported = 0;
20951 hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported);
20952 if (SUCCEEDED(hr) && isHardwareOffloadingSupported) {
20953 ma_AudioClientProperties clientProperties;
20954 MA_ZERO_OBJECT(&clientProperties);
20955 clientProperties.cbSize = sizeof(clientProperties);
20956 clientProperties.bIsOffload = 1;
20957 clientProperties.eCategory = MA_AudioCategory_Other;
20958 ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties);
20959 }
20960
20961 pAudioClient2->lpVtbl->Release(pAudioClient2);
20962 }
20963 }
20964
20965 /* 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. */
20966 result = MA_FORMAT_NOT_SUPPORTED;
20967 if (pData->shareMode == ma_share_mode_exclusive) {
20968 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
20969 /* In exclusive mode on desktop we always use the backend's native format. */
20970 ma_IPropertyStore* pStore = NULL;
20971 hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore);
20972 if (SUCCEEDED(hr)) {
20973 PROPVARIANT prop;
20974 ma_PropVariantInit(&prop);
20975 hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop);
20976 if (SUCCEEDED(hr)) {
20977 WAVEFORMATEX* pActualFormat = (WAVEFORMATEX*)prop.blob.pBlobData;
20978 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL);
20979 if (SUCCEEDED(hr)) {
20980 MA_COPY_MEMORY(&wf, pActualFormat, sizeof(WAVEFORMATEXTENSIBLE));
20981 }
20982
20983 ma_PropVariantClear(pContext, &prop);
20984 }
20985
20986 ma_IPropertyStore_Release(pStore);
20987 }
20988 #else
20989 /*
20990 I do not know how to query the device's native format on UWP so for now I'm just disabling support for
20991 exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported()
20992 until you find one that works.
20993
20994 TODO: Add support for exclusive mode to UWP.
20995 */
20996 hr = S_FALSE;
20997 #endif
20998
20999 if (hr == S_OK) {
21000 shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE;
21001 result = MA_SUCCESS;
21002 } else {
21004 }
21005 } else {
21006 /* In shared mode we are always using the format reported by the operating system. */
21007 WAVEFORMATEXTENSIBLE* pNativeFormat = NULL;
21008 hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (WAVEFORMATEX**)&pNativeFormat);
21009 if (hr != S_OK) {
21010 result = MA_FORMAT_NOT_SUPPORTED;
21011 } else {
21012 MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(wf));
21013 result = MA_SUCCESS;
21014 }
21015
21016 ma_CoTaskMemFree(pContext, pNativeFormat);
21017
21018 shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
21019 }
21020
21021 /* Return an error if we still haven't found a format. */
21022 if (result != MA_SUCCESS) {
21023 errorMsg = "[WASAPI] Failed to find best device mix format.";
21024 goto done;
21025 }
21026
21027 /*
21028 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
21029 WASAPI to perform the sample rate conversion.
21030 */
21031 nativeSampleRate = wf.Format.nSamplesPerSec;
21032 if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) {
21033 wf.Format.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE;
21034 wf.Format.nAvgBytesPerSec = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign;
21035 }
21036
21037 pData->formatOut = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)&wf);
21038 if (pData->formatOut == ma_format_unknown) {
21039 /*
21040 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
21041 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
21042 completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED.
21043 */
21044 if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
21046 } else {
21047 result = MA_FORMAT_NOT_SUPPORTED;
21048 }
21049
21050 errorMsg = "[WASAPI] Native format not supported.";
21051 goto done;
21052 }
21053
21054 pData->channelsOut = wf.Format.nChannels;
21055 pData->sampleRateOut = wf.Format.nSamplesPerSec;
21056
21057 /* Get the internal channel map based on the channel mask. */
21058 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut);
21059
21060 /* Period size. */
21061 pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS;
21062 pData->periodSizeInFramesOut = pData->periodSizeInFramesIn;
21063 if (pData->periodSizeInFramesOut == 0) {
21064 if (pData->periodSizeInMillisecondsIn == 0) {
21065 if (pData->performanceProfile == ma_performance_profile_low_latency) {
21066 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.Format.nSamplesPerSec);
21067 } else {
21068 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.Format.nSamplesPerSec);
21069 }
21070 } else {
21071 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.Format.nSamplesPerSec);
21072 }
21073 }
21074
21075 periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.Format.nSamplesPerSec;
21076
21077
21078 /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */
21079 if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
21080 MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10;
21081
21082 /*
21083 If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing
21084 it and trying it again.
21085 */
21086 hr = E_FAIL;
21087 for (;;) {
21088 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
21089 if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) {
21090 if (bufferDuration > 500*10000) {
21091 break;
21092 } else {
21093 if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */
21094 break;
21095 }
21096
21097 bufferDuration = bufferDuration * 2;
21098 continue;
21099 }
21100 } else {
21101 break;
21102 }
21103 }
21104
21105 if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
21106 ma_uint32 bufferSizeInFrames;
21107 hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
21108 if (SUCCEEDED(hr)) {
21109 bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.Format.nSamplesPerSec * bufferSizeInFrames) + 0.5);
21110
21111 /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */
21112 ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
21113
21114 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21115 hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient);
21116 #else
21117 hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient);
21118 #endif
21119
21120 if (SUCCEEDED(hr)) {
21121 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
21122 }
21123 }
21124 }
21125
21126 if (FAILED(hr)) {
21127 /* 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. */
21128 if (hr == E_ACCESSDENIED) {
21129 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED;
21130 } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
21131 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY;
21132 } else {
21133 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr);
21134 }
21135 goto done;
21136 }
21137 }
21138
21139 if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) {
21140 /*
21141 Low latency shared mode via IAudioClient3.
21142
21143 NOTE
21144 ====
21145 Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the
21146 use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using
21147 any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to
21148 that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM.
21149 */
21150 #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE
21151 {
21152 if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.Format.nSamplesPerSec) {
21153 ma_IAudioClient3* pAudioClient3 = NULL;
21154 hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3);
21155 if (SUCCEEDED(hr)) {
21156 ma_uint32 defaultPeriodInFrames;
21157 ma_uint32 fundamentalPeriodInFrames;
21158 ma_uint32 minPeriodInFrames;
21159 ma_uint32 maxPeriodInFrames;
21160 hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames);
21161 if (SUCCEEDED(hr)) {
21162 ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut;
21163 ma_uint32 actualPeriodInFrames = desiredPeriodInFrames;
21164
21165 /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */
21166 actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames;
21167 actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames;
21168
21169 /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */
21170 actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames);
21171
21172 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames);
21173 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames);
21174 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames);
21175 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames);
21176 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames);
21177
21178 /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */
21179 if (actualPeriodInFrames >= desiredPeriodInFrames) {
21180 /*
21181 MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified,
21182 IAudioClient3_InitializeSharedAudioStream() will fail.
21183 */
21184 hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (WAVEFORMATEX*)&wf, NULL);
21185 if (SUCCEEDED(hr)) {
21186 wasInitializedUsingIAudioClient3 = MA_TRUE;
21187 pData->periodSizeInFramesOut = actualPeriodInFrames;
21188
21189 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n");
21190 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut);
21191 } else {
21192 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n");
21193 }
21194 } else {
21195 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");
21196 }
21197 } else {
21198 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n");
21199 }
21200
21201 ma_IAudioClient3_Release(pAudioClient3);
21202 pAudioClient3 = NULL;
21203 }
21204 }
21205 }
21206 #else
21207 {
21208 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");
21209 }
21210 #endif
21211
21212 /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */
21213 if (!wasInitializedUsingIAudioClient3) {
21214 MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */
21215 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL);
21216 if (FAILED(hr)) {
21217 if (hr == E_ACCESSDENIED) {
21218 errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED;
21219 } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
21220 errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY;
21221 } else {
21222 errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr);
21223 }
21224
21225 goto done;
21226 }
21227 }
21228 }
21229
21230 if (!wasInitializedUsingIAudioClient3) {
21231 ma_uint32 bufferSizeInFrames;
21232 hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
21233 if (FAILED(hr)) {
21234 errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr);
21235 goto done;
21236 }
21237
21238 pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut;
21239 }
21240
21241 pData->usingAudioClient3 = wasInitializedUsingIAudioClient3;
21242
21243
21244 if (deviceType == ma_device_type_playback) {
21245 result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient);
21246 } else {
21247 result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient);
21248 }
21249
21250 /*if (FAILED(hr)) {*/
21251 if (result != MA_SUCCESS) {
21252 errorMsg = "[WASAPI] Failed to get audio client service.";
21253 goto done;
21254 }
21255
21256
21257 /* Grab the name of the device. */
21258 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21259 {
21260 ma_IPropertyStore *pProperties;
21261 hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties);
21262 if (SUCCEEDED(hr)) {
21263 PROPVARIANT varName;
21264 ma_PropVariantInit(&varName);
21265 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName);
21266 if (SUCCEEDED(hr)) {
21267 WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE);
21268 ma_PropVariantClear(pContext, &varName);
21269 }
21270
21271 ma_IPropertyStore_Release(pProperties);
21272 }
21273 }
21274 #endif
21275
21276 /*
21277 For the WASAPI backend we need to know the actual IDs of the device in order to do automatic
21278 stream routing so that IDs can be compared and we can determine which device has been detached
21279 and whether or not it matches with our ma_device.
21280 */
21281 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21282 {
21283 /* Desktop */
21284 ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id);
21285 }
21286 #else
21287 {
21288 /* UWP */
21289 /* TODO: Implement me. Need to figure out how to get the ID of the default device. */
21290 }
21291 #endif
21292
21293done:
21294 /* Clean up. */
21295#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21296 if (pDeviceInterface != NULL) {
21297 ma_IMMDevice_Release(pDeviceInterface);
21298 }
21299#else
21300 if (pDeviceInterface != NULL) {
21301 ma_IUnknown_Release(pDeviceInterface);
21302 }
21303#endif
21304
21305 if (result != MA_SUCCESS) {
21306 if (pData->pRenderClient) {
21307 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient);
21308 pData->pRenderClient = NULL;
21309 }
21310 if (pData->pCaptureClient) {
21311 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient);
21312 pData->pCaptureClient = NULL;
21313 }
21314 if (pData->pAudioClient) {
21315 ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
21316 pData->pAudioClient = NULL;
21317 }
21318
21319 if (errorMsg != NULL && errorMsg[0] != '\0') {
21320 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s", errorMsg);
21321 }
21322
21323 return result;
21324 } else {
21325 return MA_SUCCESS;
21326 }
21327}
21328
21329static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType)
21330{
21331 ma_device_init_internal_data__wasapi data;
21332 ma_result result;
21333
21334 MA_ASSERT(pDevice != NULL);
21335
21336 /* We only re-initialize the playback or capture device. Never a full-duplex device. */
21337 if (deviceType == ma_device_type_duplex) {
21338 return MA_INVALID_ARGS;
21339 }
21340
21341
21342 /*
21343 Before reinitializing the device we need to free the previous audio clients.
21344
21345 There's a known memory leak here. We will be calling this from the routing change callback that
21346 is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion
21347 this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably
21348 need some system where we post an event, but delay the execution of it until the callback has
21349 returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for
21350 a command thread which might be useful for this.
21351 */
21352 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
21353 if (pDevice->wasapi.pCaptureClient) {
21354 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
21355 pDevice->wasapi.pCaptureClient = NULL;
21356 }
21357
21358 if (pDevice->wasapi.pAudioClientCapture) {
21359 /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/
21360 pDevice->wasapi.pAudioClientCapture = NULL;
21361 }
21362 }
21363
21364 if (deviceType == ma_device_type_playback) {
21365 if (pDevice->wasapi.pRenderClient) {
21366 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
21367 pDevice->wasapi.pRenderClient = NULL;
21368 }
21369
21370 if (pDevice->wasapi.pAudioClientPlayback) {
21371 /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/
21372 pDevice->wasapi.pAudioClientPlayback = NULL;
21373 }
21374 }
21375
21376
21377 if (deviceType == ma_device_type_playback) {
21378 data.formatIn = pDevice->playback.format;
21379 data.channelsIn = pDevice->playback.channels;
21380 MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
21381 data.shareMode = pDevice->playback.shareMode;
21382 } else {
21383 data.formatIn = pDevice->capture.format;
21384 data.channelsIn = pDevice->capture.channels;
21385 MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
21386 data.shareMode = pDevice->capture.shareMode;
21387 }
21388
21389 data.sampleRateIn = pDevice->sampleRate;
21390 data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames;
21391 data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds;
21392 data.periodsIn = pDevice->wasapi.originalPeriods;
21393 data.performanceProfile = pDevice->wasapi.originalPerformanceProfile;
21394 data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC;
21395 data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC;
21396 data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading;
21397 result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data);
21398 if (result != MA_SUCCESS) {
21399 return result;
21400 }
21401
21402 /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */
21403 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
21404 pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
21405 pDevice->wasapi.pCaptureClient = data.pCaptureClient;
21406
21407 pDevice->capture.internalFormat = data.formatOut;
21408 pDevice->capture.internalChannels = data.channelsOut;
21409 pDevice->capture.internalSampleRate = data.sampleRateOut;
21410 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
21411 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
21412 pDevice->capture.internalPeriods = data.periodsOut;
21413 ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName);
21414
21415 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture);
21416
21417 pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
21418 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
21419
21420 /* We must always have a valid ID. */
21421 ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);
21422 }
21423
21424 if (deviceType == ma_device_type_playback) {
21425 pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
21426 pDevice->wasapi.pRenderClient = data.pRenderClient;
21427
21428 pDevice->playback.internalFormat = data.formatOut;
21429 pDevice->playback.internalChannels = data.channelsOut;
21430 pDevice->playback.internalSampleRate = data.sampleRateOut;
21431 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
21432 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
21433 pDevice->playback.internalPeriods = data.periodsOut;
21434 ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName);
21435
21436 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback);
21437
21438 pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
21439 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
21440
21441 /* We must always have a valid ID because rerouting will look at it. */
21442 ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);
21443 }
21444
21445 return MA_SUCCESS;
21446}
21447
21448static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
21449{
21450 ma_result result = MA_SUCCESS;
21451
21452#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21453 HRESULT hr;
21454 ma_IMMDeviceEnumerator* pDeviceEnumerator;
21455#endif
21456
21457 MA_ASSERT(pDevice != NULL);
21458
21459 MA_ZERO_OBJECT(&pDevice->wasapi);
21460 pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
21461 pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
21462 pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
21463
21464 /* Exclusive mode is not allowed with loopback. */
21467 }
21468
21470 ma_device_init_internal_data__wasapi data;
21471 data.formatIn = pDescriptorCapture->format;
21472 data.channelsIn = pDescriptorCapture->channels;
21473 data.sampleRateIn = pDescriptorCapture->sampleRate;
21474 MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
21475 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
21476 data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds;
21477 data.periodsIn = pDescriptorCapture->periodCount;
21478 data.shareMode = pDescriptorCapture->shareMode;
21479 data.performanceProfile = pConfig->performanceProfile;
21480 data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
21481 data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
21482 data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
21483
21484 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);
21485 if (result != MA_SUCCESS) {
21486 return result;
21487 }
21488
21489 pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
21490 pDevice->wasapi.pCaptureClient = data.pCaptureClient;
21491 pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;
21492 pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
21493 pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount;
21494 pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile;
21495
21496 /*
21497 The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled,
21498 however, because we want to block until we actually have something for the first call to ma_device_read().
21499 */
21500 pDevice->wasapi.hEventCapture = CreateEventW(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */
21501 if (pDevice->wasapi.hEventCapture == NULL) {
21502 result = ma_result_from_GetLastError(GetLastError());
21503
21504 if (pDevice->wasapi.pCaptureClient != NULL) {
21505 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
21506 pDevice->wasapi.pCaptureClient = NULL;
21507 }
21508 if (pDevice->wasapi.pAudioClientCapture != NULL) {
21509 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
21510 pDevice->wasapi.pAudioClientCapture = NULL;
21511 }
21512
21513 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture.");
21514 return result;
21515 }
21516 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture);
21517
21518 pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
21519 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
21520
21521 /* We must always have a valid ID. */
21522 ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);
21523
21524 /* The descriptor needs to be updated with actual values. */
21525 pDescriptorCapture->format = data.formatOut;
21526 pDescriptorCapture->channels = data.channelsOut;
21527 pDescriptorCapture->sampleRate = data.sampleRateOut;
21528 MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
21529 pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut;
21530 pDescriptorCapture->periodCount = data.periodsOut;
21531 }
21532
21533 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
21534 ma_device_init_internal_data__wasapi data;
21535 data.formatIn = pDescriptorPlayback->format;
21536 data.channelsIn = pDescriptorPlayback->channels;
21537 data.sampleRateIn = pDescriptorPlayback->sampleRate;
21538 MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
21539 data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames;
21540 data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
21541 data.periodsIn = pDescriptorPlayback->periodCount;
21542 data.shareMode = pDescriptorPlayback->shareMode;
21543 data.performanceProfile = pConfig->performanceProfile;
21544 data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
21545 data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
21546 data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
21547
21548 result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data);
21549 if (result != MA_SUCCESS) {
21550 if (pConfig->deviceType == ma_device_type_duplex) {
21551 if (pDevice->wasapi.pCaptureClient != NULL) {
21552 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
21553 pDevice->wasapi.pCaptureClient = NULL;
21554 }
21555 if (pDevice->wasapi.pAudioClientCapture != NULL) {
21556 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
21557 pDevice->wasapi.pAudioClientCapture = NULL;
21558 }
21559
21560 CloseHandle(pDevice->wasapi.hEventCapture);
21561 pDevice->wasapi.hEventCapture = NULL;
21562 }
21563 return result;
21564 }
21565
21566 pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
21567 pDevice->wasapi.pRenderClient = data.pRenderClient;
21568 pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
21569 pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
21570 pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount;
21571 pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile;
21572
21573 /*
21574 The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled
21575 only after the whole available space has been filled, never before.
21576
21577 The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able
21578 to get passed WaitForMultipleObjects().
21579 */
21580 pDevice->wasapi.hEventPlayback = CreateEventW(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */
21581 if (pDevice->wasapi.hEventPlayback == NULL) {
21582 result = ma_result_from_GetLastError(GetLastError());
21583
21584 if (pConfig->deviceType == ma_device_type_duplex) {
21585 if (pDevice->wasapi.pCaptureClient != NULL) {
21586 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
21587 pDevice->wasapi.pCaptureClient = NULL;
21588 }
21589 if (pDevice->wasapi.pAudioClientCapture != NULL) {
21590 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
21591 pDevice->wasapi.pAudioClientCapture = NULL;
21592 }
21593
21594 CloseHandle(pDevice->wasapi.hEventCapture);
21595 pDevice->wasapi.hEventCapture = NULL;
21596 }
21597
21598 if (pDevice->wasapi.pRenderClient != NULL) {
21599 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
21600 pDevice->wasapi.pRenderClient = NULL;
21601 }
21602 if (pDevice->wasapi.pAudioClientPlayback != NULL) {
21603 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
21604 pDevice->wasapi.pAudioClientPlayback = NULL;
21605 }
21606
21607 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback.");
21608 return result;
21609 }
21610 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback);
21611
21612 pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
21613 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
21614
21615 /* We must always have a valid ID because rerouting will look at it. */
21616 ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);
21617
21618 /* The descriptor needs to be updated with actual values. */
21619 pDescriptorPlayback->format = data.formatOut;
21620 pDescriptorPlayback->channels = data.channelsOut;
21621 pDescriptorPlayback->sampleRate = data.sampleRateOut;
21622 MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
21623 pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut;
21624 pDescriptorPlayback->periodCount = data.periodsOut;
21625 }
21626
21627 /*
21628 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
21629 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
21630 stop the device outright and let the application handle it.
21631 */
21632#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21633 if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) {
21634 if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID == NULL) {
21635 pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE;
21636 }
21637 if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) {
21638 pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE;
21639 }
21640 }
21641
21642 hr = ma_CoCreateInstance(pDevice->pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
21643 if (FAILED(hr)) {
21644 ma_device_uninit__wasapi(pDevice);
21645 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
21646 return ma_result_from_HRESULT(hr);
21647 }
21648
21649 pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl;
21650 pDevice->wasapi.notificationClient.counter = 1;
21651 pDevice->wasapi.notificationClient.pDevice = pDevice;
21652
21653 hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient);
21654 if (SUCCEEDED(hr)) {
21655 pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator;
21656 } else {
21657 /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */
21658 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
21659 }
21660#endif
21661
21662 c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE);
21663 c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
21664
21665 return MA_SUCCESS;
21666}
21667
21668static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount)
21669{
21670 ma_uint32 paddingFramesCount;
21671 HRESULT hr;
21672 ma_share_mode shareMode;
21673
21674 MA_ASSERT(pDevice != NULL);
21675 MA_ASSERT(pFrameCount != NULL);
21676
21677 *pFrameCount = 0;
21678
21679 if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) {
21680 return MA_INVALID_OPERATION;
21681 }
21682
21683 /*
21684 I've had a report that GetCurrentPadding() is returning a frame count of 0 which is preventing
21685 higher level function calls from doing anything because it thinks nothing is available. I have
21686 taken a look at the documentation and it looks like this is unnecessary in exclusive mode.
21687
21688 From Microsoft's documentation:
21689
21690 For an exclusive-mode rendering or capture stream that was initialized with the
21691 AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag, the client typically has no use for the padding
21692 value reported by GetCurrentPadding. Instead, the client accesses an entire buffer during
21693 each processing pass.
21694
21695 Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the
21696 entire buffer. This depends on the caller making sure they wait on the event handler.
21697 */
21698 shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode;
21699 if (shareMode == ma_share_mode_shared) {
21700 /* Shared mode. */
21701 hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount);
21702 if (FAILED(hr)) {
21703 return ma_result_from_HRESULT(hr);
21704 }
21705
21706 if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
21707 *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount;
21708 } else {
21709 *pFrameCount = paddingFramesCount;
21710 }
21711 } else {
21712 /* Exclusive mode. */
21713 if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
21714 *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback;
21715 } else {
21716 *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture;
21717 }
21718 }
21719
21720 return MA_SUCCESS;
21721}
21722
21723
21724static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType)
21725{
21726 ma_result result;
21727
21728 if (deviceType == ma_device_type_duplex) {
21729 return MA_INVALID_ARGS;
21730 }
21731
21732 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== CHANGING DEVICE ===\n");
21733
21734 result = ma_device_reinit__wasapi(pDevice, deviceType);
21735 if (result != MA_SUCCESS) {
21736 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WASAPI] Reinitializing device after route change failed.\n");
21737 return result;
21738 }
21739
21740 ma_device__post_init_setup(pDevice, deviceType);
21741
21742 ma_device__on_notification_rerouted(pDevice);
21743
21744 return MA_SUCCESS;
21745}
21746
21747static ma_result ma_device_start__wasapi(ma_device* pDevice)
21748{
21749 HRESULT hr;
21750
21751 MA_ASSERT(pDevice != NULL);
21752
21753 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
21754 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
21755 if (FAILED(hr)) {
21756 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device.");
21757 return ma_result_from_HRESULT(hr);
21758 }
21759
21760 c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_TRUE);
21761 }
21762
21763 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
21764 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
21765 if (FAILED(hr)) {
21766 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.");
21767 return ma_result_from_HRESULT(hr);
21768 }
21769
21770 c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
21771 }
21772
21773 return MA_SUCCESS;
21774}
21775
21776static ma_result ma_device_stop__wasapi(ma_device* pDevice)
21777{
21778 ma_result result;
21779 HRESULT hr;
21780
21781 MA_ASSERT(pDevice != NULL);
21782
21783 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
21784 hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
21785 if (FAILED(hr)) {
21786 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device.");
21787 return ma_result_from_HRESULT(hr);
21788 }
21789
21790 /* The audio client needs to be reset otherwise restarting will fail. */
21791 hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
21792 if (FAILED(hr)) {
21793 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device.");
21794 return ma_result_from_HRESULT(hr);
21795 }
21796
21797 /* If we have a mapped buffer we need to release it. */
21798 if (pDevice->wasapi.pMappedBufferCapture != NULL) {
21799 ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
21800 pDevice->wasapi.pMappedBufferCapture = NULL;
21801 pDevice->wasapi.mappedBufferCaptureCap = 0;
21802 pDevice->wasapi.mappedBufferCaptureLen = 0;
21803 }
21804
21805 c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE);
21806 }
21807
21808 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
21809 /*
21810 The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to
21811 the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played.
21812 */
21813 if (c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) {
21814 /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */
21815 DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate;
21816
21817 if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
21818 WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime);
21819 } else {
21820 ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1;
21821 ma_uint32 framesAvailablePlayback;
21822 for (;;) {
21823 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
21824 if (result != MA_SUCCESS) {
21825 break;
21826 }
21827
21828 if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) {
21829 break;
21830 }
21831
21832 /*
21833 Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames
21834 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.
21835 */
21836 if (framesAvailablePlayback == prevFramesAvaialablePlayback) {
21837 break;
21838 }
21839 prevFramesAvaialablePlayback = framesAvailablePlayback;
21840
21841 WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime);
21842 ResetEvent(pDevice->wasapi.hEventPlayback); /* Manual reset. */
21843 }
21844 }
21845 }
21846
21847 hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
21848 if (FAILED(hr)) {
21849 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device.");
21850 return ma_result_from_HRESULT(hr);
21851 }
21852
21853 /* The audio client needs to be reset otherwise restarting will fail. */
21854 hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
21855 if (FAILED(hr)) {
21856 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device.");
21857 return ma_result_from_HRESULT(hr);
21858 }
21859
21860 if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
21861 ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
21862 pDevice->wasapi.pMappedBufferPlayback = NULL;
21863 pDevice->wasapi.mappedBufferPlaybackCap = 0;
21864 pDevice->wasapi.mappedBufferPlaybackLen = 0;
21865 }
21866
21867 c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
21868 }
21869
21870 return MA_SUCCESS;
21871}
21872
21873
21874#ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS
21875#define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000
21876#endif
21877
21878static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
21879{
21880 ma_result result = MA_SUCCESS;
21881 ma_uint32 totalFramesProcessed = 0;
21882
21883 /*
21884 When reading, we need to get a buffer and process all of it before releasing it. Because the
21885 frame count (frameCount) can be different to the size of the buffer, we'll need to cache the
21886 pointer to the buffer.
21887 */
21888
21889 /* Keep running until we've processed the requested number of frames. */
21890 while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) {
21891 ma_uint32 framesRemaining = frameCount - totalFramesProcessed;
21892
21893 /* If we have a mapped data buffer, consume that first. */
21894 if (pDevice->wasapi.pMappedBufferCapture != NULL) {
21895 /* We have a cached data pointer so consume that before grabbing another one from WASAPI. */
21896 ma_uint32 framesToProcessNow = framesRemaining;
21897 if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) {
21898 framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen;
21899 }
21900
21901 /* Now just copy the data over to the output buffer. */
21903 ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels),
21904 ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels),
21905 framesToProcessNow,
21907 );
21908
21909 totalFramesProcessed += framesToProcessNow;
21910 pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow;
21911
21912 /* If the data buffer has been fully consumed we need to release it. */
21913 if (pDevice->wasapi.mappedBufferCaptureLen == 0) {
21914 ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
21915 pDevice->wasapi.pMappedBufferCapture = NULL;
21916 pDevice->wasapi.mappedBufferCaptureCap = 0;
21917 }
21918 } else {
21919 /* We don't have any cached data pointer, so grab another one. */
21920 HRESULT hr;
21921 DWORD flags;
21922
21923 /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */
21924 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL);
21925 if (hr == S_OK) {
21926 /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */
21927
21928 /* Overrun detection. */
21929 if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
21930 /* Glitched. Probably due to an overrun. */
21931 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);
21932
21933 /*
21934 If we got an overrun it probably means we're straddling the end of the buffer. In order to prevent
21935 a never-ending sequence of glitches we're going to recover by completely clearing out the capture
21936 buffer.
21937 */
21938 {
21939 ma_uint32 iterationCount = 4; /* Safety to prevent an infinite loop. */
21940 ma_uint32 i;
21941
21942 for (i = 0; i < iterationCount; i += 1) {
21943 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
21944 if (FAILED(hr)) {
21945 break;
21946 }
21947
21948 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL);
21949 if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) {
21950 break;
21951 }
21952 }
21953 }
21954
21955 /* We should not have a valid buffer at this point so make sure everything is empty. */
21956 pDevice->wasapi.pMappedBufferCapture = NULL;
21957 pDevice->wasapi.mappedBufferCaptureCap = 0;
21958 pDevice->wasapi.mappedBufferCaptureLen = 0;
21959 } else {
21960 /* The data is clean. */
21961 pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap;
21962
21963 if (flags != 0) {
21964 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags);
21965 }
21966 }
21967
21968 continue;
21969 } else {
21970 if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || hr == MA_AUDCLNT_E_BUFFER_ERROR) {
21971 /*
21972 No data is available. We need to wait for more. There's two situations to consider
21973 here. The first is normal capture mode. If this times out it probably means the
21974 microphone isn't delivering data for whatever reason. In this case we'll just
21975 abort the read and return whatever we were able to get. The other situations is
21976 loopback mode, in which case a timeout probably just means the nothing is playing
21977 through the speakers.
21978 */
21979 if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
21980 if (pDevice->type == ma_device_type_loopback) {
21981 continue; /* Keep waiting in loopback mode. */
21982 } else {
21983 result = MA_ERROR;
21984 break; /* Wait failed. */
21985 }
21986 }
21987
21988 /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */
21989 } else {
21990 /* An error occured and we need to abort. */
21991 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);
21992 result = ma_result_from_HRESULT(hr);
21993 break;
21994 }
21995 }
21996 }
21997 }
21998
21999 /*
22000 If we were unable to process the entire requested frame count, but we still have a mapped buffer,
22001 there's a good chance either an error occurred or the device was stopped mid-read. In this case
22002 we'll need to make sure the buffer is released.
22003 */
22004 if (totalFramesProcessed < frameCount && pDevice->wasapi.pMappedBufferCapture != NULL) {
22005 ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
22006 pDevice->wasapi.pMappedBufferCapture = NULL;
22007 pDevice->wasapi.mappedBufferCaptureCap = 0;
22008 pDevice->wasapi.mappedBufferCaptureLen = 0;
22009 }
22010
22011 if (pFramesRead != NULL) {
22012 *pFramesRead = totalFramesProcessed;
22013 }
22014
22015 return result;
22016}
22017
22018static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
22019{
22020 ma_result result = MA_SUCCESS;
22021 ma_uint32 totalFramesProcessed = 0;
22022
22023 /* Keep writing to the device until it's stopped or we've consumed all of our input. */
22024 while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) {
22025 ma_uint32 framesRemaining = frameCount - totalFramesProcessed;
22026
22027 /*
22028 We're going to do this in a similar way to capture. We'll first check if the cached data pointer
22029 is valid, and if so, read from that. Otherwise We will call IAudioRenderClient_GetBuffer() with
22030 a requested buffer size equal to our actual period size. If it returns AUDCLNT_E_BUFFER_TOO_LARGE
22031 it means we need to wait for some data to become available.
22032 */
22033 if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
22034 /* We still have some space available in the mapped data buffer. Write to it. */
22035 ma_uint32 framesToProcessNow = framesRemaining;
22036 if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) {
22037 framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen);
22038 }
22039
22040 /* Now just copy the data over to the output buffer. */
22042 ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels),
22043 ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels),
22044 framesToProcessNow,
22046 );
22047
22048 totalFramesProcessed += framesToProcessNow;
22049 pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow;
22050
22051 /* If the data buffer has been fully consumed we need to release it. */
22052 if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) {
22053 ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
22054 pDevice->wasapi.pMappedBufferPlayback = NULL;
22055 pDevice->wasapi.mappedBufferPlaybackCap = 0;
22056 pDevice->wasapi.mappedBufferPlaybackLen = 0;
22057
22058 /*
22059 In exclusive mode we need to wait here. Exclusive mode is weird because GetBuffer() never
22060 seems to return AUDCLNT_E_BUFFER_TOO_LARGE, which is what we normally use to determine
22061 whether or not we need to wait for more data.
22062 */
22063 if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
22064 if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
22065 result = MA_ERROR;
22066 break; /* Wait failed. Probably timed out. */
22067 }
22068 }
22069 }
22070 } else {
22071 /* We don't have a mapped data buffer so we'll need to get one. */
22072 HRESULT hr;
22073 ma_uint32 bufferSizeInFrames;
22074
22075 /* Special rules for exclusive mode. */
22076 if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
22077 bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback;
22078 } else {
22079 bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback;
22080 }
22081
22082 hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback);
22083 if (hr == S_OK) {
22084 /* We have data available. */
22085 pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames;
22086 pDevice->wasapi.mappedBufferPlaybackLen = 0;
22087 } else {
22088 if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) {
22089 /* Not enough data available. We need to wait for more. */
22090 if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
22091 result = MA_ERROR;
22092 break; /* Wait failed. Probably timed out. */
22093 }
22094 } else {
22095 /* Some error occurred. We'll need to abort. */
22096 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);
22097 result = ma_result_from_HRESULT(hr);
22098 break;
22099 }
22100 }
22101 }
22102 }
22103
22104 if (pFramesWritten != NULL) {
22105 *pFramesWritten = totalFramesProcessed;
22106 }
22107
22108 return result;
22109}
22110
22111static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice)
22112{
22113 MA_ASSERT(pDevice != NULL);
22114
22115 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
22116 SetEvent((HANDLE)pDevice->wasapi.hEventCapture);
22117 }
22118
22119 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
22120 SetEvent((HANDLE)pDevice->wasapi.hEventPlayback);
22121 }
22122
22123 return MA_SUCCESS;
22124}
22125
22126
22127static ma_result ma_context_uninit__wasapi(ma_context* pContext)
22128{
22129 MA_ASSERT(pContext != NULL);
22130 MA_ASSERT(pContext->backend == ma_backend_wasapi);
22131
22132 if (pContext->wasapi.commandThread != NULL) {
22133 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI);
22134 ma_context_post_command__wasapi(pContext, &cmd);
22135 ma_thread_wait(&pContext->wasapi.commandThread);
22136
22137 /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */
22138 ma_semaphore_uninit(&pContext->wasapi.commandSem);
22139 ma_mutex_uninit(&pContext->wasapi.commandLock);
22140 }
22141
22142 return MA_SUCCESS;
22143}
22144
22145static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
22146{
22147 ma_result result = MA_SUCCESS;
22148
22149 MA_ASSERT(pContext != NULL);
22150
22151 (void)pConfig;
22152
22153#ifdef MA_WIN32_DESKTOP
22154 /*
22155 WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven
22156 exclusive mode does not work until SP1.
22157
22158 Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error.
22159 */
22160 {
22161 ma_OSVERSIONINFOEXW osvi;
22162 ma_handle kernel32DLL;
22163 ma_PFNVerifyVersionInfoW _VerifyVersionInfoW;
22164 ma_PFNVerSetConditionMask _VerSetConditionMask;
22165
22166 kernel32DLL = ma_dlopen(pContext, "kernel32.dll");
22167 if (kernel32DLL == NULL) {
22168 return MA_NO_BACKEND;
22169 }
22170
22171 _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(pContext, kernel32DLL, "VerifyVersionInfoW");
22172 _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(pContext, kernel32DLL, "VerSetConditionMask");
22173 if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) {
22174 ma_dlclose(pContext, kernel32DLL);
22175 return MA_NO_BACKEND;
22176 }
22177
22178 MA_ZERO_OBJECT(&osvi);
22179 osvi.dwOSVersionInfoSize = sizeof(osvi);
22180 osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF);
22181 osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF);
22182 osvi.wServicePackMajor = 1;
22183 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))) {
22184 result = MA_SUCCESS;
22185 } else {
22186 result = MA_NO_BACKEND;
22187 }
22188
22189 ma_dlclose(pContext, kernel32DLL);
22190 }
22191#endif
22192
22193 if (result != MA_SUCCESS) {
22194 return result;
22195 }
22196
22197 MA_ZERO_OBJECT(&pContext->wasapi);
22198
22199 /*
22200 Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread
22201 than the one that retrieved it with GetService(). This can result in a deadlock in two
22202 situations:
22203
22204 1) When calling ma_device_uninit() from a different thread to ma_device_init(); and
22205 2) When uninitializing and reinitializing the internal IAudioClient object in response to
22206 automatic stream routing.
22207
22208 We could define ma_device_uninit() such that it must be called on the same thread as
22209 ma_device_init(). We could also just not release the IAudioClient when performing automatic
22210 stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so
22211 we're going to have to work around this with a worker thread. This is not ideal, but I can't
22212 think of a better way to do this.
22213
22214 More information about this can be found here:
22215
22216 https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient
22217
22218 Note this section:
22219
22220 When releasing an IAudioRenderClient interface instance, the client must call the interface's
22221 Release method from the same thread as the call to IAudioClient::GetService that created the
22222 object.
22223 */
22224 {
22225 result = ma_mutex_init(&pContext->wasapi.commandLock);
22226 if (result != MA_SUCCESS) {
22227 return result;
22228 }
22229
22230 result = ma_semaphore_init(0, &pContext->wasapi.commandSem);
22231 if (result != MA_SUCCESS) {
22232 ma_mutex_uninit(&pContext->wasapi.commandLock);
22233 return result;
22234 }
22235
22236 result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks);
22237 if (result != MA_SUCCESS) {
22238 ma_semaphore_uninit(&pContext->wasapi.commandSem);
22239 ma_mutex_uninit(&pContext->wasapi.commandLock);
22240 return result;
22241 }
22242 }
22243
22244
22245 pCallbacks->onContextInit = ma_context_init__wasapi;
22246 pCallbacks->onContextUninit = ma_context_uninit__wasapi;
22247 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi;
22248 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi;
22249 pCallbacks->onDeviceInit = ma_device_init__wasapi;
22250 pCallbacks->onDeviceUninit = ma_device_uninit__wasapi;
22251 pCallbacks->onDeviceStart = ma_device_start__wasapi;
22252 pCallbacks->onDeviceStop = ma_device_stop__wasapi;
22253 pCallbacks->onDeviceRead = ma_device_read__wasapi;
22254 pCallbacks->onDeviceWrite = ma_device_write__wasapi;
22255 pCallbacks->onDeviceDataLoop = NULL;
22256 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi;
22257
22258 return MA_SUCCESS;
22259}
22260#endif
22261
22262
22267#ifdef MA_HAS_DSOUND
22268/*#include <dsound.h>*/
22269
22270/*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/
22271
22272/* miniaudio only uses priority or exclusive modes. */
22273#define MA_DSSCL_NORMAL 1
22274#define MA_DSSCL_PRIORITY 2
22275#define MA_DSSCL_EXCLUSIVE 3
22276#define MA_DSSCL_WRITEPRIMARY 4
22277
22278#define MA_DSCAPS_PRIMARYMONO 0x00000001
22279#define MA_DSCAPS_PRIMARYSTEREO 0x00000002
22280#define MA_DSCAPS_PRIMARY8BIT 0x00000004
22281#define MA_DSCAPS_PRIMARY16BIT 0x00000008
22282#define MA_DSCAPS_CONTINUOUSRATE 0x00000010
22283#define MA_DSCAPS_EMULDRIVER 0x00000020
22284#define MA_DSCAPS_CERTIFIED 0x00000040
22285#define MA_DSCAPS_SECONDARYMONO 0x00000100
22286#define MA_DSCAPS_SECONDARYSTEREO 0x00000200
22287#define MA_DSCAPS_SECONDARY8BIT 0x00000400
22288#define MA_DSCAPS_SECONDARY16BIT 0x00000800
22289
22290#define MA_DSBCAPS_PRIMARYBUFFER 0x00000001
22291#define MA_DSBCAPS_STATIC 0x00000002
22292#define MA_DSBCAPS_LOCHARDWARE 0x00000004
22293#define MA_DSBCAPS_LOCSOFTWARE 0x00000008
22294#define MA_DSBCAPS_CTRL3D 0x00000010
22295#define MA_DSBCAPS_CTRLFREQUENCY 0x00000020
22296#define MA_DSBCAPS_CTRLPAN 0x00000040
22297#define MA_DSBCAPS_CTRLVOLUME 0x00000080
22298#define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100
22299#define MA_DSBCAPS_CTRLFX 0x00000200
22300#define MA_DSBCAPS_STICKYFOCUS 0x00004000
22301#define MA_DSBCAPS_GLOBALFOCUS 0x00008000
22302#define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000
22303#define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000
22304#define MA_DSBCAPS_LOCDEFER 0x00040000
22305#define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000
22306
22307#define MA_DSBPLAY_LOOPING 0x00000001
22308#define MA_DSBPLAY_LOCHARDWARE 0x00000002
22309#define MA_DSBPLAY_LOCSOFTWARE 0x00000004
22310#define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008
22311#define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010
22312#define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020
22313
22314#define MA_DSCBSTART_LOOPING 0x00000001
22315
22316typedef struct
22317{
22318 DWORD dwSize;
22319 DWORD dwFlags;
22320 DWORD dwBufferBytes;
22321 DWORD dwReserved;
22322 WAVEFORMATEX* lpwfxFormat;
22323 GUID guid3DAlgorithm;
22324} MA_DSBUFFERDESC;
22325
22326typedef struct
22327{
22328 DWORD dwSize;
22329 DWORD dwFlags;
22330 DWORD dwBufferBytes;
22331 DWORD dwReserved;
22332 WAVEFORMATEX* lpwfxFormat;
22333 DWORD dwFXCount;
22334 void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */
22335} MA_DSCBUFFERDESC;
22336
22337typedef struct
22338{
22339 DWORD dwSize;
22340 DWORD dwFlags;
22341 DWORD dwMinSecondarySampleRate;
22342 DWORD dwMaxSecondarySampleRate;
22343 DWORD dwPrimaryBuffers;
22344 DWORD dwMaxHwMixingAllBuffers;
22345 DWORD dwMaxHwMixingStaticBuffers;
22346 DWORD dwMaxHwMixingStreamingBuffers;
22347 DWORD dwFreeHwMixingAllBuffers;
22348 DWORD dwFreeHwMixingStaticBuffers;
22349 DWORD dwFreeHwMixingStreamingBuffers;
22350 DWORD dwMaxHw3DAllBuffers;
22351 DWORD dwMaxHw3DStaticBuffers;
22352 DWORD dwMaxHw3DStreamingBuffers;
22353 DWORD dwFreeHw3DAllBuffers;
22354 DWORD dwFreeHw3DStaticBuffers;
22355 DWORD dwFreeHw3DStreamingBuffers;
22356 DWORD dwTotalHwMemBytes;
22357 DWORD dwFreeHwMemBytes;
22358 DWORD dwMaxContigFreeHwMemBytes;
22359 DWORD dwUnlockTransferRateHwBuffers;
22360 DWORD dwPlayCpuOverheadSwBuffers;
22361 DWORD dwReserved1;
22362 DWORD dwReserved2;
22363} MA_DSCAPS;
22364
22365typedef struct
22366{
22367 DWORD dwSize;
22368 DWORD dwFlags;
22369 DWORD dwBufferBytes;
22370 DWORD dwUnlockTransferRate;
22371 DWORD dwPlayCpuOverhead;
22372} MA_DSBCAPS;
22373
22374typedef struct
22375{
22376 DWORD dwSize;
22377 DWORD dwFlags;
22378 DWORD dwFormats;
22379 DWORD dwChannels;
22380} MA_DSCCAPS;
22381
22382typedef struct
22383{
22384 DWORD dwSize;
22385 DWORD dwFlags;
22386 DWORD dwBufferBytes;
22387 DWORD dwReserved;
22388} MA_DSCBCAPS;
22389
22390typedef struct
22391{
22392 DWORD dwOffset;
22393 HANDLE hEventNotify;
22394} MA_DSBPOSITIONNOTIFY;
22395
22396typedef struct ma_IDirectSound ma_IDirectSound;
22397typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer;
22398typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture;
22399typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer;
22400typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify;
22401
22402
22403/*
22404COM objects. The way these work is that you have a vtable (a list of function pointers, kind of
22405like how C++ works internally), and then you have a structure with a single member, which is a
22406pointer to the vtable. The vtable is where the methods of the object are defined. Methods need
22407to be in a specific order, and parent classes need to have their methods declared first.
22408*/
22409
22410/* IDirectSound */
22411typedef struct
22412{
22413 /* IUnknown */
22414 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject);
22415 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis);
22416 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis);
22417
22418 /* IDirectSound */
22419 HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter);
22420 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps);
22421 HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate);
22422 HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel);
22423 HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis);
22424 HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig);
22425 HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig);
22426 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice);
22427} ma_IDirectSoundVtbl;
22428struct ma_IDirectSound
22429{
22430 ma_IDirectSoundVtbl* lpVtbl;
22431};
22432static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
22433static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); }
22434static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); }
22435static 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); }
22436static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); }
22437static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); }
22438static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); }
22439static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); }
22440static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); }
22441static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); }
22442static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
22443
22444
22445/* IDirectSoundBuffer */
22446typedef struct
22447{
22448 /* IUnknown */
22449 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject);
22450 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis);
22451 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis);
22452
22453 /* IDirectSoundBuffer */
22454 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps);
22455 HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor);
22456 HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
22457 HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume);
22458 HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan);
22459 HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency);
22460 HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus);
22461 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc);
22462 HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
22463 HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags);
22464 HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition);
22465 HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat);
22466 HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume);
22467 HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan);
22468 HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency);
22469 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis);
22470 HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
22471 HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis);
22472} ma_IDirectSoundBufferVtbl;
22473struct ma_IDirectSoundBuffer
22474{
22475 ma_IDirectSoundBufferVtbl* lpVtbl;
22476};
22477static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
22478static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
22479static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
22480static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); }
22481static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); }
22482static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
22483static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); }
22484static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); }
22485static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); }
22486static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
22487static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); }
22488static 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); }
22489static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); }
22490static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); }
22491static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); }
22492static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); }
22493static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); }
22494static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); }
22495static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
22496static 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); }
22497static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); }
22498
22499
22500/* IDirectSoundCapture */
22501typedef struct
22502{
22503 /* IUnknown */
22504 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject);
22505 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis);
22506 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis);
22507
22508 /* IDirectSoundCapture */
22509 HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter);
22510 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps);
22511 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice);
22512} ma_IDirectSoundCaptureVtbl;
22513struct ma_IDirectSoundCapture
22514{
22515 ma_IDirectSoundCaptureVtbl* lpVtbl;
22516};
22517static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
22518static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); }
22519static MA_INLINE ULONG ma_IDirectSoundCapture_Release (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); }
22520static 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); }
22521static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); }
22522static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
22523
22524
22525/* IDirectSoundCaptureBuffer */
22526typedef struct
22527{
22528 /* IUnknown */
22529 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject);
22530 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis);
22531 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis);
22532
22533 /* IDirectSoundCaptureBuffer */
22534 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps);
22535 HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition);
22536 HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
22537 HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus);
22538 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc);
22539 HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
22540 HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags);
22541 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis);
22542 HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
22543} ma_IDirectSoundCaptureBufferVtbl;
22544struct ma_IDirectSoundCaptureBuffer
22545{
22546 ma_IDirectSoundCaptureBufferVtbl* lpVtbl;
22547};
22548static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
22549static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
22550static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
22551static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); }
22552static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); }
22553static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
22554static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
22555static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); }
22556static 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); }
22557static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); }
22558static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
22559static 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); }
22560
22561
22562/* IDirectSoundNotify */
22563typedef struct
22564{
22565 /* IUnknown */
22566 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject);
22567 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis);
22568 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis);
22569
22570 /* IDirectSoundNotify */
22571 HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies);
22572} ma_IDirectSoundNotifyVtbl;
22573struct ma_IDirectSoundNotify
22574{
22575 ma_IDirectSoundNotifyVtbl* lpVtbl;
22576};
22577static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
22578static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); }
22579static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); }
22580static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); }
22581
22582
22583typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (LPGUID pDeviceGUID, LPCSTR pDeviceDescription, LPCSTR pModule, LPVOID pContext);
22584typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, LPUNKNOWN pUnkOuter);
22585typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext);
22586typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, LPUNKNOWN pUnkOuter);
22587typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext);
22588
22589static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax)
22590{
22591 /* Normalize the range in case we were given something stupid. */
22592 if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) {
22593 sampleRateMin = (ma_uint32)ma_standard_sample_rate_min;
22594 }
22595 if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) {
22596 sampleRateMax = (ma_uint32)ma_standard_sample_rate_max;
22597 }
22598 if (sampleRateMin > sampleRateMax) {
22599 sampleRateMin = sampleRateMax;
22600 }
22601
22602 if (sampleRateMin == sampleRateMax) {
22603 return sampleRateMax;
22604 } else {
22605 size_t iStandardRate;
22606 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
22607 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
22608 if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) {
22609 return standardRate;
22610 }
22611 }
22612 }
22613
22614 /* Should never get here. */
22615 MA_ASSERT(MA_FALSE);
22616 return 0;
22617}
22618
22619/*
22620Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown,
22621the channel count and channel map will be left unmodified.
22622*/
22623static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut)
22624{
22625 WORD channels;
22626 DWORD channelMap;
22627
22628 channels = 0;
22629 if (pChannelsOut != NULL) {
22630 channels = *pChannelsOut;
22631 }
22632
22633 channelMap = 0;
22634 if (pChannelMapOut != NULL) {
22635 channelMap = *pChannelMapOut;
22636 }
22637
22638 /*
22639 The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper
22640 16 bits is for the geometry.
22641 */
22642 switch ((BYTE)(speakerConfig)) {
22643 case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
22644 case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break;
22645 case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
22646 case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
22647 case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break;
22648 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;
22649 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;
22650 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;
22651 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;
22652 default: break;
22653 }
22654
22655 if (pChannelsOut != NULL) {
22656 *pChannelsOut = channels;
22657 }
22658
22659 if (pChannelMapOut != NULL) {
22660 *pChannelMapOut = channelMap;
22661 }
22662}
22663
22664
22665static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound)
22666{
22667 ma_IDirectSound* pDirectSound;
22668 HWND hWnd;
22669 HRESULT hr;
22670
22671 MA_ASSERT(pContext != NULL);
22672 MA_ASSERT(ppDirectSound != NULL);
22673
22674 *ppDirectSound = NULL;
22675 pDirectSound = NULL;
22676
22677 if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) {
22678 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device.");
22680 }
22681
22682 /* The cooperative level must be set before doing anything else. */
22683 hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
22684 if (hWnd == NULL) {
22685 hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
22686 }
22687
22688 hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY);
22689 if (FAILED(hr)) {
22690 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.");
22691 return ma_result_from_HRESULT(hr);
22692 }
22693
22694 *ppDirectSound = pDirectSound;
22695 return MA_SUCCESS;
22696}
22697
22698static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture)
22699{
22700 ma_IDirectSoundCapture* pDirectSoundCapture;
22701 HRESULT hr;
22702
22703 MA_ASSERT(pContext != NULL);
22704 MA_ASSERT(ppDirectSoundCapture != NULL);
22705
22706 /* DirectSound does not support exclusive mode for capture. */
22707 if (shareMode == ma_share_mode_exclusive) {
22709 }
22710
22711 *ppDirectSoundCapture = NULL;
22712 pDirectSoundCapture = NULL;
22713
22714 hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL);
22715 if (FAILED(hr)) {
22716 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.");
22717 return ma_result_from_HRESULT(hr);
22718 }
22719
22720 *ppDirectSoundCapture = pDirectSoundCapture;
22721 return MA_SUCCESS;
22722}
22723
22724static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate)
22725{
22726 HRESULT hr;
22727 MA_DSCCAPS caps;
22728 WORD bitsPerSample;
22729 DWORD sampleRate;
22730
22731 MA_ASSERT(pContext != NULL);
22732 MA_ASSERT(pDirectSoundCapture != NULL);
22733
22734 if (pChannels) {
22735 *pChannels = 0;
22736 }
22737 if (pBitsPerSample) {
22738 *pBitsPerSample = 0;
22739 }
22740 if (pSampleRate) {
22741 *pSampleRate = 0;
22742 }
22743
22744 MA_ZERO_OBJECT(&caps);
22745 caps.dwSize = sizeof(caps);
22746 hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps);
22747 if (FAILED(hr)) {
22748 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device.");
22749 return ma_result_from_HRESULT(hr);
22750 }
22751
22752 if (pChannels) {
22753 *pChannels = (WORD)caps.dwChannels;
22754 }
22755
22756 /* 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. */
22757 bitsPerSample = 16;
22758 sampleRate = 48000;
22759
22760 if (caps.dwChannels == 1) {
22761 if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) {
22762 sampleRate = 48000;
22763 } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) {
22764 sampleRate = 44100;
22765 } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) {
22766 sampleRate = 22050;
22767 } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) {
22768 sampleRate = 11025;
22769 } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) {
22770 sampleRate = 96000;
22771 } else {
22772 bitsPerSample = 8;
22773 if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) {
22774 sampleRate = 48000;
22775 } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) {
22776 sampleRate = 44100;
22777 } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) {
22778 sampleRate = 22050;
22779 } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) {
22780 sampleRate = 11025;
22781 } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) {
22782 sampleRate = 96000;
22783 } else {
22784 bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
22785 }
22786 }
22787 } else if (caps.dwChannels == 2) {
22788 if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) {
22789 sampleRate = 48000;
22790 } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) {
22791 sampleRate = 44100;
22792 } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) {
22793 sampleRate = 22050;
22794 } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) {
22795 sampleRate = 11025;
22796 } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) {
22797 sampleRate = 96000;
22798 } else {
22799 bitsPerSample = 8;
22800 if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) {
22801 sampleRate = 48000;
22802 } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) {
22803 sampleRate = 44100;
22804 } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) {
22805 sampleRate = 22050;
22806 } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) {
22807 sampleRate = 11025;
22808 } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) {
22809 sampleRate = 96000;
22810 } else {
22811 bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
22812 }
22813 }
22814 }
22815
22816 if (pBitsPerSample) {
22817 *pBitsPerSample = bitsPerSample;
22818 }
22819 if (pSampleRate) {
22820 *pSampleRate = sampleRate;
22821 }
22822
22823 return MA_SUCCESS;
22824}
22825
22826
22827typedef struct
22828{
22829 ma_context* pContext;
22830 ma_device_type deviceType;
22832 void* pUserData;
22833 ma_bool32 terminated;
22834} ma_context_enumerate_devices_callback_data__dsound;
22835
22836static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
22837{
22838 ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext;
22839 ma_device_info deviceInfo;
22840
22841 (void)lpcstrModule;
22842
22843 MA_ZERO_OBJECT(&deviceInfo);
22844
22845 /* ID. */
22846 if (lpGuid != NULL) {
22847 MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16);
22848 } else {
22849 MA_ZERO_MEMORY(deviceInfo.id.dsound, 16);
22850 deviceInfo.isDefault = MA_TRUE;
22851 }
22852
22853 /* Name / Description */
22854 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1);
22855
22856
22857 /* Call the callback function, but make sure we stop enumerating if the callee requested so. */
22858 MA_ASSERT(pData != NULL);
22859 pData->terminated = !pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData);
22860 if (pData->terminated) {
22861 return FALSE; /* Stop enumeration. */
22862 } else {
22863 return TRUE; /* Continue enumeration. */
22864 }
22865}
22866
22867static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
22868{
22869 ma_context_enumerate_devices_callback_data__dsound data;
22870
22871 MA_ASSERT(pContext != NULL);
22872 MA_ASSERT(callback != NULL);
22873
22874 data.pContext = pContext;
22875 data.callback = callback;
22876 data.pUserData = pUserData;
22877 data.terminated = MA_FALSE;
22878
22879 /* Playback. */
22880 if (!data.terminated) {
22881 data.deviceType = ma_device_type_playback;
22882 ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
22883 }
22884
22885 /* Capture. */
22886 if (!data.terminated) {
22887 data.deviceType = ma_device_type_capture;
22888 ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
22889 }
22890
22891 return MA_SUCCESS;
22892}
22893
22894
22895typedef struct
22896{
22897 const ma_device_id* pDeviceID;
22898 ma_device_info* pDeviceInfo;
22899 ma_bool32 found;
22900} ma_context_get_device_info_callback_data__dsound;
22901
22902static BOOL CALLBACK ma_context_get_device_info_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
22903{
22904 ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext;
22905 MA_ASSERT(pData != NULL);
22906
22907 if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) {
22908 /* Default device. */
22909 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
22910 pData->pDeviceInfo->isDefault = MA_TRUE;
22911 pData->found = MA_TRUE;
22912 return FALSE; /* Stop enumeration. */
22913 } else {
22914 /* Not the default device. */
22915 if (lpGuid != NULL && pData->pDeviceID != NULL) {
22916 if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) {
22917 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
22918 pData->found = MA_TRUE;
22919 return FALSE; /* Stop enumeration. */
22920 }
22921 }
22922 }
22923
22924 (void)lpcstrModule;
22925 return TRUE;
22926}
22927
22928static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
22929{
22930 ma_result result;
22931 HRESULT hr;
22932
22933 if (pDeviceID != NULL) {
22934 ma_context_get_device_info_callback_data__dsound data;
22935
22936 /* ID. */
22937 MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16);
22938
22939 /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */
22940 data.pDeviceID = pDeviceID;
22941 data.pDeviceInfo = pDeviceInfo;
22942 data.found = MA_FALSE;
22943 if (deviceType == ma_device_type_playback) {
22944 ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
22945 } else {
22946 ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
22947 }
22948
22949 if (!data.found) {
22950 return MA_NO_DEVICE;
22951 }
22952 } else {
22953 /* 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. */
22954
22955 /* ID */
22956 MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16);
22957
22958 /* Name / Description */
22959 if (deviceType == ma_device_type_playback) {
22960 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
22961 } else {
22962 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
22963 }
22964
22965 pDeviceInfo->isDefault = MA_TRUE;
22966 }
22967
22968 /* Retrieving detailed information is slightly different depending on the device type. */
22969 if (deviceType == ma_device_type_playback) {
22970 /* Playback. */
22971 ma_IDirectSound* pDirectSound;
22972 MA_DSCAPS caps;
22973 WORD channels;
22974
22975 result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound);
22976 if (result != MA_SUCCESS) {
22977 return result;
22978 }
22979
22980 MA_ZERO_OBJECT(&caps);
22981 caps.dwSize = sizeof(caps);
22982 hr = ma_IDirectSound_GetCaps(pDirectSound, &caps);
22983 if (FAILED(hr)) {
22984 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.");
22985 return ma_result_from_HRESULT(hr);
22986 }
22987
22988
22989 /* Channels. Only a single channel count is reported for DirectSound. */
22990 if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
22991 /* It supports at least stereo, but could support more. */
22992 DWORD speakerConfig;
22993
22994 channels = 2;
22995
22996 /* Look at the speaker configuration to get a better idea on the channel count. */
22997 hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig);
22998 if (SUCCEEDED(hr)) {
22999 ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL);
23000 }
23001 } else {
23002 /* It does not support stereo, which means we are stuck with mono. */
23003 channels = 1;
23004 }
23005
23006
23007 /*
23008 In DirectSound, our native formats are centered around sample rates. All formats are supported, and we're only reporting a single channel
23009 count. However, DirectSound can report a range of supported sample rates. We're only going to include standard rates known by miniaudio
23010 in order to keep the size of this within reason.
23011 */
23012 if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
23013 /* Multiple sample rates are supported. We'll report in order of our preferred sample rates. */
23014 size_t iStandardSampleRate;
23015 for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {
23016 ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];
23017 if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) {
23019 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
23020 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
23021 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
23022 pDeviceInfo->nativeDataFormatCount += 1;
23023 }
23024 }
23025 } else {
23026 /* Only a single sample rate is supported. */
23028 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
23029 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate;
23030 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
23031 pDeviceInfo->nativeDataFormatCount += 1;
23032 }
23033
23034 ma_IDirectSound_Release(pDirectSound);
23035 } else {
23036 /*
23037 Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture
23038 devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just
23039 reporting the best format.
23040 */
23041 ma_IDirectSoundCapture* pDirectSoundCapture;
23042 WORD channels;
23043 WORD bitsPerSample;
23044 DWORD sampleRate;
23045
23046 result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture);
23047 if (result != MA_SUCCESS) {
23048 return result;
23049 }
23050
23051 result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate);
23052 if (result != MA_SUCCESS) {
23053 ma_IDirectSoundCapture_Release(pDirectSoundCapture);
23054 return result;
23055 }
23056
23057 ma_IDirectSoundCapture_Release(pDirectSoundCapture);
23058
23059 /* The format is always an integer format and is based on the bits per sample. */
23060 if (bitsPerSample == 8) {
23061 pDeviceInfo->nativeDataFormats[0].format = ma_format_u8;
23062 } else if (bitsPerSample == 16) {
23063 pDeviceInfo->nativeDataFormats[0].format = ma_format_s16;
23064 } else if (bitsPerSample == 24) {
23065 pDeviceInfo->nativeDataFormats[0].format = ma_format_s24;
23066 } else if (bitsPerSample == 32) {
23067 pDeviceInfo->nativeDataFormats[0].format = ma_format_s32;
23068 } else {
23070 }
23071
23072 pDeviceInfo->nativeDataFormats[0].channels = channels;
23073 pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate;
23074 pDeviceInfo->nativeDataFormats[0].flags = 0;
23075 pDeviceInfo->nativeDataFormatCount = 1;
23076 }
23077
23078 return MA_SUCCESS;
23079}
23080
23081
23082
23083static ma_result ma_device_uninit__dsound(ma_device* pDevice)
23084{
23085 MA_ASSERT(pDevice != NULL);
23086
23087 if (pDevice->dsound.pCaptureBuffer != NULL) {
23088 ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
23089 }
23090 if (pDevice->dsound.pCapture != NULL) {
23091 ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture);
23092 }
23093
23094 if (pDevice->dsound.pPlaybackBuffer != NULL) {
23095 ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
23096 }
23097 if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) {
23098 ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer);
23099 }
23100 if (pDevice->dsound.pPlayback != NULL) {
23101 ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback);
23102 }
23103
23104 return MA_SUCCESS;
23105}
23106
23107static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, WAVEFORMATEXTENSIBLE* pWF)
23108{
23109 GUID subformat;
23110
23111 if (format == ma_format_unknown) {
23112 format = MA_DEFAULT_FORMAT;
23113 }
23114
23115 if (channels == 0) {
23116 channels = MA_DEFAULT_CHANNELS;
23117 }
23118
23119 if (sampleRate == 0) {
23120 sampleRate = MA_DEFAULT_SAMPLE_RATE;
23121 }
23122
23123 switch (format)
23124 {
23125 case ma_format_u8:
23126 case ma_format_s16:
23127 case ma_format_s24:
23128 /*case ma_format_s24_32:*/
23129 case ma_format_s32:
23130 {
23131 subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
23132 } break;
23133
23134 case ma_format_f32:
23135 {
23136 subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
23137 } break;
23138
23139 default:
23141 }
23142
23143 MA_ZERO_OBJECT(pWF);
23144 pWF->Format.cbSize = sizeof(*pWF);
23145 pWF->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
23146 pWF->Format.nChannels = (WORD)channels;
23147 pWF->Format.nSamplesPerSec = (DWORD)sampleRate;
23148 pWF->Format.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8);
23149 pWF->Format.nBlockAlign = (WORD)(pWF->Format.nChannels * pWF->Format.wBitsPerSample / 8);
23150 pWF->Format.nAvgBytesPerSec = pWF->Format.nBlockAlign * pWF->Format.nSamplesPerSec;
23151 pWF->Samples.wValidBitsPerSample = pWF->Format.wBitsPerSample;
23152 pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels);
23153 pWF->SubFormat = subformat;
23154
23155 return MA_SUCCESS;
23156}
23157
23158static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
23159{
23160 /*
23161 DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for
23162 reliable glitch-free processing so going to use 30ms instead.
23163 */
23164 ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate);
23165 ma_uint32 periodSizeInFrames;
23166
23167 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);
23168 if (periodSizeInFrames < minPeriodSizeInFrames) {
23169 periodSizeInFrames = minPeriodSizeInFrames;
23170 }
23171
23172 return periodSizeInFrames;
23173}
23174
23175static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
23176{
23177 ma_result result;
23178 HRESULT hr;
23179
23180 MA_ASSERT(pDevice != NULL);
23181
23182 MA_ZERO_OBJECT(&pDevice->dsound);
23183
23184 if (pConfig->deviceType == ma_device_type_loopback) {
23186 }
23187
23188 /*
23189 Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize
23190 the capture device first because we'll want to match it's buffer size and period count on the playback side if we're using
23191 full-duplex mode.
23192 */
23193 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
23194 WAVEFORMATEXTENSIBLE wf;
23195 MA_DSCBUFFERDESC descDS;
23196 ma_uint32 periodSizeInFrames;
23197 ma_uint32 periodCount;
23198 char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
23199 WAVEFORMATEXTENSIBLE* pActualFormat;
23200
23201 result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf);
23202 if (result != MA_SUCCESS) {
23203 return result;
23204 }
23205
23206 result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture);
23207 if (result != MA_SUCCESS) {
23208 ma_device_uninit__dsound(pDevice);
23209 return result;
23210 }
23211
23212 result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.Format.nChannels, &wf.Format.wBitsPerSample, &wf.Format.nSamplesPerSec);
23213 if (result != MA_SUCCESS) {
23214 ma_device_uninit__dsound(pDevice);
23215 return result;
23216 }
23217
23218 wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8);
23219 wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
23220 wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample;
23221 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
23222
23223 /* The size of the buffer must be a clean multiple of the period count. */
23224 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.Format.nSamplesPerSec, pConfig->performanceProfile);
23225 periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS;
23226
23227 MA_ZERO_OBJECT(&descDS);
23228 descDS.dwSize = sizeof(descDS);
23229 descDS.dwFlags = 0;
23230 descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.Format.nBlockAlign;
23231 descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
23232 hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);
23233 if (FAILED(hr)) {
23234 ma_device_uninit__dsound(pDevice);
23235 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.");
23236 return ma_result_from_HRESULT(hr);
23237 }
23238
23239 /* Get the _actual_ properties of the buffer. */
23240 pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
23241 hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);
23242 if (FAILED(hr)) {
23243 ma_device_uninit__dsound(pDevice);
23244 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.");
23245 return ma_result_from_HRESULT(hr);
23246 }
23247
23248 /* We can now start setting the output data formats. */
23249 pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat);
23250 pDescriptorCapture->channels = pActualFormat->Format.nChannels;
23251 pDescriptorCapture->sampleRate = pActualFormat->Format.nSamplesPerSec;
23252
23253 /* Get the native channel map based on the channel mask. */
23254 if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
23255 ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
23256 } else {
23257 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
23258 }
23259
23260 /*
23261 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
23262 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.
23263 */
23264 if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) {
23265 descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount;
23266 ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
23267
23268 hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);
23269 if (FAILED(hr)) {
23270 ma_device_uninit__dsound(pDevice);
23271 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.");
23272 return ma_result_from_HRESULT(hr);
23273 }
23274 }
23275
23276 /* DirectSound should give us a buffer exactly the size we asked for. */
23277 pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
23278 pDescriptorCapture->periodCount = periodCount;
23279 }
23280
23281 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
23282 WAVEFORMATEXTENSIBLE wf;
23283 MA_DSBUFFERDESC descDSPrimary;
23284 MA_DSCAPS caps;
23285 char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
23286 WAVEFORMATEXTENSIBLE* pActualFormat;
23287 ma_uint32 periodSizeInFrames;
23288 ma_uint32 periodCount;
23289 MA_DSBUFFERDESC descDS;
23290
23291 result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf);
23292 if (result != MA_SUCCESS) {
23293 return result;
23294 }
23295
23296 result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback);
23297 if (result != MA_SUCCESS) {
23298 ma_device_uninit__dsound(pDevice);
23299 return result;
23300 }
23301
23302 MA_ZERO_OBJECT(&descDSPrimary);
23303 descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC);
23304 descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME;
23305 hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL);
23306 if (FAILED(hr)) {
23307 ma_device_uninit__dsound(pDevice);
23308 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.");
23309 return ma_result_from_HRESULT(hr);
23310 }
23311
23312
23313 /* We may want to make some adjustments to the format if we are using defaults. */
23314 MA_ZERO_OBJECT(&caps);
23315 caps.dwSize = sizeof(caps);
23316 hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps);
23317 if (FAILED(hr)) {
23318 ma_device_uninit__dsound(pDevice);
23319 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.");
23320 return ma_result_from_HRESULT(hr);
23321 }
23322
23323 if (pDescriptorPlayback->channels == 0) {
23324 if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
23325 DWORD speakerConfig;
23326
23327 /* It supports at least stereo, but could support more. */
23328 wf.Format.nChannels = 2;
23329
23330 /* Look at the speaker configuration to get a better idea on the channel count. */
23331 if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) {
23332 ma_get_channels_from_speaker_config__dsound(speakerConfig, &wf.Format.nChannels, &wf.dwChannelMask);
23333 }
23334 } else {
23335 /* It does not support stereo, which means we are stuck with mono. */
23336 wf.Format.nChannels = 1;
23337 }
23338 }
23339
23340 if (pDescriptorPlayback->sampleRate == 0) {
23341 /* We base the sample rate on the values returned by GetCaps(). */
23342 if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
23343 wf.Format.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate);
23344 } else {
23345 wf.Format.nSamplesPerSec = caps.dwMaxSecondarySampleRate;
23346 }
23347 }
23348
23349 wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8);
23350 wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
23351
23352 /*
23353 From MSDN:
23354
23355 The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest
23356 supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer
23357 and compare the result with the format that was requested with the SetFormat method.
23358 */
23359 hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)&wf);
23360 if (FAILED(hr)) {
23361 ma_device_uninit__dsound(pDevice);
23362 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer.");
23363 return ma_result_from_HRESULT(hr);
23364 }
23365
23366 /* Get the _actual_ properties of the buffer. */
23367 pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
23368 hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);
23369 if (FAILED(hr)) {
23370 ma_device_uninit__dsound(pDevice);
23371 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.");
23372 return ma_result_from_HRESULT(hr);
23373 }
23374
23375 /* We now have enough information to start setting some output properties. */
23376 pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat);
23377 pDescriptorPlayback->channels = pActualFormat->Format.nChannels;
23378 pDescriptorPlayback->sampleRate = pActualFormat->Format.nSamplesPerSec;
23379
23380 /* Get the internal channel map based on the channel mask. */
23381 if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
23382 ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
23383 } else {
23384 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
23385 }
23386
23387 /* The size of the buffer must be a clean multiple of the period count. */
23388 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
23389 periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS;
23390
23391 /*
23392 Meaning of dwFlags (from MSDN):
23393
23394 DSBCAPS_CTRLPOSITIONNOTIFY
23395 The buffer has position notification capability.
23396
23397 DSBCAPS_GLOBALFOCUS
23398 With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to
23399 another application, even if the new application uses DirectSound.
23400
23401 DSBCAPS_GETCURRENTPOSITION2
23402 In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated
23403 sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the
23404 application can get a more accurate play cursor.
23405 */
23406 MA_ZERO_OBJECT(&descDS);
23407 descDS.dwSize = sizeof(descDS);
23408 descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2;
23409 descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels);
23410 descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
23411 hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL);
23412 if (FAILED(hr)) {
23413 ma_device_uninit__dsound(pDevice);
23414 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.");
23415 return ma_result_from_HRESULT(hr);
23416 }
23417
23418 /* DirectSound should give us a buffer exactly the size we asked for. */
23419 pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
23420 pDescriptorPlayback->periodCount = periodCount;
23421 }
23422
23423 return MA_SUCCESS;
23424}
23425
23426
23427static ma_result ma_device_data_loop__dsound(ma_device* pDevice)
23428{
23429 ma_result result = MA_SUCCESS;
23430 ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
23431 ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
23432 HRESULT hr;
23433 DWORD lockOffsetInBytesCapture;
23434 DWORD lockSizeInBytesCapture;
23435 DWORD mappedSizeInBytesCapture;
23436 DWORD mappedDeviceFramesProcessedCapture;
23437 void* pMappedDeviceBufferCapture;
23438 DWORD lockOffsetInBytesPlayback;
23439 DWORD lockSizeInBytesPlayback;
23440 DWORD mappedSizeInBytesPlayback;
23441 void* pMappedDeviceBufferPlayback;
23442 DWORD prevReadCursorInBytesCapture = 0;
23443 DWORD prevPlayCursorInBytesPlayback = 0;
23444 ma_bool32 physicalPlayCursorLoopFlagPlayback = 0;
23445 DWORD virtualWriteCursorInBytesPlayback = 0;
23446 ma_bool32 virtualWriteCursorLoopFlagPlayback = 0;
23447 ma_bool32 isPlaybackDeviceStarted = MA_FALSE;
23448 ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */
23449 ma_uint32 waitTimeInMilliseconds = 1;
23450
23451 MA_ASSERT(pDevice != NULL);
23452
23453 /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */
23454 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23455 hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING);
23456 if (FAILED(hr)) {
23457 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed.");
23458 return ma_result_from_HRESULT(hr);
23459 }
23460 }
23461
23462 while (ma_device_get_state(pDevice) == ma_device_state_started) {
23463 switch (pDevice->type)
23464 {
23466 {
23467 DWORD physicalCaptureCursorInBytes;
23468 DWORD physicalReadCursorInBytes;
23469 hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
23470 if (FAILED(hr)) {
23471 return ma_result_from_HRESULT(hr);
23472 }
23473
23474 /* If nothing is available we just sleep for a bit and return from this iteration. */
23475 if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) {
23476 ma_sleep(waitTimeInMilliseconds);
23477 continue; /* Nothing is available in the capture buffer. */
23478 }
23479
23480 /*
23481 The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure
23482 we don't return until every frame has been copied over.
23483 */
23484 if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
23485 /* The capture position has not looped. This is the simple case. */
23486 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
23487 lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
23488 } else {
23489 /*
23490 The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
23491 do it again from the start.
23492 */
23493 if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
23494 /* Lock up to the end of the buffer. */
23495 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
23496 lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
23497 } else {
23498 /* Lock starting from the start of the buffer. */
23499 lockOffsetInBytesCapture = 0;
23500 lockSizeInBytesCapture = physicalReadCursorInBytes;
23501 }
23502 }
23503
23504 if (lockSizeInBytesCapture == 0) {
23505 ma_sleep(waitTimeInMilliseconds);
23506 continue; /* Nothing is available in the capture buffer. */
23507 }
23508
23509 hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
23510 if (FAILED(hr)) {
23511 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.");
23512 return ma_result_from_HRESULT(hr);
23513 }
23514
23515
23516 /* 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. */
23517 mappedDeviceFramesProcessedCapture = 0;
23518
23519 for (;;) { /* Keep writing to the playback device. */
23520 ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
23521 ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
23522 ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
23523 ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
23524 ma_uint32 outputFramesInClientFormatCount;
23525 ma_uint32 outputFramesInClientFormatConsumed = 0;
23526 ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap);
23527 ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture;
23528 void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture);
23529
23530 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess);
23531 if (result != MA_SUCCESS) {
23532 break;
23533 }
23534
23535 outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess;
23536 mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess;
23537
23538 ma_device__handle_data_callback(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess);
23539
23540 /* 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. */
23541 for (;;) {
23542 ma_uint32 framesWrittenThisIteration;
23543 DWORD physicalPlayCursorInBytes;
23544 DWORD physicalWriteCursorInBytes;
23545 DWORD availableBytesPlayback;
23546 DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */
23547
23548 /* We need the physical play and write cursors. */
23549 if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
23550 break;
23551 }
23552
23553 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
23554 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
23555 }
23556 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
23557
23558 /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
23559 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
23560 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
23561 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
23562 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
23563 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
23564 } else {
23565 /* This is an error. */
23566 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);
23567 availableBytesPlayback = 0;
23568 }
23569 } else {
23570 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
23571 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
23572 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
23573 } else {
23574 /* This is an error. */
23575 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);
23576 availableBytesPlayback = 0;
23577 }
23578 }
23579
23580 /* If there's no room available for writing we need to wait for more. */
23581 if (availableBytesPlayback == 0) {
23582 /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
23583 if (!isPlaybackDeviceStarted) {
23584 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
23585 if (FAILED(hr)) {
23586 ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
23587 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
23588 return ma_result_from_HRESULT(hr);
23589 }
23590 isPlaybackDeviceStarted = MA_TRUE;
23591 } else {
23592 ma_sleep(waitTimeInMilliseconds);
23593 continue;
23594 }
23595 }
23596
23597
23598 /* 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. */
23599 lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
23600 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
23601 /* Same loop iteration. Go up to the end of the buffer. */
23602 lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
23603 } else {
23604 /* Different loop iterations. Go up to the physical play cursor. */
23605 lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
23606 }
23607
23608 hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
23609 if (FAILED(hr)) {
23610 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.");
23611 result = ma_result_from_HRESULT(hr);
23612 break;
23613 }
23614
23615 /*
23616 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
23617 endless glitching due to it constantly running out of data.
23618 */
23619 if (isPlaybackDeviceStarted) {
23620 DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback;
23621 if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) {
23622 silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback;
23623 if (silentPaddingInBytes > lockSizeInBytesPlayback) {
23624 silentPaddingInBytes = lockSizeInBytesPlayback;
23625 }
23626
23627 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);
23628 }
23629 }
23630
23631 /* At this point we have a buffer for output. */
23632 if (silentPaddingInBytes > 0) {
23633 MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes);
23634 framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback;
23635 } else {
23636 ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed);
23637 ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback;
23638 void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback);
23639 void* pConvertedFramesOut = pMappedDeviceBufferPlayback;
23640
23641 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut);
23642 if (result != MA_SUCCESS) {
23643 break;
23644 }
23645
23646 outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut;
23647 framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut;
23648 }
23649
23650
23651 hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0);
23652 if (FAILED(hr)) {
23653 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.");
23654 result = ma_result_from_HRESULT(hr);
23655 break;
23656 }
23657
23658 virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback;
23659 if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) {
23660 virtualWriteCursorInBytesPlayback = 0;
23661 virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
23662 }
23663
23664 /*
23665 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
23666 a bit of a buffer to prevent the playback buffer from getting starved.
23667 */
23668 framesWrittenToPlaybackDevice += framesWrittenThisIteration;
23669 if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) {
23670 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
23671 if (FAILED(hr)) {
23672 ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
23673 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
23674 return ma_result_from_HRESULT(hr);
23675 }
23676 isPlaybackDeviceStarted = MA_TRUE;
23677 }
23678
23679 if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) {
23680 break; /* We're finished with the output data.*/
23681 }
23682 }
23683
23684 if (clientCapturedFramesToProcess == 0) {
23685 break; /* We just consumed every input sample. */
23686 }
23687 }
23688
23689
23690 /* At this point we're done with the mapped portion of the capture buffer. */
23691 hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
23692 if (FAILED(hr)) {
23693 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.");
23694 return ma_result_from_HRESULT(hr);
23695 }
23696 prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture);
23697 } break;
23698
23699
23700
23702 {
23703 DWORD physicalCaptureCursorInBytes;
23704 DWORD physicalReadCursorInBytes;
23705 hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
23706 if (FAILED(hr)) {
23707 return MA_ERROR;
23708 }
23709
23710 /* If the previous capture position is the same as the current position we need to wait a bit longer. */
23711 if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) {
23712 ma_sleep(waitTimeInMilliseconds);
23713 continue;
23714 }
23715
23716 /* Getting here means we have capture data available. */
23717 if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
23718 /* The capture position has not looped. This is the simple case. */
23719 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
23720 lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
23721 } else {
23722 /*
23723 The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
23724 do it again from the start.
23725 */
23726 if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
23727 /* Lock up to the end of the buffer. */
23728 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
23729 lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
23730 } else {
23731 /* Lock starting from the start of the buffer. */
23732 lockOffsetInBytesCapture = 0;
23733 lockSizeInBytesCapture = physicalReadCursorInBytes;
23734 }
23735 }
23736
23737 if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) {
23738 ma_sleep(waitTimeInMilliseconds);
23739 continue; /* Nothing is available in the capture buffer. */
23740 }
23741
23742 hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
23743 if (FAILED(hr)) {
23744 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.");
23745 result = ma_result_from_HRESULT(hr);
23746 }
23747
23748 if (lockSizeInBytesCapture != mappedSizeInBytesCapture) {
23749 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture);
23750 }
23751
23752 ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture);
23753
23754 hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
23755 if (FAILED(hr)) {
23756 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.");
23757 return ma_result_from_HRESULT(hr);
23758 }
23759 prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture;
23760
23761 if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) {
23762 prevReadCursorInBytesCapture = 0;
23763 }
23764 } break;
23765
23766
23767
23769 {
23770 DWORD availableBytesPlayback;
23771 DWORD physicalPlayCursorInBytes;
23772 DWORD physicalWriteCursorInBytes;
23773 hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
23774 if (FAILED(hr)) {
23775 break;
23776 }
23777
23778 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
23779 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
23780 }
23781 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
23782
23783 /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
23784 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
23785 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
23786 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
23787 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
23788 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
23789 } else {
23790 /* This is an error. */
23791 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);
23792 availableBytesPlayback = 0;
23793 }
23794 } else {
23795 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
23796 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
23797 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
23798 } else {
23799 /* This is an error. */
23800 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);
23801 availableBytesPlayback = 0;
23802 }
23803 }
23804
23805 /* If there's no room available for writing we need to wait for more. */
23806 if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) {
23807 /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
23808 if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) {
23809 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
23810 if (FAILED(hr)) {
23811 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
23812 return ma_result_from_HRESULT(hr);
23813 }
23814 isPlaybackDeviceStarted = MA_TRUE;
23815 } else {
23816 ma_sleep(waitTimeInMilliseconds);
23817 continue;
23818 }
23819 }
23820
23821 /* 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. */
23822 lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
23823 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
23824 /* Same loop iteration. Go up to the end of the buffer. */
23825 lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
23826 } else {
23827 /* Different loop iterations. Go up to the physical play cursor. */
23828 lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
23829 }
23830
23831 hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
23832 if (FAILED(hr)) {
23833 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.");
23834 result = ma_result_from_HRESULT(hr);
23835 break;
23836 }
23837
23838 /* At this point we have a buffer for output. */
23839 ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback);
23840
23841 hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0);
23842 if (FAILED(hr)) {
23843 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.");
23844 result = ma_result_from_HRESULT(hr);
23845 break;
23846 }
23847
23848 virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback;
23849 if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) {
23850 virtualWriteCursorInBytesPlayback = 0;
23851 virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
23852 }
23853
23854 /*
23855 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
23856 a bit of a buffer to prevent the playback buffer from getting starved.
23857 */
23858 framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback;
23859 if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) {
23860 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
23861 if (FAILED(hr)) {
23862 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
23863 return ma_result_from_HRESULT(hr);
23864 }
23865 isPlaybackDeviceStarted = MA_TRUE;
23866 }
23867 } break;
23868
23869
23870 default: return MA_INVALID_ARGS; /* Invalid device type. */
23871 }
23872
23873 if (result != MA_SUCCESS) {
23874 return result;
23875 }
23876 }
23877
23878 /* Getting here means the device is being stopped. */
23879 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23880 hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
23881 if (FAILED(hr)) {
23882 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.");
23883 return ma_result_from_HRESULT(hr);
23884 }
23885 }
23886
23887 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23888 /* 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. */
23889 if (isPlaybackDeviceStarted) {
23890 for (;;) {
23891 DWORD availableBytesPlayback = 0;
23892 DWORD physicalPlayCursorInBytes;
23893 DWORD physicalWriteCursorInBytes;
23894 hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
23895 if (FAILED(hr)) {
23896 break;
23897 }
23898
23899 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
23900 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
23901 }
23902 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
23903
23904 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
23905 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
23906 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
23907 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
23908 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
23909 } else {
23910 break;
23911 }
23912 } else {
23913 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
23914 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
23915 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
23916 } else {
23917 break;
23918 }
23919 }
23920
23921 if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) {
23922 break;
23923 }
23924
23925 ma_sleep(waitTimeInMilliseconds);
23926 }
23927 }
23928
23929 hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
23930 if (FAILED(hr)) {
23931 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed.");
23932 return ma_result_from_HRESULT(hr);
23933 }
23934
23935 ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0);
23936 }
23937
23938 return MA_SUCCESS;
23939}
23940
23941static ma_result ma_context_uninit__dsound(ma_context* pContext)
23942{
23943 MA_ASSERT(pContext != NULL);
23944 MA_ASSERT(pContext->backend == ma_backend_dsound);
23945
23946 ma_dlclose(pContext, pContext->dsound.hDSoundDLL);
23947
23948 return MA_SUCCESS;
23949}
23950
23951static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
23952{
23953 MA_ASSERT(pContext != NULL);
23954
23955 (void)pConfig;
23956
23957 pContext->dsound.hDSoundDLL = ma_dlopen(pContext, "dsound.dll");
23958 if (pContext->dsound.hDSoundDLL == NULL) {
23959 return MA_API_NOT_FOUND;
23960 }
23961
23962 pContext->dsound.DirectSoundCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCreate");
23963 pContext->dsound.DirectSoundEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA");
23964 pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate");
23965 pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA");
23966
23967 pCallbacks->onContextInit = ma_context_init__dsound;
23968 pCallbacks->onContextUninit = ma_context_uninit__dsound;
23969 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound;
23970 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound;
23971 pCallbacks->onDeviceInit = ma_device_init__dsound;
23972 pCallbacks->onDeviceUninit = ma_device_uninit__dsound;
23973 pCallbacks->onDeviceStart = NULL; /* Not used. Started in onDeviceDataLoop. */
23974 pCallbacks->onDeviceStop = NULL; /* Not used. Stopped in onDeviceDataLoop. */
23975 pCallbacks->onDeviceRead = NULL; /* Not used. Data is read directly in onDeviceDataLoop. */
23976 pCallbacks->onDeviceWrite = NULL; /* Not used. Data is written directly in onDeviceDataLoop. */
23977 pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound;
23978
23979 return MA_SUCCESS;
23980}
23981#endif
23982
23983
23984
23985
23990#ifdef MA_HAS_WINMM
23991
23992/*
23993Some older compilers don't have WAVEOUTCAPS2A and WAVEINCAPS2A, so we'll need to write this ourselves. These structures
23994are exactly the same as the older ones but they have a few GUIDs for manufacturer/product/name identification. I'm keeping
23995the names the same as the Win32 library for consistency, but namespaced to avoid naming conflicts with the Win32 version.
23996*/
23997typedef struct
23998{
23999 WORD wMid;
24000 WORD wPid;
24001 MMVERSION vDriverVersion;
24002 CHAR szPname[MAXPNAMELEN];
24003 DWORD dwFormats;
24004 WORD wChannels;
24005 WORD wReserved1;
24006 DWORD dwSupport;
24007 GUID ManufacturerGuid;
24008 GUID ProductGuid;
24009 GUID NameGuid;
24010} MA_WAVEOUTCAPS2A;
24011typedef struct
24012{
24013 WORD wMid;
24014 WORD wPid;
24015 MMVERSION vDriverVersion;
24016 CHAR szPname[MAXPNAMELEN];
24017 DWORD dwFormats;
24018 WORD wChannels;
24019 WORD wReserved1;
24020 GUID ManufacturerGuid;
24021 GUID ProductGuid;
24022 GUID NameGuid;
24023} MA_WAVEINCAPS2A;
24024
24025typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void);
24026typedef MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc);
24027typedef MMRESULT (WINAPI * MA_PFN_waveOutOpen)(LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
24028typedef MMRESULT (WINAPI * MA_PFN_waveOutClose)(HWAVEOUT hwo);
24029typedef MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
24030typedef MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
24031typedef MMRESULT (WINAPI * MA_PFN_waveOutWrite)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
24032typedef MMRESULT (WINAPI * MA_PFN_waveOutReset)(HWAVEOUT hwo);
24033typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void);
24034typedef MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic);
24035typedef MMRESULT (WINAPI * MA_PFN_waveInOpen)(LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
24036typedef MMRESULT (WINAPI * MA_PFN_waveInClose)(HWAVEIN hwi);
24037typedef MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
24038typedef MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
24039typedef MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
24040typedef MMRESULT (WINAPI * MA_PFN_waveInStart)(HWAVEIN hwi);
24041typedef MMRESULT (WINAPI * MA_PFN_waveInReset)(HWAVEIN hwi);
24042
24043static ma_result ma_result_from_MMRESULT(MMRESULT resultMM)
24044{
24045 switch (resultMM) {
24046 case MMSYSERR_NOERROR: return MA_SUCCESS;
24047 case MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS;
24048 case MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS;
24049 case MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY;
24050 case MMSYSERR_INVALFLAG: return MA_INVALID_ARGS;
24051 case MMSYSERR_INVALPARAM: return MA_INVALID_ARGS;
24052 case MMSYSERR_HANDLEBUSY: return MA_BUSY;
24053 case MMSYSERR_ERROR: return MA_ERROR;
24054 default: return MA_ERROR;
24055 }
24056}
24057
24058static char* ma_find_last_character(char* str, char ch)
24059{
24060 char* last;
24061
24062 if (str == NULL) {
24063 return NULL;
24064 }
24065
24066 last = NULL;
24067 while (*str != '\0') {
24068 if (*str == ch) {
24069 last = str;
24070 }
24071
24072 str += 1;
24073 }
24074
24075 return last;
24076}
24077
24078static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels)
24079{
24080 return periodSizeInFrames * ma_get_bytes_per_frame(format, channels);
24081}
24082
24083
24084/*
24085Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so
24086we can do things generically and typesafely. Names are being kept the same for consistency.
24087*/
24088typedef struct
24089{
24090 CHAR szPname[MAXPNAMELEN];
24091 DWORD dwFormats;
24092 WORD wChannels;
24093 GUID NameGuid;
24094} MA_WAVECAPSA;
24095
24096static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate)
24097{
24098 WORD bitsPerSample = 0;
24099 DWORD sampleRate = 0;
24100
24101 if (pBitsPerSample) {
24102 *pBitsPerSample = 0;
24103 }
24104 if (pSampleRate) {
24105 *pSampleRate = 0;
24106 }
24107
24108 if (channels == 1) {
24109 bitsPerSample = 16;
24110 if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
24111 sampleRate = 48000;
24112 } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
24113 sampleRate = 44100;
24114 } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
24115 sampleRate = 22050;
24116 } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
24117 sampleRate = 11025;
24118 } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
24119 sampleRate = 96000;
24120 } else {
24121 bitsPerSample = 8;
24122 if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
24123 sampleRate = 48000;
24124 } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
24125 sampleRate = 44100;
24126 } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
24127 sampleRate = 22050;
24128 } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
24129 sampleRate = 11025;
24130 } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
24131 sampleRate = 96000;
24132 } else {
24134 }
24135 }
24136 } else {
24137 bitsPerSample = 16;
24138 if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
24139 sampleRate = 48000;
24140 } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
24141 sampleRate = 44100;
24142 } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
24143 sampleRate = 22050;
24144 } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
24145 sampleRate = 11025;
24146 } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
24147 sampleRate = 96000;
24148 } else {
24149 bitsPerSample = 8;
24150 if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
24151 sampleRate = 48000;
24152 } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
24153 sampleRate = 44100;
24154 } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
24155 sampleRate = 22050;
24156 } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
24157 sampleRate = 11025;
24158 } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
24159 sampleRate = 96000;
24160 } else {
24162 }
24163 }
24164 }
24165
24166 if (pBitsPerSample) {
24167 *pBitsPerSample = bitsPerSample;
24168 }
24169 if (pSampleRate) {
24170 *pSampleRate = sampleRate;
24171 }
24172
24173 return MA_SUCCESS;
24174}
24175
24176static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, WAVEFORMATEX* pWF)
24177{
24178 ma_result result;
24179
24180 MA_ASSERT(pWF != NULL);
24181
24182 MA_ZERO_OBJECT(pWF);
24183 pWF->cbSize = sizeof(*pWF);
24184 pWF->wFormatTag = WAVE_FORMAT_PCM;
24185 pWF->nChannels = (WORD)channels;
24186 if (pWF->nChannels > 2) {
24187 pWF->nChannels = 2;
24188 }
24189
24190 result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec);
24191 if (result != MA_SUCCESS) {
24192 return result;
24193 }
24194
24195 pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8);
24196 pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec;
24197
24198 return MA_SUCCESS;
24199}
24200
24201static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo)
24202{
24203 WORD bitsPerSample;
24204 DWORD sampleRate;
24205 ma_result result;
24206
24207 MA_ASSERT(pContext != NULL);
24208 MA_ASSERT(pCaps != NULL);
24209 MA_ASSERT(pDeviceInfo != NULL);
24210
24211 /*
24212 Name / Description
24213
24214 Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking
24215 situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try
24216 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.
24217 */
24218
24219 /* Set the default to begin with. */
24220 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1);
24221
24222 /*
24223 Now try the registry. There's a few things to consider here:
24224 - The name GUID can be null, in which we case we just need to stick to the original 31 characters.
24225 - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters.
24226 - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The
24227 problem, however is that WASAPI and DirectSound use "<component> (<name>)" format (such as "Speakers (High Definition Audio)"),
24228 but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to
24229 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
24230 name, and then concatenate the name from the registry.
24231 */
24232 if (!ma_is_guid_null(&pCaps->NameGuid)) {
24233 wchar_t guidStrW[256];
24234 if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) {
24235 char guidStr[256];
24236 char keyStr[1024];
24237 HKEY hKey;
24238
24239 WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE);
24240
24241 ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\");
24242 ma_strcat_s(keyStr, sizeof(keyStr), guidStr);
24243
24244 if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
24245 BYTE nameFromReg[512];
24246 DWORD nameFromRegSize = sizeof(nameFromReg);
24247 LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize);
24248 ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey);
24249
24250 if (resultWin32 == ERROR_SUCCESS) {
24251 /* We have the value from the registry, so now we need to construct the name string. */
24252 char name[1024];
24253 if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) {
24254 char* nameBeg = ma_find_last_character(name, '(');
24255 if (nameBeg != NULL) {
24256 size_t leadingLen = (nameBeg - name);
24257 ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1);
24258
24259 /* The closing ")", if it can fit. */
24260 if (leadingLen + nameFromRegSize < sizeof(name)-1) {
24261 ma_strcat_s(name, sizeof(name), ")");
24262 }
24263
24264 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1);
24265 }
24266 }
24267 }
24268 }
24269 }
24270 }
24271
24272
24273 result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate);
24274 if (result != MA_SUCCESS) {
24275 return result;
24276 }
24277
24278 if (bitsPerSample == 8) {
24279 pDeviceInfo->nativeDataFormats[0].format = ma_format_u8;
24280 } else if (bitsPerSample == 16) {
24281 pDeviceInfo->nativeDataFormats[0].format = ma_format_s16;
24282 } else if (bitsPerSample == 24) {
24283 pDeviceInfo->nativeDataFormats[0].format = ma_format_s24;
24284 } else if (bitsPerSample == 32) {
24285 pDeviceInfo->nativeDataFormats[0].format = ma_format_s32;
24286 } else {
24288 }
24289 pDeviceInfo->nativeDataFormats[0].channels = pCaps->wChannels;
24290 pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate;
24291 pDeviceInfo->nativeDataFormats[0].flags = 0;
24292 pDeviceInfo->nativeDataFormatCount = 1;
24293
24294 return MA_SUCCESS;
24295}
24296
24297static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo)
24298{
24299 MA_WAVECAPSA caps;
24300
24301 MA_ASSERT(pContext != NULL);
24302 MA_ASSERT(pCaps != NULL);
24303 MA_ASSERT(pDeviceInfo != NULL);
24304
24305 MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
24306 caps.dwFormats = pCaps->dwFormats;
24307 caps.wChannels = pCaps->wChannels;
24308 caps.NameGuid = pCaps->NameGuid;
24309 return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
24310}
24311
24312static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo)
24313{
24314 MA_WAVECAPSA caps;
24315
24316 MA_ASSERT(pContext != NULL);
24317 MA_ASSERT(pCaps != NULL);
24318 MA_ASSERT(pDeviceInfo != NULL);
24319
24320 MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
24321 caps.dwFormats = pCaps->dwFormats;
24322 caps.wChannels = pCaps->wChannels;
24323 caps.NameGuid = pCaps->NameGuid;
24324 return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
24325}
24326
24327
24328static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
24329{
24330 UINT playbackDeviceCount;
24331 UINT captureDeviceCount;
24332 UINT iPlaybackDevice;
24333 UINT iCaptureDevice;
24334
24335 MA_ASSERT(pContext != NULL);
24336 MA_ASSERT(callback != NULL);
24337
24338 /* Playback. */
24339 playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)();
24340 for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) {
24341 MMRESULT result;
24342 MA_WAVEOUTCAPS2A caps;
24343
24344 MA_ZERO_OBJECT(&caps);
24345
24346 result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (WAVEOUTCAPSA*)&caps, sizeof(caps));
24347 if (result == MMSYSERR_NOERROR) {
24348 ma_device_info deviceInfo;
24349
24350 MA_ZERO_OBJECT(&deviceInfo);
24351 deviceInfo.id.winmm = iPlaybackDevice;
24352
24353 /* The first enumerated device is the default device. */
24354 if (iPlaybackDevice == 0) {
24355 deviceInfo.isDefault = MA_TRUE;
24356 }
24357
24358 if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
24359 ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
24360 if (cbResult == MA_FALSE) {
24361 return MA_SUCCESS; /* Enumeration was stopped. */
24362 }
24363 }
24364 }
24365 }
24366
24367 /* Capture. */
24368 captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)();
24369 for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) {
24370 MMRESULT result;
24371 MA_WAVEINCAPS2A caps;
24372
24373 MA_ZERO_OBJECT(&caps);
24374
24375 result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (WAVEINCAPSA*)&caps, sizeof(caps));
24376 if (result == MMSYSERR_NOERROR) {
24377 ma_device_info deviceInfo;
24378
24379 MA_ZERO_OBJECT(&deviceInfo);
24380 deviceInfo.id.winmm = iCaptureDevice;
24381
24382 /* The first enumerated device is the default device. */
24383 if (iCaptureDevice == 0) {
24384 deviceInfo.isDefault = MA_TRUE;
24385 }
24386
24387 if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
24388 ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
24389 if (cbResult == MA_FALSE) {
24390 return MA_SUCCESS; /* Enumeration was stopped. */
24391 }
24392 }
24393 }
24394 }
24395
24396 return MA_SUCCESS;
24397}
24398
24399static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
24400{
24401 UINT winMMDeviceID;
24402
24403 MA_ASSERT(pContext != NULL);
24404
24405 winMMDeviceID = 0;
24406 if (pDeviceID != NULL) {
24407 winMMDeviceID = (UINT)pDeviceID->winmm;
24408 }
24409
24410 pDeviceInfo->id.winmm = winMMDeviceID;
24411
24412 /* The first ID is the default device. */
24413 if (winMMDeviceID == 0) {
24414 pDeviceInfo->isDefault = MA_TRUE;
24415 }
24416
24417 if (deviceType == ma_device_type_playback) {
24418 MMRESULT result;
24419 MA_WAVEOUTCAPS2A caps;
24420
24421 MA_ZERO_OBJECT(&caps);
24422
24423 result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (WAVEOUTCAPSA*)&caps, sizeof(caps));
24424 if (result == MMSYSERR_NOERROR) {
24425 return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo);
24426 }
24427 } else {
24428 MMRESULT result;
24429 MA_WAVEINCAPS2A caps;
24430
24431 MA_ZERO_OBJECT(&caps);
24432
24433 result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (WAVEINCAPSA*)&caps, sizeof(caps));
24434 if (result == MMSYSERR_NOERROR) {
24435 return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo);
24436 }
24437 }
24438
24439 return MA_NO_DEVICE;
24440}
24441
24442
24443static ma_result ma_device_uninit__winmm(ma_device* pDevice)
24444{
24445 MA_ASSERT(pDevice != NULL);
24446
24447 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24448 ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture);
24449 CloseHandle((HANDLE)pDevice->winmm.hEventCapture);
24450 }
24451
24452 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24453 ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
24454 ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
24455 CloseHandle((HANDLE)pDevice->winmm.hEventPlayback);
24456 }
24457
24458 ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
24459
24460 MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */
24461
24462 return MA_SUCCESS;
24463}
24464
24465static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
24466{
24467 /* WinMM has a minimum period size of 40ms. */
24468 ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate);
24469 ma_uint32 periodSizeInFrames;
24470
24471 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);
24472 if (periodSizeInFrames < minPeriodSizeInFrames) {
24473 periodSizeInFrames = minPeriodSizeInFrames;
24474 }
24475
24476 return periodSizeInFrames;
24477}
24478
24479static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
24480{
24481 const char* errorMsg = "";
24482 ma_result errorCode = MA_ERROR;
24483 ma_result result = MA_SUCCESS;
24484 ma_uint32 heapSize;
24485 UINT winMMDeviceIDPlayback = 0;
24486 UINT winMMDeviceIDCapture = 0;
24487
24488 MA_ASSERT(pDevice != NULL);
24489
24490 MA_ZERO_OBJECT(&pDevice->winmm);
24491
24492 if (pConfig->deviceType == ma_device_type_loopback) {
24494 }
24495
24496 /* No exlusive mode with WinMM. */
24497 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
24498 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
24500 }
24501
24502 if (pDescriptorPlayback->pDeviceID != NULL) {
24503 winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm;
24504 }
24505 if (pDescriptorCapture->pDeviceID != NULL) {
24506 winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm;
24507 }
24508
24509 /* The capture device needs to be initialized first. */
24510 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
24511 WAVEINCAPSA caps;
24512 WAVEFORMATEX wf;
24513 MMRESULT resultMM;
24514
24515 /* We use an event to know when a new fragment needs to be enqueued. */
24516 pDevice->winmm.hEventCapture = (ma_handle)CreateEventW(NULL, TRUE, TRUE, NULL);
24517 if (pDevice->winmm.hEventCapture == NULL) {
24518 errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError());
24519 goto on_error;
24520 }
24521
24522 /* The format should be based on the device's actual format. */
24523 if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MMSYSERR_NOERROR) {
24524 errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
24525 goto on_error;
24526 }
24527
24528 result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
24529 if (result != MA_SUCCESS) {
24530 errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
24531 goto on_error;
24532 }
24533
24534 resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((LPHWAVEIN)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC);
24535 if (resultMM != MMSYSERR_NOERROR) {
24536 errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
24537 goto on_error;
24538 }
24539
24540 pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf);
24541 pDescriptorCapture->channels = wf.nChannels;
24542 pDescriptorCapture->sampleRate = wf.nSamplesPerSec;
24543 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
24544 pDescriptorCapture->periodCount = pDescriptorCapture->periodCount;
24545 pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
24546 }
24547
24548 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
24549 WAVEOUTCAPSA caps;
24550 WAVEFORMATEX wf;
24551 MMRESULT resultMM;
24552
24553 /* We use an event to know when a new fragment needs to be enqueued. */
24554 pDevice->winmm.hEventPlayback = (ma_handle)CreateEventW(NULL, TRUE, TRUE, NULL);
24555 if (pDevice->winmm.hEventPlayback == NULL) {
24556 errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError());
24557 goto on_error;
24558 }
24559
24560 /* The format should be based on the device's actual format. */
24561 if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MMSYSERR_NOERROR) {
24562 errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
24563 goto on_error;
24564 }
24565
24566 result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
24567 if (result != MA_SUCCESS) {
24568 errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
24569 goto on_error;
24570 }
24571
24572 resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((LPHWAVEOUT)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC);
24573 if (resultMM != MMSYSERR_NOERROR) {
24574 errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
24575 goto on_error;
24576 }
24577
24578 pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf);
24579 pDescriptorPlayback->channels = wf.nChannels;
24580 pDescriptorPlayback->sampleRate = wf.nSamplesPerSec;
24581 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);
24582 pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount;
24583 pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
24584 }
24585
24586 /*
24587 The heap allocated data is allocated like so:
24588
24589 [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer]
24590 */
24591 heapSize = 0;
24592 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
24593 heapSize += sizeof(WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels));
24594 }
24595 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
24596 heapSize += sizeof(WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels));
24597 }
24598
24599 pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks);
24600 if (pDevice->winmm._pHeapData == NULL) {
24601 errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY;
24602 goto on_error;
24603 }
24604
24605 MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize);
24606
24607 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
24608 ma_uint32 iPeriod;
24609
24610 if (pConfig->deviceType == ma_device_type_capture) {
24611 pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
24612 pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount));
24613 } else {
24614 pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
24615 pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount));
24616 }
24617
24618 /* Prepare headers. */
24619 for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) {
24620 ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels);
24621
24622 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod));
24623 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes;
24624 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L;
24625 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L;
24626 ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
24627
24628 /*
24629 The user data of the 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
24630 it's unlocked and available for writing. A value of 1 means it's locked.
24631 */
24632 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0;
24633 }
24634 }
24635
24636 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
24637 ma_uint32 iPeriod;
24638
24639 if (pConfig->deviceType == ma_device_type_playback) {
24640 pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData;
24641 pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*pDescriptorPlayback->periodCount);
24642 } else {
24643 pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount));
24644 pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels));
24645 }
24646
24647 /* Prepare headers. */
24648 for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) {
24649 ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels);
24650
24651 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod));
24652 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes;
24653 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L;
24654 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L;
24655 ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR));
24656
24657 /*
24658 The user data of the 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
24659 it's unlocked and available for writing. A value of 1 means it's locked.
24660 */
24661 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0;
24662 }
24663 }
24664
24665 return MA_SUCCESS;
24666
24667on_error:
24668 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24669 if (pDevice->winmm.pWAVEHDRCapture != NULL) {
24670 ma_uint32 iPeriod;
24671 for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) {
24672 ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
24673 }
24674 }
24675
24676 ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture);
24677 }
24678
24679 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24680 if (pDevice->winmm.pWAVEHDRCapture != NULL) {
24681 ma_uint32 iPeriod;
24682 for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) {
24683 ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR));
24684 }
24685 }
24686
24687 ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
24688 }
24689
24690 ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
24691
24692 if (errorMsg != NULL && errorMsg[0] != '\0') {
24693 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "%s", errorMsg);
24694 }
24695
24696 return errorCode;
24697}
24698
24699static ma_result ma_device_start__winmm(ma_device* pDevice)
24700{
24701 MA_ASSERT(pDevice != NULL);
24702
24703 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24704 MMRESULT resultMM;
24705 WAVEHDR* pWAVEHDR;
24706 ma_uint32 iPeriod;
24707
24708 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
24709
24710 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
24711 ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
24712
24713 /* 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. */
24714 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
24715 resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
24716 if (resultMM != MMSYSERR_NOERROR) {
24717 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.");
24718 return ma_result_from_MMRESULT(resultMM);
24719 }
24720
24721 /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */
24722 pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */
24723 }
24724
24725 /* Capture devices need to be explicitly started, unlike playback devices. */
24726 resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((HWAVEIN)pDevice->winmm.hDeviceCapture);
24727 if (resultMM != MMSYSERR_NOERROR) {
24728 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device.");
24729 return ma_result_from_MMRESULT(resultMM);
24730 }
24731 }
24732
24733 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24734 /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */
24735 }
24736
24737 return MA_SUCCESS;
24738}
24739
24740static ma_result ma_device_stop__winmm(ma_device* pDevice)
24741{
24742 MMRESULT resultMM;
24743
24744 MA_ASSERT(pDevice != NULL);
24745
24746 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24747 if (pDevice->winmm.hDeviceCapture == NULL) {
24748 return MA_INVALID_ARGS;
24749 }
24750
24751 resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((HWAVEIN)pDevice->winmm.hDeviceCapture);
24752 if (resultMM != MMSYSERR_NOERROR) {
24753 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device.");
24754 }
24755 }
24756
24757 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24758 ma_uint32 iPeriod;
24759 WAVEHDR* pWAVEHDR;
24760
24761 if (pDevice->winmm.hDevicePlayback == NULL) {
24762 return MA_INVALID_ARGS;
24763 }
24764
24765 /* 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. */
24766 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
24767 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) {
24768 if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */
24769 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
24770 break; /* An error occurred so just abandon ship and stop the device without draining. */
24771 }
24772
24773 pWAVEHDR[iPeriod].dwUser = 0;
24774 }
24775 }
24776
24777 resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
24778 if (resultMM != MMSYSERR_NOERROR) {
24779 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device.");
24780 }
24781 }
24782
24783 return MA_SUCCESS;
24784}
24785
24786static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
24787{
24788 ma_result result = MA_SUCCESS;
24789 MMRESULT resultMM;
24790 ma_uint32 totalFramesWritten;
24791 WAVEHDR* pWAVEHDR;
24792
24793 MA_ASSERT(pDevice != NULL);
24794 MA_ASSERT(pPCMFrames != NULL);
24795
24796 if (pFramesWritten != NULL) {
24797 *pFramesWritten = 0;
24798 }
24799
24800 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
24801
24802 /* Keep processing as much data as possible. */
24803 totalFramesWritten = 0;
24804 while (totalFramesWritten < frameCount) {
24805 /* If the current header has some space available we need to write part of it. */
24806 if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */
24807 /*
24808 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
24809 write it out and move on to the next iteration.
24810 */
24811 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
24812 ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback;
24813
24814 ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten));
24815 const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf);
24816 void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf);
24817 MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
24818
24819 pDevice->winmm.headerFramesConsumedPlayback += framesToCopy;
24820 totalFramesWritten += framesToCopy;
24821
24822 /* If we've consumed the buffer entirely we need to write it out to the device. */
24823 if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) {
24824 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */
24825 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
24826
24827 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
24828 ResetEvent((HANDLE)pDevice->winmm.hEventPlayback);
24829
24830 /* The device will be started here. */
24831 resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(WAVEHDR));
24832 if (resultMM != MMSYSERR_NOERROR) {
24833 result = ma_result_from_MMRESULT(resultMM);
24834 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed.");
24835 break;
24836 }
24837
24838 /* Make sure we move to the next header. */
24839 pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods;
24840 pDevice->winmm.headerFramesConsumedPlayback = 0;
24841 }
24842
24843 /* If at this point we have consumed the entire input buffer we can return. */
24844 MA_ASSERT(totalFramesWritten <= frameCount);
24845 if (totalFramesWritten == frameCount) {
24846 break;
24847 }
24848
24849 /* Getting here means there's more to process. */
24850 continue;
24851 }
24852
24853 /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */
24854 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
24855 result = MA_ERROR;
24856 break;
24857 }
24858
24859 /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
24860 if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & WHDR_DONE) != 0) {
24861 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */
24862 pDevice->winmm.headerFramesConsumedPlayback = 0;
24863 }
24864
24865 /* If the device has been stopped we need to break. */
24867 break;
24868 }
24869 }
24870
24871 if (pFramesWritten != NULL) {
24872 *pFramesWritten = totalFramesWritten;
24873 }
24874
24875 return result;
24876}
24877
24878static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
24879{
24880 ma_result result = MA_SUCCESS;
24881 MMRESULT resultMM;
24882 ma_uint32 totalFramesRead;
24883 WAVEHDR* pWAVEHDR;
24884
24885 MA_ASSERT(pDevice != NULL);
24886 MA_ASSERT(pPCMFrames != NULL);
24887
24888 if (pFramesRead != NULL) {
24889 *pFramesRead = 0;
24890 }
24891
24892 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
24893
24894 /* Keep processing as much data as possible. */
24895 totalFramesRead = 0;
24896 while (totalFramesRead < frameCount) {
24897 /* If the current header has some space available we need to write part of it. */
24898 if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */
24899 /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */
24900 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
24901 ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture;
24902
24903 ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead));
24904 const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf);
24905 void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf);
24906 MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
24907
24908 pDevice->winmm.headerFramesConsumedCapture += framesToCopy;
24909 totalFramesRead += framesToCopy;
24910
24911 /* If we've consumed the buffer entirely we need to add it back to the device. */
24912 if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) {
24913 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */
24914 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
24915
24916 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
24917 ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
24918
24919 /* The device will be started here. */
24920 resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(WAVEHDR));
24921 if (resultMM != MMSYSERR_NOERROR) {
24922 result = ma_result_from_MMRESULT(resultMM);
24923 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed.");
24924 break;
24925 }
24926
24927 /* Make sure we move to the next header. */
24928 pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods;
24929 pDevice->winmm.headerFramesConsumedCapture = 0;
24930 }
24931
24932 /* If at this point we have filled the entire input buffer we can return. */
24933 MA_ASSERT(totalFramesRead <= frameCount);
24934 if (totalFramesRead == frameCount) {
24935 break;
24936 }
24937
24938 /* Getting here means there's more to process. */
24939 continue;
24940 }
24941
24942 /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */
24943 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
24944 result = MA_ERROR;
24945 break;
24946 }
24947
24948 /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
24949 if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & WHDR_DONE) != 0) {
24950 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */
24951 pDevice->winmm.headerFramesConsumedCapture = 0;
24952 }
24953
24954 /* If the device has been stopped we need to break. */
24956 break;
24957 }
24958 }
24959
24960 if (pFramesRead != NULL) {
24961 *pFramesRead = totalFramesRead;
24962 }
24963
24964 return result;
24965}
24966
24967static ma_result ma_context_uninit__winmm(ma_context* pContext)
24968{
24969 MA_ASSERT(pContext != NULL);
24970 MA_ASSERT(pContext->backend == ma_backend_winmm);
24971
24972 ma_dlclose(pContext, pContext->winmm.hWinMM);
24973 return MA_SUCCESS;
24974}
24975
24976static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
24977{
24978 MA_ASSERT(pContext != NULL);
24979
24980 (void)pConfig;
24981
24982 pContext->winmm.hWinMM = ma_dlopen(pContext, "winmm.dll");
24983 if (pContext->winmm.hWinMM == NULL) {
24984 return MA_NO_BACKEND;
24985 }
24986
24987 pContext->winmm.waveOutGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetNumDevs");
24988 pContext->winmm.waveOutGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetDevCapsA");
24989 pContext->winmm.waveOutOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutOpen");
24990 pContext->winmm.waveOutClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutClose");
24991 pContext->winmm.waveOutPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutPrepareHeader");
24992 pContext->winmm.waveOutUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutUnprepareHeader");
24993 pContext->winmm.waveOutWrite = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutWrite");
24994 pContext->winmm.waveOutReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutReset");
24995 pContext->winmm.waveInGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetNumDevs");
24996 pContext->winmm.waveInGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetDevCapsA");
24997 pContext->winmm.waveInOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInOpen");
24998 pContext->winmm.waveInClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInClose");
24999 pContext->winmm.waveInPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInPrepareHeader");
25000 pContext->winmm.waveInUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInUnprepareHeader");
25001 pContext->winmm.waveInAddBuffer = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInAddBuffer");
25002 pContext->winmm.waveInStart = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInStart");
25003 pContext->winmm.waveInReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInReset");
25004
25005 pCallbacks->onContextInit = ma_context_init__winmm;
25006 pCallbacks->onContextUninit = ma_context_uninit__winmm;
25007 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm;
25008 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm;
25009 pCallbacks->onDeviceInit = ma_device_init__winmm;
25010 pCallbacks->onDeviceUninit = ma_device_uninit__winmm;
25011 pCallbacks->onDeviceStart = ma_device_start__winmm;
25012 pCallbacks->onDeviceStop = ma_device_stop__winmm;
25013 pCallbacks->onDeviceRead = ma_device_read__winmm;
25014 pCallbacks->onDeviceWrite = ma_device_write__winmm;
25015 pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */
25016
25017 return MA_SUCCESS;
25018}
25019#endif
25020
25021
25022
25023
25024
25029#ifdef MA_HAS_ALSA
25030
25031#include <poll.h> /* poll(), struct pollfd */
25032#include <sys/eventfd.h> /* eventfd() */
25033
25034#ifdef MA_NO_RUNTIME_LINKING
25035
25036/* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */
25037#if !defined(__cplusplus)
25038 #if defined(__STRICT_ANSI__)
25039 #if !defined(inline)
25040 #define inline __inline__ __attribute__((always_inline))
25041 #define MA_INLINE_DEFINED
25042 #endif
25043 #endif
25044#endif
25045#include <alsa/asoundlib.h>
25046#if defined(MA_INLINE_DEFINED)
25047 #undef inline
25048 #undef MA_INLINE_DEFINED
25049#endif
25050
25051typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t;
25052typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t;
25053typedef snd_pcm_stream_t ma_snd_pcm_stream_t;
25054typedef snd_pcm_format_t ma_snd_pcm_format_t;
25055typedef snd_pcm_access_t ma_snd_pcm_access_t;
25056typedef snd_pcm_t ma_snd_pcm_t;
25057typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
25058typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
25059typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
25060typedef snd_pcm_info_t ma_snd_pcm_info_t;
25061typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t;
25062typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t;
25063typedef snd_pcm_state_t ma_snd_pcm_state_t;
25064
25065/* snd_pcm_stream_t */
25066#define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK
25067#define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE
25068
25069/* snd_pcm_format_t */
25070#define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN
25071#define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8
25072#define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE
25073#define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE
25074#define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE
25075#define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE
25076#define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE
25077#define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE
25078#define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE
25079#define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE
25080#define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE
25081#define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE
25082#define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW
25083#define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW
25084#define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE
25085#define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE
25086
25087/* ma_snd_pcm_access_t */
25088#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED
25089#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED
25090#define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX
25091#define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED
25092#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED
25093
25094/* Channel positions. */
25095#define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN
25096#define MA_SND_CHMAP_NA SND_CHMAP_NA
25097#define MA_SND_CHMAP_MONO SND_CHMAP_MONO
25098#define MA_SND_CHMAP_FL SND_CHMAP_FL
25099#define MA_SND_CHMAP_FR SND_CHMAP_FR
25100#define MA_SND_CHMAP_RL SND_CHMAP_RL
25101#define MA_SND_CHMAP_RR SND_CHMAP_RR
25102#define MA_SND_CHMAP_FC SND_CHMAP_FC
25103#define MA_SND_CHMAP_LFE SND_CHMAP_LFE
25104#define MA_SND_CHMAP_SL SND_CHMAP_SL
25105#define MA_SND_CHMAP_SR SND_CHMAP_SR
25106#define MA_SND_CHMAP_RC SND_CHMAP_RC
25107#define MA_SND_CHMAP_FLC SND_CHMAP_FLC
25108#define MA_SND_CHMAP_FRC SND_CHMAP_FRC
25109#define MA_SND_CHMAP_RLC SND_CHMAP_RLC
25110#define MA_SND_CHMAP_RRC SND_CHMAP_RRC
25111#define MA_SND_CHMAP_FLW SND_CHMAP_FLW
25112#define MA_SND_CHMAP_FRW SND_CHMAP_FRW
25113#define MA_SND_CHMAP_FLH SND_CHMAP_FLH
25114#define MA_SND_CHMAP_FCH SND_CHMAP_FCH
25115#define MA_SND_CHMAP_FRH SND_CHMAP_FRH
25116#define MA_SND_CHMAP_TC SND_CHMAP_TC
25117#define MA_SND_CHMAP_TFL SND_CHMAP_TFL
25118#define MA_SND_CHMAP_TFR SND_CHMAP_TFR
25119#define MA_SND_CHMAP_TFC SND_CHMAP_TFC
25120#define MA_SND_CHMAP_TRL SND_CHMAP_TRL
25121#define MA_SND_CHMAP_TRR SND_CHMAP_TRR
25122#define MA_SND_CHMAP_TRC SND_CHMAP_TRC
25123#define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC
25124#define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC
25125#define MA_SND_CHMAP_TSL SND_CHMAP_TSL
25126#define MA_SND_CHMAP_TSR SND_CHMAP_TSR
25127#define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE
25128#define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE
25129#define MA_SND_CHMAP_BC SND_CHMAP_BC
25130#define MA_SND_CHMAP_BLC SND_CHMAP_BLC
25131#define MA_SND_CHMAP_BRC SND_CHMAP_BRC
25132
25133/* Open mode flags. */
25134#define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE
25135#define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS
25136#define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT
25137#else
25138#include <errno.h> /* For EPIPE, etc. */
25139typedef unsigned long ma_snd_pcm_uframes_t;
25140typedef long ma_snd_pcm_sframes_t;
25141typedef int ma_snd_pcm_stream_t;
25142typedef int ma_snd_pcm_format_t;
25143typedef int ma_snd_pcm_access_t;
25144typedef int ma_snd_pcm_state_t;
25145typedef struct ma_snd_pcm_t ma_snd_pcm_t;
25146typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
25147typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
25148typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
25149typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t;
25150typedef struct
25151{
25152 void* addr;
25153 unsigned int first;
25154 unsigned int step;
25155} ma_snd_pcm_channel_area_t;
25156typedef struct
25157{
25158 unsigned int channels;
25159 unsigned int pos[1];
25160} ma_snd_pcm_chmap_t;
25161
25162/* snd_pcm_state_t */
25163#define MA_SND_PCM_STATE_OPEN 0
25164#define MA_SND_PCM_STATE_SETUP 1
25165#define MA_SND_PCM_STATE_PREPARED 2
25166#define MA_SND_PCM_STATE_RUNNING 3
25167#define MA_SND_PCM_STATE_XRUN 4
25168#define MA_SND_PCM_STATE_DRAINING 5
25169#define MA_SND_PCM_STATE_PAUSED 6
25170#define MA_SND_PCM_STATE_SUSPENDED 7
25171#define MA_SND_PCM_STATE_DISCONNECTED 8
25172
25173/* snd_pcm_stream_t */
25174#define MA_SND_PCM_STREAM_PLAYBACK 0
25175#define MA_SND_PCM_STREAM_CAPTURE 1
25176
25177/* snd_pcm_format_t */
25178#define MA_SND_PCM_FORMAT_UNKNOWN -1
25179#define MA_SND_PCM_FORMAT_U8 1
25180#define MA_SND_PCM_FORMAT_S16_LE 2
25181#define MA_SND_PCM_FORMAT_S16_BE 3
25182#define MA_SND_PCM_FORMAT_S24_LE 6
25183#define MA_SND_PCM_FORMAT_S24_BE 7
25184#define MA_SND_PCM_FORMAT_S32_LE 10
25185#define MA_SND_PCM_FORMAT_S32_BE 11
25186#define MA_SND_PCM_FORMAT_FLOAT_LE 14
25187#define MA_SND_PCM_FORMAT_FLOAT_BE 15
25188#define MA_SND_PCM_FORMAT_FLOAT64_LE 16
25189#define MA_SND_PCM_FORMAT_FLOAT64_BE 17
25190#define MA_SND_PCM_FORMAT_MU_LAW 20
25191#define MA_SND_PCM_FORMAT_A_LAW 21
25192#define MA_SND_PCM_FORMAT_S24_3LE 32
25193#define MA_SND_PCM_FORMAT_S24_3BE 33
25194
25195/* snd_pcm_access_t */
25196#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0
25197#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1
25198#define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2
25199#define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3
25200#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4
25201
25202/* Channel positions. */
25203#define MA_SND_CHMAP_UNKNOWN 0
25204#define MA_SND_CHMAP_NA 1
25205#define MA_SND_CHMAP_MONO 2
25206#define MA_SND_CHMAP_FL 3
25207#define MA_SND_CHMAP_FR 4
25208#define MA_SND_CHMAP_RL 5
25209#define MA_SND_CHMAP_RR 6
25210#define MA_SND_CHMAP_FC 7
25211#define MA_SND_CHMAP_LFE 8
25212#define MA_SND_CHMAP_SL 9
25213#define MA_SND_CHMAP_SR 10
25214#define MA_SND_CHMAP_RC 11
25215#define MA_SND_CHMAP_FLC 12
25216#define MA_SND_CHMAP_FRC 13
25217#define MA_SND_CHMAP_RLC 14
25218#define MA_SND_CHMAP_RRC 15
25219#define MA_SND_CHMAP_FLW 16
25220#define MA_SND_CHMAP_FRW 17
25221#define MA_SND_CHMAP_FLH 18
25222#define MA_SND_CHMAP_FCH 19
25223#define MA_SND_CHMAP_FRH 20
25224#define MA_SND_CHMAP_TC 21
25225#define MA_SND_CHMAP_TFL 22
25226#define MA_SND_CHMAP_TFR 23
25227#define MA_SND_CHMAP_TFC 24
25228#define MA_SND_CHMAP_TRL 25
25229#define MA_SND_CHMAP_TRR 26
25230#define MA_SND_CHMAP_TRC 27
25231#define MA_SND_CHMAP_TFLC 28
25232#define MA_SND_CHMAP_TFRC 29
25233#define MA_SND_CHMAP_TSL 30
25234#define MA_SND_CHMAP_TSR 31
25235#define MA_SND_CHMAP_LLFE 32
25236#define MA_SND_CHMAP_RLFE 33
25237#define MA_SND_CHMAP_BC 34
25238#define MA_SND_CHMAP_BLC 35
25239#define MA_SND_CHMAP_BRC 36
25240
25241/* Open mode flags. */
25242#define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000
25243#define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000
25244#define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000
25245#endif
25246
25247typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode);
25248typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm);
25249typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void);
25250typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
25251typedef 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);
25252typedef 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);
25253typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask);
25254typedef int (* ma_snd_pcm_hw_params_set_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);
25255typedef 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);
25256typedef 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);
25257typedef 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);
25258typedef 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);
25259typedef 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);
25260typedef 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);
25261typedef 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);
25262typedef 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);
25263typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);
25264typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
25265typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
25266typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
25267typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
25268typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
25269typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
25270typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val);
25271typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
25272typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access);
25273typedef 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);
25274typedef int (* ma_snd_pcm_hw_params_test_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);
25275typedef 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);
25276typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
25277typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void);
25278typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
25279typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val);
25280typedef 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);
25281typedef 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);
25282typedef 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);
25283typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
25284typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void);
25285typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val);
25286typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm);
25287typedef ma_snd_pcm_state_t (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm);
25288typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm);
25289typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm);
25290typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm);
25291typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm);
25292typedef int (* ma_snd_pcm_reset_proc) (ma_snd_pcm_t *pcm);
25293typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints);
25294typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id);
25295typedef int (* ma_snd_card_get_index_proc) (const char *name);
25296typedef int (* ma_snd_device_name_free_hint_proc) (void **hints);
25297typedef 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);
25298typedef 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);
25299typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent);
25300typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size);
25301typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size);
25302typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm);
25303typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm);
25304typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout);
25305typedef int (* ma_snd_pcm_nonblock_proc) (ma_snd_pcm_t *pcm, int nonblock);
25306typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info);
25307typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void);
25308typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info);
25309typedef int (* ma_snd_pcm_poll_descriptors_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space);
25310typedef int (* ma_snd_pcm_poll_descriptors_count_proc) (ma_snd_pcm_t *pcm);
25311typedef int (* ma_snd_pcm_poll_descriptors_revents_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents);
25312typedef int (* ma_snd_config_update_free_global_proc) (void);
25313
25314/* This array specifies each of the common devices that can be used for both playback and capture. */
25315static const char* g_maCommonDeviceNamesALSA[] = {
25316 "default",
25317 "null",
25318 "pulse",
25319 "jack"
25320};
25321
25322/* This array allows us to blacklist specific playback devices. */
25323static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = {
25324 ""
25325};
25326
25327/* This array allows us to blacklist specific capture devices. */
25328static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = {
25329 ""
25330};
25331
25332
25333static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format)
25334{
25335 ma_snd_pcm_format_t ALSAFormats[] = {
25336 MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */
25337 MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */
25338 MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */
25339 MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */
25340 MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */
25341 MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */
25342 };
25343
25344 if (ma_is_big_endian()) {
25345 ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN;
25346 ALSAFormats[1] = MA_SND_PCM_FORMAT_U8;
25347 ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE;
25348 ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE;
25349 ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE;
25350 ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE;
25351 }
25352
25353 return ALSAFormats[format];
25354}
25355
25356static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA)
25357{
25358 if (ma_is_little_endian()) {
25359 switch (formatALSA) {
25360 case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16;
25361 case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24;
25362 case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32;
25363 case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32;
25364 default: break;
25365 }
25366 } else {
25367 switch (formatALSA) {
25368 case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16;
25369 case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24;
25370 case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32;
25371 case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32;
25372 default: break;
25373 }
25374 }
25375
25376 /* Endian agnostic. */
25377 switch (formatALSA) {
25378 case MA_SND_PCM_FORMAT_U8: return ma_format_u8;
25379 default: return ma_format_unknown;
25380 }
25381}
25382
25383static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos)
25384{
25385 switch (alsaChannelPos)
25386 {
25387 case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO;
25388 case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT;
25389 case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT;
25390 case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT;
25391 case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT;
25392 case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER;
25393 case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE;
25394 case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT;
25395 case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT;
25396 case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER;
25397 case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER;
25398 case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER;
25399 case MA_SND_CHMAP_RLC: return 0;
25400 case MA_SND_CHMAP_RRC: return 0;
25401 case MA_SND_CHMAP_FLW: return 0;
25402 case MA_SND_CHMAP_FRW: return 0;
25403 case MA_SND_CHMAP_FLH: return 0;
25404 case MA_SND_CHMAP_FCH: return 0;
25405 case MA_SND_CHMAP_FRH: return 0;
25406 case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER;
25407 case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT;
25408 case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT;
25409 case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER;
25410 case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT;
25411 case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT;
25412 case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER;
25413 default: break;
25414 }
25415
25416 return 0;
25417}
25418
25419static ma_bool32 ma_is_common_device_name__alsa(const char* name)
25420{
25421 size_t iName;
25422 for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) {
25423 if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) {
25424 return MA_TRUE;
25425 }
25426 }
25427
25428 return MA_FALSE;
25429}
25430
25431
25432static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name)
25433{
25434 size_t iName;
25435 for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) {
25436 if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) {
25437 return MA_TRUE;
25438 }
25439 }
25440
25441 return MA_FALSE;
25442}
25443
25444static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name)
25445{
25446 size_t iName;
25447 for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) {
25448 if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) {
25449 return MA_TRUE;
25450 }
25451 }
25452
25453 return MA_FALSE;
25454}
25455
25456static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name)
25457{
25458 if (deviceType == ma_device_type_playback) {
25459 return ma_is_playback_device_blacklisted__alsa(name);
25460 } else {
25461 return ma_is_capture_device_blacklisted__alsa(name);
25462 }
25463}
25464
25465
25466static const char* ma_find_char(const char* str, char c, int* index)
25467{
25468 int i = 0;
25469 for (;;) {
25470 if (str[i] == '\0') {
25471 if (index) *index = -1;
25472 return NULL;
25473 }
25474
25475 if (str[i] == c) {
25476 if (index) *index = i;
25477 return str + i;
25478 }
25479
25480 i += 1;
25481 }
25482
25483 /* Should never get here, but treat it as though the character was not found to make me feel better inside. */
25484 if (index) *index = -1;
25485 return NULL;
25486}
25487
25488static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid)
25489{
25490 /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */
25491
25492 int commaPos;
25493 const char* dev;
25494 int i;
25495
25496 if (hwid == NULL) {
25497 return MA_FALSE;
25498 }
25499
25500 if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') {
25501 return MA_FALSE;
25502 }
25503
25504 hwid += 3;
25505
25506 dev = ma_find_char(hwid, ',', &commaPos);
25507 if (dev == NULL) {
25508 return MA_FALSE;
25509 } else {
25510 dev += 1; /* Skip past the ",". */
25511 }
25512
25513 /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */
25514 for (i = 0; i < commaPos; ++i) {
25515 if (hwid[i] < '0' || hwid[i] > '9') {
25516 return MA_FALSE;
25517 }
25518 }
25519
25520 /* Check if everything after the "," is numeric. If not, return false. */
25521 i = 0;
25522 while (dev[i] != '\0') {
25523 if (dev[i] < '0' || dev[i] > '9') {
25524 return MA_FALSE;
25525 }
25526 i += 1;
25527 }
25528
25529 return MA_TRUE;
25530}
25531
25532static 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. */
25533{
25534 /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */
25535
25536 int colonPos;
25537 int commaPos;
25538 char card[256];
25539 const char* dev;
25540 int cardIndex;
25541
25542 if (dst == NULL) {
25543 return -1;
25544 }
25545 if (dstSize < 7) {
25546 return -1; /* Absolute minimum size of the output buffer is 7 bytes. */
25547 }
25548
25549 *dst = '\0'; /* Safety. */
25550 if (src == NULL) {
25551 return -1;
25552 }
25553
25554 /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */
25555 if (ma_is_device_name_in_hw_format__alsa(src)) {
25556 return ma_strcpy_s(dst, dstSize, src);
25557 }
25558
25559 src = ma_find_char(src, ':', &colonPos);
25560 if (src == NULL) {
25561 return -1; /* Couldn't find a colon */
25562 }
25563
25564 dev = ma_find_char(src, ',', &commaPos);
25565 if (dev == NULL) {
25566 dev = "0";
25567 ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */
25568 } else {
25569 dev = dev + 5; /* +5 = ",DEV=" */
25570 ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */
25571 }
25572
25573 cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card);
25574 if (cardIndex < 0) {
25575 return -2; /* Failed to retrieve the card index. */
25576 }
25577
25578
25579 /* Construction. */
25580 dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':';
25581 if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) {
25582 return -3;
25583 }
25584 if (ma_strcat_s(dst, dstSize, ",") != 0) {
25585 return -3;
25586 }
25587 if (ma_strcat_s(dst, dstSize, dev) != 0) {
25588 return -3;
25589 }
25590
25591 return 0;
25592}
25593
25594static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID)
25595{
25596 ma_uint32 i;
25597
25598 MA_ASSERT(pHWID != NULL);
25599
25600 for (i = 0; i < count; ++i) {
25601 if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) {
25602 return MA_TRUE;
25603 }
25604 }
25605
25606 return MA_FALSE;
25607}
25608
25609
25610static 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)
25611{
25612 ma_snd_pcm_t* pPCM;
25613 ma_snd_pcm_stream_t stream;
25614
25615 MA_ASSERT(pContext != NULL);
25616 MA_ASSERT(ppPCM != NULL);
25617
25618 *ppPCM = NULL;
25619 pPCM = NULL;
25620
25621 stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE;
25622
25623 if (pDeviceID == NULL) {
25624 ma_bool32 isDeviceOpen;
25625 size_t i;
25626
25627 /*
25628 We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes
25629 me feel better to try as hard as we can get to get _something_ working.
25630 */
25631 const char* defaultDeviceNames[] = {
25632 "default",
25633 NULL,
25634 NULL,
25635 NULL,
25636 NULL,
25637 NULL,
25638 NULL
25639 };
25640
25641 if (shareMode == ma_share_mode_exclusive) {
25642 defaultDeviceNames[1] = "hw";
25643 defaultDeviceNames[2] = "hw:0";
25644 defaultDeviceNames[3] = "hw:0,0";
25645 } else {
25646 if (deviceType == ma_device_type_playback) {
25647 defaultDeviceNames[1] = "dmix";
25648 defaultDeviceNames[2] = "dmix:0";
25649 defaultDeviceNames[3] = "dmix:0,0";
25650 } else {
25651 defaultDeviceNames[1] = "dsnoop";
25652 defaultDeviceNames[2] = "dsnoop:0";
25653 defaultDeviceNames[3] = "dsnoop:0,0";
25654 }
25655 defaultDeviceNames[4] = "hw";
25656 defaultDeviceNames[5] = "hw:0";
25657 defaultDeviceNames[6] = "hw:0,0";
25658 }
25659
25660 isDeviceOpen = MA_FALSE;
25661 for (i = 0; i < ma_countof(defaultDeviceNames); ++i) {
25662 if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') {
25663 if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) {
25664 isDeviceOpen = MA_TRUE;
25665 break;
25666 }
25667 }
25668 }
25669
25670 if (!isDeviceOpen) {
25671 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.");
25673 }
25674 } else {
25675 /*
25676 We're trying to open a specific device. There's a few things to consider here:
25677
25678 miniaudio recongnizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When
25679 an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it
25680 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").
25681 */
25682
25683 /* May end up needing to make small adjustments to the ID, so make a copy. */
25684 ma_device_id deviceID = *pDeviceID;
25685 int resultALSA = -ENODEV;
25686
25687 if (deviceID.alsa[0] != ':') {
25688 /* The ID is not in ":0,0" format. Use the ID exactly as-is. */
25689 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode);
25690 } else {
25691 char hwid[256];
25692
25693 /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */
25694 if (deviceID.alsa[1] == '\0') {
25695 deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */
25696 }
25697
25698 if (shareMode == ma_share_mode_shared) {
25699 if (deviceType == ma_device_type_playback) {
25700 ma_strcpy_s(hwid, sizeof(hwid), "dmix");
25701 } else {
25702 ma_strcpy_s(hwid, sizeof(hwid), "dsnoop");
25703 }
25704
25705 if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
25706 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
25707 }
25708 }
25709
25710 /* 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. */
25711 if (resultALSA != 0) {
25712 ma_strcpy_s(hwid, sizeof(hwid), "hw");
25713 if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
25714 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
25715 }
25716 }
25717 }
25718
25719 if (resultALSA < 0) {
25720 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed.");
25721 return ma_result_from_errno(-resultALSA);
25722 }
25723 }
25724
25725 *ppPCM = pPCM;
25726 return MA_SUCCESS;
25727}
25728
25729
25730static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
25731{
25732 int resultALSA;
25733 ma_bool32 cbResult = MA_TRUE;
25734 char** ppDeviceHints;
25735 ma_device_id* pUniqueIDs = NULL;
25736 ma_uint32 uniqueIDCount = 0;
25737 char** ppNextDeviceHint;
25738
25739 MA_ASSERT(pContext != NULL);
25740 MA_ASSERT(callback != NULL);
25741
25742 ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock);
25743
25744 resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints);
25745 if (resultALSA < 0) {
25746 ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
25747 return ma_result_from_errno(-resultALSA);
25748 }
25749
25750 ppNextDeviceHint = ppDeviceHints;
25751 while (*ppNextDeviceHint != NULL) {
25752 char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME");
25753 char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC");
25754 char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID");
25756 ma_bool32 stopEnumeration = MA_FALSE;
25757 char hwid[sizeof(pUniqueIDs->alsa)];
25758 ma_device_info deviceInfo;
25759
25760 if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) {
25761 deviceType = ma_device_type_playback;
25762 }
25763 if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) {
25764 deviceType = ma_device_type_capture;
25765 }
25766
25767 if (NAME != NULL) {
25768 if (pContext->alsa.useVerboseDeviceEnumeration) {
25769 /* Verbose mode. Use the name exactly as-is. */
25770 ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
25771 } else {
25772 /* Simplified mode. Use ":%d,%d" format. */
25773 if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) {
25774 /*
25775 At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the
25776 plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device
25777 initialization time and is used as an indicator to try and use the most appropriate plugin depending on the
25778 device type and sharing mode.
25779 */
25780 char* dst = hwid;
25781 char* src = hwid+2;
25782 while ((*dst++ = *src++));
25783 } else {
25784 /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */
25785 ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
25786 }
25787
25788 if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) {
25789 goto next_device; /* The device has already been enumerated. Move on to the next one. */
25790 } else {
25791 /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */
25792 size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1);
25793 ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks);
25794 if (pNewUniqueIDs == NULL) {
25795 goto next_device; /* Failed to allocate memory. */
25796 }
25797
25798 pUniqueIDs = pNewUniqueIDs;
25799 MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid));
25800 uniqueIDCount += 1;
25801 }
25802 }
25803 } else {
25804 MA_ZERO_MEMORY(hwid, sizeof(hwid));
25805 }
25806
25807 MA_ZERO_OBJECT(&deviceInfo);
25808 ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1);
25809
25810 /*
25811 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
25812 just use the name of "default" as the indicator.
25813 */
25814 if (ma_strcmp(deviceInfo.id.alsa, "default") == 0) {
25815 deviceInfo.isDefault = MA_TRUE;
25816 }
25817
25818
25819 /*
25820 DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose
25821 device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish
25822 between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the
25823 description.
25824
25825 The value in DESC seems to be split into two lines, with the first line being the name of the device and the
25826 second line being a description of the device. I don't like having the description be across two lines because
25827 it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line
25828 being put into parentheses. In simplified mode I'm just stripping the second line entirely.
25829 */
25830 if (DESC != NULL) {
25831 int lfPos;
25832 const char* line2 = ma_find_char(DESC, '\n', &lfPos);
25833 if (line2 != NULL) {
25834 line2 += 1; /* Skip past the new-line character. */
25835
25836 if (pContext->alsa.useVerboseDeviceEnumeration) {
25837 /* Verbose mode. Put the second line in brackets. */
25838 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
25839 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " (");
25840 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2);
25841 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")");
25842 } else {
25843 /* Simplified mode. Strip the second line entirely. */
25844 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
25845 }
25846 } else {
25847 /* There's no second line. Just copy the whole description. */
25848 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1);
25849 }
25850 }
25851
25852 if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) {
25853 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
25854 }
25855
25856 /*
25857 Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback
25858 again for the other device type in this case. We do this for known devices and where the IOID hint is NULL, which
25859 means both Input and Output.
25860 */
25861 if (cbResult) {
25862 if (ma_is_common_device_name__alsa(NAME) || IOID == NULL) {
25863 if (deviceType == ma_device_type_playback) {
25864 if (!ma_is_capture_device_blacklisted__alsa(NAME)) {
25865 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
25866 }
25867 } else {
25868 if (!ma_is_playback_device_blacklisted__alsa(NAME)) {
25869 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
25870 }
25871 }
25872 }
25873 }
25874
25875 if (cbResult == MA_FALSE) {
25876 stopEnumeration = MA_TRUE;
25877 }
25878
25879 next_device:
25880 free(NAME);
25881 free(DESC);
25882 free(IOID);
25883 ppNextDeviceHint += 1;
25884
25885 /* We need to stop enumeration if the callback returned false. */
25886 if (stopEnumeration) {
25887 break;
25888 }
25889 }
25890
25891 ma_free(pUniqueIDs, &pContext->allocationCallbacks);
25892 ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);
25893
25894 ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
25895
25896 return MA_SUCCESS;
25897}
25898
25899
25900typedef struct
25901{
25902 ma_device_type deviceType;
25903 const ma_device_id* pDeviceID;
25904 ma_share_mode shareMode;
25905 ma_device_info* pDeviceInfo;
25906 ma_bool32 foundDevice;
25907} ma_context_get_device_info_enum_callback_data__alsa;
25908
25909static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData)
25910{
25911 ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData;
25912 MA_ASSERT(pData != NULL);
25913
25914 (void)pContext;
25915
25916 if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
25917 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
25918 pData->foundDevice = MA_TRUE;
25919 } else {
25920 if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) {
25921 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
25922 pData->foundDevice = MA_TRUE;
25923 }
25924 }
25925
25926 /* Keep enumerating until we have found the device. */
25927 return !pData->foundDevice;
25928}
25929
25930static 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)
25931{
25932 MA_ASSERT(pPCM != NULL);
25933 MA_ASSERT(pHWParams != NULL);
25934 MA_ASSERT(pDeviceInfo != NULL);
25935
25936 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) {
25937 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
25938 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
25939 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
25940 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
25941 pDeviceInfo->nativeDataFormatCount += 1;
25942 }
25943}
25944
25945static 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)
25946{
25947 ma_uint32 iSampleRate;
25948 unsigned int minSampleRate;
25949 unsigned int maxSampleRate;
25950 int sampleRateDir; /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */
25951
25952 /* There could be a range. */
25953 ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir);
25954 ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir);
25955
25956 /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculus. */
25957 minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);
25958 maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);
25959
25960 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {
25961 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];
25962
25963 if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {
25964 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo);
25965 }
25966 }
25967
25968 /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */
25969 if (!ma_is_standard_sample_rate(minSampleRate)) {
25970 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo);
25971 }
25972
25973 if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) {
25974 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo);
25975 }
25976}
25977
25978static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
25979{
25980 ma_context_get_device_info_enum_callback_data__alsa data;
25981 ma_result result;
25982 int resultALSA;
25983 ma_snd_pcm_t* pPCM;
25984 ma_snd_pcm_hw_params_t* pHWParams;
25985 ma_uint32 iFormat;
25986 ma_uint32 iChannel;
25987
25988 MA_ASSERT(pContext != NULL);
25989
25990 /* We just enumerate to find basic information about the device. */
25991 data.deviceType = deviceType;
25992 data.pDeviceID = pDeviceID;
25993 data.pDeviceInfo = pDeviceInfo;
25994 data.foundDevice = MA_FALSE;
25995 result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data);
25996 if (result != MA_SUCCESS) {
25997 return result;
25998 }
25999
26000 if (!data.foundDevice) {
26001 return MA_NO_DEVICE;
26002 }
26003
26004 if (ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
26005 pDeviceInfo->isDefault = MA_TRUE;
26006 }
26007
26008 /* For detailed info we need to open the device. */
26009 result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM);
26010 if (result != MA_SUCCESS) {
26011 return result;
26012 }
26013
26014 /* We need to initialize a HW parameters object in order to know what formats are supported. */
26015 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);
26016 if (pHWParams == NULL) {
26017 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
26018 return MA_OUT_OF_MEMORY;
26019 }
26020
26021 resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
26022 if (resultALSA < 0) {
26023 ma_free(pHWParams, &pContext->allocationCallbacks);
26024 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
26025 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.");
26026 return ma_result_from_errno(-resultALSA);
26027 }
26028
26029 /*
26030 Some ALSA devices can support many permutations of formats, channels and rates. We only support
26031 a fixed number of permutations which means we need to employ some strategies to ensure the best
26032 combinations are returned. An example is the "pulse" device which can do it's own data conversion
26033 in software and as a result can support any combination of format, channels and rate.
26034
26035 We want to ensure the the first data formats are the best. We have a list of favored sample
26036 formats and sample rates, so these will be the basis of our iteration.
26037 */
26038
26039 /* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */
26040 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
26041 ma_format format = g_maFormatPriorities[iFormat];
26042
26043 /*
26044 For each format we need to make sure we reset the configuration space so we don't return
26045 channel counts and rates that aren't compatible with a format.
26046 */
26047 ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
26048
26049 /* Test the format first. If this fails it means the format is not supported and we can skip it. */
26050 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) {
26051 /* The format is supported. */
26052 unsigned int minChannels;
26053 unsigned int maxChannels;
26054
26055 /*
26056 The configuration space needs to be restricted to this format so we can get an accurate
26057 picture of which sample rates and channel counts are support with this format.
26058 */
26059 ((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));
26060
26061 /* Now we need to check for supported channels. */
26062 ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels);
26063 ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels);
26064
26065 if (minChannels > MA_MAX_CHANNELS) {
26066 continue; /* Too many channels. */
26067 }
26068 if (maxChannels < MA_MIN_CHANNELS) {
26069 continue; /* Not enough channels. */
26070 }
26071
26072 /*
26073 Make sure the channel count is clamped. This is mainly intended for the max channels
26074 because some devices can report an unbound maximum.
26075 */
26076 minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
26077 maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
26078
26079 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
26080 /* 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. */
26081 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. */
26082 } else {
26083 /* The device only supports a specific set of channels. We need to iterate over all of them. */
26084 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
26085 /* Test the channel before applying it to the configuration space. */
26086 unsigned int channels = iChannel;
26087
26088 /* Make sure our channel range is reset before testing again or else we'll always fail the test. */
26089 ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
26090 ((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));
26091
26092 if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) {
26093 /* The channel count is supported. */
26094
26095 /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */
26096 ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels);
26097
26098 /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */
26099 ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo);
26100 } else {
26101 /* The channel count is not supported. Skip. */
26102 }
26103 }
26104 }
26105 } else {
26106 /* The format is not supported. Skip. */
26107 }
26108 }
26109
26110 ma_free(pHWParams, &pContext->allocationCallbacks);
26111
26112 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
26113 return MA_SUCCESS;
26114}
26115
26116static ma_result ma_device_uninit__alsa(ma_device* pDevice)
26117{
26118 MA_ASSERT(pDevice != NULL);
26119
26120 if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) {
26121 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
26122 close(pDevice->alsa.wakeupfdCapture);
26123 ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks);
26124 }
26125
26126 if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) {
26127 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
26128 close(pDevice->alsa.wakeupfdPlayback);
26129 ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks);
26130 }
26131
26132 return MA_SUCCESS;
26133}
26134
26135static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
26136{
26137 ma_result result;
26138 int resultALSA;
26139 ma_snd_pcm_t* pPCM;
26140 ma_bool32 isUsingMMap;
26141 ma_snd_pcm_format_t formatALSA;
26142 ma_format internalFormat;
26143 ma_uint32 internalChannels;
26144 ma_uint32 internalSampleRate;
26145 ma_channel internalChannelMap[MA_MAX_CHANNELS];
26146 ma_uint32 internalPeriodSizeInFrames;
26147 ma_uint32 internalPeriods;
26148 int openMode;
26149 ma_snd_pcm_hw_params_t* pHWParams;
26150 ma_snd_pcm_sw_params_t* pSWParams;
26151 ma_snd_pcm_uframes_t bufferBoundary;
26152 int pollDescriptorCount;
26153 struct pollfd* pPollDescriptors;
26154 int wakeupfd;
26155
26156 MA_ASSERT(pConfig != NULL);
26157 MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */
26158 MA_ASSERT(pDevice != NULL);
26159
26160 formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format);
26161
26162 openMode = 0;
26163 if (pConfig->alsa.noAutoResample) {
26164 openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE;
26165 }
26166 if (pConfig->alsa.noAutoChannels) {
26167 openMode |= MA_SND_PCM_NO_AUTO_CHANNELS;
26168 }
26169 if (pConfig->alsa.noAutoFormat) {
26170 openMode |= MA_SND_PCM_NO_AUTO_FORMAT;
26171 }
26172
26173 result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM);
26174 if (result != MA_SUCCESS) {
26175 return result;
26176 }
26177
26178
26179 /* Hardware parameters. */
26180 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);
26181 if (pHWParams == NULL) {
26182 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26183 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters.");
26184 return MA_OUT_OF_MEMORY;
26185 }
26186
26187 resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
26188 if (resultALSA < 0) {
26189 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
26190 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26191 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.");
26192 return ma_result_from_errno(-resultALSA);
26193 }
26194
26195 /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */
26196 isUsingMMap = MA_FALSE;
26197#if 0 /* NOTE: MMAP mode temporarily disabled. */
26198 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. */
26199 if (!pConfig->alsa.noMMap && ma_device__is_async(pDevice)) {
26200 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) {
26201 pDevice->alsa.isUsingMMap = MA_TRUE;
26202 }
26203 }
26204 }
26205#endif
26206
26207 if (!isUsingMMap) {
26208 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);
26209 if (resultALSA < 0) {
26210 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
26211 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26212 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.");
26213 return ma_result_from_errno(-resultALSA);
26214 }
26215 }
26216
26217 /*
26218 Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't
26219 find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS.
26220 */
26221
26222 /* Format. */
26223 {
26224 /*
26225 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
26226 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.
26227 */
26228 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) {
26229 /* We're either requesting the native format or the specified format is not supported. */
26230 size_t iFormat;
26231
26232 formatALSA = MA_SND_PCM_FORMAT_UNKNOWN;
26233 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) {
26234 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) {
26235 formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]);
26236 break;
26237 }
26238 }
26239
26240 if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) {
26241 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
26242 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26243 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats.");
26245 }
26246 }
26247
26248 resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA);
26249 if (resultALSA < 0) {
26250 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
26251 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26252 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.");
26253 return ma_result_from_errno(-resultALSA);
26254 }
26255
26256 internalFormat = ma_format_from_alsa(formatALSA);
26257 if (internalFormat == ma_format_unknown) {
26258 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
26259 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26260 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio.");
26262 }
26263 }
26264
26265 /* Channels. */
26266 {
26267 unsigned int channels = pDescriptor->channels;
26268 if (channels == 0) {
26269 channels = MA_DEFAULT_CHANNELS;
26270 }
26271
26272 resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels);
26273 if (resultALSA < 0) {
26274 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
26275 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26276 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.");
26277 return ma_result_from_errno(-resultALSA);
26278 }
26279
26280 internalChannels = (ma_uint32)channels;
26281 }
26282
26283 /* Sample Rate */
26284 {
26285 unsigned int sampleRate;
26286
26287 /*
26288 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
26289 problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable
26290 resampling.
26291
26292 To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a
26293 sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling
26294 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
26295 faster rate.
26296
26297 miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine
26298 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
26299 good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion.
26300
26301 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
26302 this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins.
26303 */
26304 ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0);
26305
26306 sampleRate = pDescriptor->sampleRate;
26307 if (sampleRate == 0) {
26308 sampleRate = MA_DEFAULT_SAMPLE_RATE;
26309 }
26310
26311 resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0);
26312 if (resultALSA < 0) {
26313 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
26314 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26315 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.");
26316 return ma_result_from_errno(-resultALSA);
26317 }
26318
26319 internalSampleRate = (ma_uint32)sampleRate;
26320 }
26321
26322 /* Periods. */
26323 {
26324 ma_uint32 periods = pDescriptor->periodCount;
26325
26326 resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL);
26327 if (resultALSA < 0) {
26328 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
26329 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26330 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.");
26331 return ma_result_from_errno(-resultALSA);
26332 }
26333
26334 internalPeriods = periods;
26335 }
26336
26337 /* Buffer Size */
26338 {
26339 ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods;
26340
26341 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);
26342 if (resultALSA < 0) {
26343 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
26344 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26345 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.");
26346 return ma_result_from_errno(-resultALSA);
26347 }
26348
26349 internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods;
26350 }
26351
26352 /* Apply hardware parameters. */
26353 resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams);
26354 if (resultALSA < 0) {
26355 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
26356 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26357 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.");
26358 return ma_result_from_errno(-resultALSA);
26359 }
26360
26361 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
26362 pHWParams = NULL;
26363
26364
26365 /* Software parameters. */
26366 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);
26367 if (pSWParams == NULL) {
26368 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26369 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters.");
26370 return MA_OUT_OF_MEMORY;
26371 }
26372
26373 resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams);
26374 if (resultALSA < 0) {
26375 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
26376 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26377 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed.");
26378 return ma_result_from_errno(-resultALSA);
26379 }
26380
26381 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));
26382 if (resultALSA < 0) {
26383 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
26384 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26385 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.");
26386 return ma_result_from_errno(-resultALSA);
26387 }
26388
26389 resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary);
26390 if (resultALSA < 0) {
26391 bufferBoundary = internalPeriodSizeInFrames * internalPeriods;
26392 }
26393
26394 if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */
26395 /*
26396 Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to
26397 the size of a period. But for full-duplex we need to set it such that it is at least two periods.
26398 */
26399 resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2);
26400 if (resultALSA < 0) {
26401 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
26402 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26403 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.");
26404 return ma_result_from_errno(-resultALSA);
26405 }
26406
26407 resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary);
26408 if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */
26409 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
26410 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26411 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.");
26412 return ma_result_from_errno(-resultALSA);
26413 }
26414 }
26415
26416 resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams);
26417 if (resultALSA < 0) {
26418 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
26419 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26420 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.");
26421 return ma_result_from_errno(-resultALSA);
26422 }
26423
26424 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
26425 pSWParams = NULL;
26426
26427
26428 /* 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. */
26429 {
26430 ma_snd_pcm_chmap_t* pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM);
26431 if (pChmap != NULL) {
26432 ma_uint32 iChannel;
26433
26434 /* 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(). */
26435 if (pChmap->channels >= internalChannels) {
26436 /* Drop excess channels. */
26437 for (iChannel = 0; iChannel < internalChannels; ++iChannel) {
26438 internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
26439 }
26440 } else {
26441 ma_uint32 i;
26442
26443 /*
26444 Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate
26445 channels. If validation fails, fall back to defaults.
26446 */
26447 ma_bool32 isValid = MA_TRUE;
26448
26449 /* Fill with defaults. */
26450 ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
26451
26452 /* Overwrite first pChmap->channels channels. */
26453 for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) {
26454 internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
26455 }
26456
26457 /* Validate. */
26458 for (i = 0; i < internalChannels && isValid; ++i) {
26459 ma_uint32 j;
26460 for (j = i+1; j < internalChannels; ++j) {
26461 if (internalChannelMap[i] == internalChannelMap[j]) {
26462 isValid = MA_FALSE;
26463 break;
26464 }
26465 }
26466 }
26467
26468 /* If our channel map is invalid, fall back to defaults. */
26469 if (!isValid) {
26470 ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
26471 }
26472 }
26473
26474 free(pChmap);
26475 pChmap = NULL;
26476 } else {
26477 /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */
26478 ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
26479 }
26480 }
26481
26482
26483 /*
26484 We need to retrieve the poll descriptors so we can use poll() to wait for data to become
26485 available for reading or writing. There's no well defined maximum for this so we're just going
26486 to allocate this on the heap.
26487 */
26488 pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM);
26489 if (pollDescriptorCount <= 0) {
26490 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26491 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count.");
26492 return MA_ERROR;
26493 }
26494
26495 pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks); /* +1 because we want room for the wakeup descriptor. */
26496 if (pPollDescriptors == NULL) {
26497 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26498 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors.");
26499 return MA_OUT_OF_MEMORY;
26500 }
26501
26502 /*
26503 We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver
26504 never returns from writei() and readi(). This has been observed with the "pulse" device.
26505 */
26506 wakeupfd = eventfd(0, 0);
26507 if (wakeupfd < 0) {
26508 ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
26509 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26510 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup.");
26511 return ma_result_from_errno(errno);
26512 }
26513
26514 /* We'll place the wakeup fd at the start of the buffer. */
26515 pPollDescriptors[0].fd = wakeupfd;
26516 pPollDescriptors[0].events = POLLIN; /* We only care about waiting to read from the wakeup file descriptor. */
26517 pPollDescriptors[0].revents = 0;
26518
26519 /* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */
26520 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. */
26521 if (pollDescriptorCount <= 0) {
26522 close(wakeupfd);
26523 ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
26524 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26525 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors.");
26526 return MA_ERROR;
26527 }
26528
26529 if (deviceType == ma_device_type_capture) {
26530 pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount;
26531 pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors;
26532 pDevice->alsa.wakeupfdCapture = wakeupfd;
26533 } else {
26534 pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount;
26535 pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors;
26536 pDevice->alsa.wakeupfdPlayback = wakeupfd;
26537 }
26538
26539
26540 /* We're done. Prepare the device. */
26541 resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM);
26542 if (resultALSA < 0) {
26543 close(wakeupfd);
26544 ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
26545 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
26546 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.");
26547 return ma_result_from_errno(-resultALSA);
26548 }
26549
26550
26551 if (deviceType == ma_device_type_capture) {
26552 pDevice->alsa.pPCMCapture = (ma_ptr)pPCM;
26553 pDevice->alsa.isUsingMMapCapture = isUsingMMap;
26554 } else {
26555 pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM;
26556 pDevice->alsa.isUsingMMapPlayback = isUsingMMap;
26557 }
26558
26559 pDescriptor->format = internalFormat;
26560 pDescriptor->channels = internalChannels;
26561 pDescriptor->sampleRate = internalSampleRate;
26562 ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS));
26563 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
26564 pDescriptor->periodCount = internalPeriods;
26565
26566 return MA_SUCCESS;
26567}
26568
26569static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
26570{
26571 MA_ASSERT(pDevice != NULL);
26572
26573 MA_ZERO_OBJECT(&pDevice->alsa);
26574
26575 if (pConfig->deviceType == ma_device_type_loopback) {
26577 }
26578
26579 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
26580 ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
26581 if (result != MA_SUCCESS) {
26582 return result;
26583 }
26584 }
26585
26586 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
26587 ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
26588 if (result != MA_SUCCESS) {
26589 return result;
26590 }
26591 }
26592
26593 return MA_SUCCESS;
26594}
26595
26596static ma_result ma_device_start__alsa(ma_device* pDevice)
26597{
26598 int resultALSA;
26599
26600 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26601 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
26602 if (resultALSA < 0) {
26603 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device.");
26604 return ma_result_from_errno(-resultALSA);
26605 }
26606 }
26607
26608 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26609 /* Don't need to do anything for playback because it'll be started automatically when enough data has been written. */
26610 }
26611
26612 return MA_SUCCESS;
26613}
26614
26615static ma_result ma_device_stop__alsa(ma_device* pDevice)
26616{
26617 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26618 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n");
26619 ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
26620 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device successful.\n");
26621
26622 /* We need to prepare the device again, otherwise we won't be able to restart the device. */
26623 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device...\n");
26624 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
26625 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device failed.\n");
26626 } else {
26627 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n");
26628 }
26629 }
26630
26631 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26632 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device...\n");
26633 ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
26634 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device successful.\n");
26635
26636 /* We need to prepare the device again, otherwise we won't be able to restart the device. */
26637 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device...\n");
26638 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {
26639 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device failed.\n");
26640 } else {
26641 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n");
26642 }
26643 }
26644
26645 return MA_SUCCESS;
26646}
26647
26648static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent)
26649{
26650 for (;;) {
26651 unsigned short revents;
26652 int resultALSA;
26653 int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1);
26654 if (resultPoll < 0) {
26655 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed.");
26656 return ma_result_from_errno(errno);
26657 }
26658
26659 /*
26660 Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor
26661 has had it's POLLIN flag set. If so, we need to actually read the data and then exit
26662 function. The wakeup descriptor will be the first item in the descriptors buffer.
26663 */
26664 if ((pPollDescriptors[0].revents & POLLIN) != 0) {
26665 ma_uint64 t;
26666 int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */
26667 if (resultRead < 0) {
26668 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed.");
26669 return ma_result_from_errno(errno);
26670 }
26671
26672 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] POLLIN set for wakeupfd\n");
26673 return MA_DEVICE_NOT_STARTED;
26674 }
26675
26676 /*
26677 Getting here means that some data should be able to be read. We need to use ALSA to
26678 translate the revents flags for us.
26679 */
26680 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. */
26681 if (resultALSA < 0) {
26682 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.");
26683 return ma_result_from_errno(-resultALSA);
26684 }
26685
26686 if ((revents & POLLERR) != 0) {
26687 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] POLLERR detected.");
26688 return ma_result_from_errno(errno);
26689 }
26690
26691 if ((revents & requiredEvent) == requiredEvent) {
26692 break; /* We're done. Data available for reading or writing. */
26693 }
26694 }
26695
26696 return MA_SUCCESS;
26697}
26698
26699static ma_result ma_device_wait_read__alsa(ma_device* pDevice)
26700{
26701 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. */
26702}
26703
26704static ma_result ma_device_wait_write__alsa(ma_device* pDevice)
26705{
26706 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. */
26707}
26708
26709static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead)
26710{
26711 ma_snd_pcm_sframes_t resultALSA = 0;
26712
26713 MA_ASSERT(pDevice != NULL);
26714 MA_ASSERT(pFramesOut != NULL);
26715
26716 if (pFramesRead != NULL) {
26717 *pFramesRead = 0;
26718 }
26719
26720 while (ma_device_get_state(pDevice) == ma_device_state_started) {
26721 ma_result result;
26722
26723 /* 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. */
26724 result = ma_device_wait_read__alsa(pDevice);
26725 if (result != MA_SUCCESS) {
26726 return result;
26727 }
26728
26729 /* Getting here means we should have data available. */
26730 resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
26731 if (resultALSA >= 0) {
26732 break; /* Success. */
26733 } else {
26734 if (resultALSA == -EAGAIN) {
26735 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (read)\n");*/
26736 continue; /* Try again. */
26737 } else if (resultALSA == -EPIPE) {
26738 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (read)\n");
26739
26740 /* Overrun. Recover and try again. If this fails we need to return an error. */
26741 resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE);
26742 if (resultALSA < 0) {
26743 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.");
26744 return ma_result_from_errno((int)-resultALSA);
26745 }
26746
26747 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
26748 if (resultALSA < 0) {
26749 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.");
26750 return ma_result_from_errno((int)-resultALSA);
26751 }
26752
26753 continue; /* Try reading again. */
26754 }
26755 }
26756 }
26757
26758 if (pFramesRead != NULL) {
26759 *pFramesRead = resultALSA;
26760 }
26761
26762 return MA_SUCCESS;
26763}
26764
26765static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
26766{
26767 ma_snd_pcm_sframes_t resultALSA = 0;
26768
26769 MA_ASSERT(pDevice != NULL);
26770 MA_ASSERT(pFrames != NULL);
26771
26772 if (pFramesWritten != NULL) {
26773 *pFramesWritten = 0;
26774 }
26775
26776 while (ma_device_get_state(pDevice) == ma_device_state_started) {
26777 ma_result result;
26778
26779 /* 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. */
26780 result = ma_device_wait_write__alsa(pDevice);
26781 if (result != MA_SUCCESS) {
26782 return result;
26783 }
26784
26785 resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
26786 if (resultALSA >= 0) {
26787 break; /* Success. */
26788 } else {
26789 if (resultALSA == -EAGAIN) {
26790 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (write)\n");*/
26791 continue; /* Try again. */
26792 } else if (resultALSA == -EPIPE) {
26793 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (write)\n");
26794
26795 /* Underrun. Recover and try again. If this fails we need to return an error. */
26796 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). */
26797 if (resultALSA < 0) {
26798 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.");
26799 return ma_result_from_errno((int)-resultALSA);
26800 }
26801
26802 /*
26803 In my testing I have had a situation where writei() does not automatically restart the device even though I've set it
26804 up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of
26805 frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure
26806 if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't
26807 quite right here.
26808 */
26809 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
26810 if (resultALSA < 0) {
26811 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.");
26812 return ma_result_from_errno((int)-resultALSA);
26813 }
26814
26815 continue; /* Try writing again. */
26816 }
26817 }
26818 }
26819
26820 if (pFramesWritten != NULL) {
26821 *pFramesWritten = resultALSA;
26822 }
26823
26824 return MA_SUCCESS;
26825}
26826
26827static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice)
26828{
26829 ma_uint64 t = 1;
26830 int resultWrite = 0;
26831
26832 MA_ASSERT(pDevice != NULL);
26833
26834 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up...\n");
26835
26836 /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */
26837 if (pDevice->alsa.pPollDescriptorsCapture != NULL) {
26838 resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t));
26839 }
26840 if (pDevice->alsa.pPollDescriptorsPlayback != NULL) {
26841 resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t));
26842 }
26843
26844 if (resultWrite < 0) {
26845 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] write() failed.\n");
26846 return ma_result_from_errno(errno);
26847 }
26848
26849 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up completed successfully.\n");
26850
26851 return MA_SUCCESS;
26852}
26853
26854static ma_result ma_context_uninit__alsa(ma_context* pContext)
26855{
26856 MA_ASSERT(pContext != NULL);
26857 MA_ASSERT(pContext->backend == ma_backend_alsa);
26858
26859 /* Clean up memory for memory leak checkers. */
26860 ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)();
26861
26862#ifndef MA_NO_RUNTIME_LINKING
26863 ma_dlclose(pContext, pContext->alsa.asoundSO);
26864#endif
26865
26866 ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock);
26867
26868 return MA_SUCCESS;
26869}
26870
26871static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
26872{
26873 ma_result result;
26874#ifndef MA_NO_RUNTIME_LINKING
26875 const char* libasoundNames[] = {
26876 "libasound.so.2",
26877 "libasound.so"
26878 };
26879 size_t i;
26880
26881 for (i = 0; i < ma_countof(libasoundNames); ++i) {
26882 pContext->alsa.asoundSO = ma_dlopen(pContext, libasoundNames[i]);
26883 if (pContext->alsa.asoundSO != NULL) {
26884 break;
26885 }
26886 }
26887
26888 if (pContext->alsa.asoundSO == NULL) {
26889 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n");
26890 return MA_NO_BACKEND;
26891 }
26892
26893 pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_open");
26894 pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_close");
26895 pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof");
26896 pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_any");
26897 pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format");
26898 pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first");
26899 pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask");
26900 pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels");
26901 pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near");
26902 pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax");
26903 pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample");
26904 pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate");
26905 pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near");
26906 pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near");
26907 pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near");
26908 pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access");
26909 pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format");
26910 pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels");
26911 pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min");
26912 pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max");
26913 pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate");
26914 pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min");
26915 pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max");
26916 pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size");
26917 pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods");
26918 pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access");
26919 pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format");
26920 pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels");
26921 pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate");
26922 pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params");
26923 pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof");
26924 pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_current");
26925 pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary");
26926 pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min");
26927 pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold");
26928 pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold");
26929 pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params");
26930 pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof");
26931 pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_test");
26932 pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_get_chmap");
26933 pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_state");
26934 pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_prepare");
26935 pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start");
26936 pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop");
26937 pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain");
26938 pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_reset");
26939 pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint");
26940 pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint");
26941 pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index");
26942 pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_free_hint");
26943 pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_begin");
26944 pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_commit");
26945 pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_recover");
26946 pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_readi");
26947 pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_writei");
26948 pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail");
26949 pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update");
26950 pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait");
26951 pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_nonblock");
26952 pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info");
26953 pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof");
26954 pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name");
26955 pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors");
26956 pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count");
26957 pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents");
26958 pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global");
26959#else
26960 /* The system below is just for type safety. */
26961 ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open;
26962 ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close;
26963 ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof;
26964 ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any;
26965 ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format;
26966 ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first;
26967 ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask;
26968 ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels;
26969 ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near;
26970 ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample;
26971 ma_snd_pcm_hw_params_set_rate_near _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate;
26972 ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near;
26973 ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax;
26974 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;
26975 ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near;
26976 ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access;
26977 ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format;
26978 ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels;
26979 ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min;
26980 ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max;
26981 ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate;
26982 ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min;
26983 ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max;
26984 ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size;
26985 ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods;
26986 ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access;
26987 ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format;
26988 ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels;
26989 ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate;
26990 ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params;
26991 ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof;
26992 ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current;
26993 ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary;
26994 ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min;
26995 ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold;
26996 ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold;
26997 ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params;
26998 ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof;
26999 ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test;
27000 ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap;
27001 ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state;
27002 ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare;
27003 ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start;
27004 ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop;
27005 ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain;
27006 ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset;
27007 ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint;
27008 ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint;
27009 ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index;
27010 ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint;
27011 ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin;
27012 ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit;
27013 ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover;
27014 ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi;
27015 ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei;
27016 ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail;
27017 ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update;
27018 ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait;
27019 ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock;
27020 ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info;
27021 ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof;
27022 ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name;
27023 ma_snd_pcm_poll_descriptors _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors;
27024 ma_snd_pcm_poll_descriptors_count _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count;
27025 ma_snd_pcm_poll_descriptors_revents _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents;
27026 ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global;
27027
27028 pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open;
27029 pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close;
27030 pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof;
27031 pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any;
27032 pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format;
27033 pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first;
27034 pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask;
27035 pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels;
27036 pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near;
27037 pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax;
27038 pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample;
27039 pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate;
27040 pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near;
27041 pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near;
27042 pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near;
27043 pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access;
27044 pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format;
27045 pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels;
27046 pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min;
27047 pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max;
27048 pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate;
27049 pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min;
27050 pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max;
27051 pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size;
27052 pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods;
27053 pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access;
27054 pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format;
27055 pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels;
27056 pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate;
27057 pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params;
27058 pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof;
27059 pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current;
27060 pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary;
27061 pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min;
27062 pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold;
27063 pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold;
27064 pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params;
27065 pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof;
27066 pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test;
27067 pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap;
27068 pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state;
27069 pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare;
27070 pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start;
27071 pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop;
27072 pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain;
27073 pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset;
27074 pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint;
27075 pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint;
27076 pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index;
27077 pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint;
27078 pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin;
27079 pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit;
27080 pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover;
27081 pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi;
27082 pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei;
27083 pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail;
27084 pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update;
27085 pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait;
27086 pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock;
27087 pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info;
27088 pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof;
27089 pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name;
27090 pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors;
27091 pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count;
27092 pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents;
27093 pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global;
27094#endif
27095
27096 pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration;
27097
27098 result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock);
27099 if (result != MA_SUCCESS) {
27100 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration.");
27101 return result;
27102 }
27103
27104 pCallbacks->onContextInit = ma_context_init__alsa;
27105 pCallbacks->onContextUninit = ma_context_uninit__alsa;
27106 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa;
27107 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa;
27108 pCallbacks->onDeviceInit = ma_device_init__alsa;
27109 pCallbacks->onDeviceUninit = ma_device_uninit__alsa;
27110 pCallbacks->onDeviceStart = ma_device_start__alsa;
27111 pCallbacks->onDeviceStop = ma_device_stop__alsa;
27112 pCallbacks->onDeviceRead = ma_device_read__alsa;
27113 pCallbacks->onDeviceWrite = ma_device_write__alsa;
27114 pCallbacks->onDeviceDataLoop = NULL;
27115 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa;
27116
27117 return MA_SUCCESS;
27118}
27119#endif /* ALSA */
27120
27121
27122
27123
27128#ifdef MA_HAS_PULSEAUDIO
27129/*
27130The PulseAudio API, along with Apple's Core Audio, is the worst of the maintream audio APIs. This is a brief description of what's going on
27131in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion.
27132
27133PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it
27134allows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it
27135appears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or
27136write functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the
27137simple API has some smarts to do this automatically, but I'm not sure. Another limitation with the simple API is that it seems inefficient
27138when you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API.
27139
27140Since 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
27141get fun, and I don't mean that in a good way...
27142
27143The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands
27144don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is
27145enabled through the use of a "main loop". In the asychronous API you cannot get away from the main loop, and the main loop is where almost
27146all of PulseAudio's problems stem from.
27147
27148When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own
27149vtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called
27150pa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop
27151because it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use
27152it to implement a worker thread which runs in a loop. The main loop is where operations are actually executed.
27153
27154To 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
27155to the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded
27156main loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely
27157specialized such as if you want to integrate it into your application's existing main loop infrastructure.
27158
27159(EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262.
27160It is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.)
27161
27162Once you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to
27163miniaudio'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
27164one `ma_context` to many `ma_device`s. Here's where it starts to get annoying, however. When you first create the PulseAudio context, which
27165is done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if
27166you remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()`
27167has 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
27168set 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.
27169All objects from here out are created from the context, and, I believe, you can't be creating these objects until the context is connected.
27170This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before
27171attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`.
27172
27173The reason for this asynchronous design is to support cases where you're connecting to a remote server, say through a local network or an
27174internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the
27175host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind.
27176
27177Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device.
27178The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call
27179`pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get
27180information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object
27181is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to
27182run 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
27183context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up.
27184All of that just to retrieve basic information about a device!
27185
27186Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the
27187context, 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
27188choices in PulseAudio.
27189
27190PulseAudio 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
27191because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for
27192writing 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
27193set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices
27194straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified,
27195PulseAudio will immediately fire it's write or read callback. This is *technically* correct (based on the wording in the documentation)
27196because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback
27197would 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
27198requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at
27199that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the
27200stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data
27201callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio
27202doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been
27203started. 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
27204callback is not fired.
27205
27206This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will
27207continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device
27208is running, and will be fired regardless of whether or not the user actually wrote anything to the device/stream. This not the case in
27209PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call
27210`pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always
27211writing 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
27212you're continuously writing data to the stream, the stream will never get drained! That means in order to drain the stream, you need to
27213*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
27214important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained
27215before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write
27216data 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!
27217
27218This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not*
27219write 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
27220resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This
27221disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the
27222callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.)
27223
27224Once you've created the stream, you need to connect it which involves the whole waiting procedure. This is the same process as the context,
27225only this time you'll poll for the state with `pa_stream_get_status()`. The starting and stopping of a streaming is referred to as
27226"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
27227it'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
27228guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is
27229absolutely beyond me. Would it really be that hard to just make it run synchronously?
27230
27231Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that
27232they were initialized in.
27233
27234That'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
27235embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to
27236run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche
27237requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is
27238constantly fired, regardless of whether or not `pa_stream_write()` has been called, and it needs to take a pointer to a buffer as a
27239parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These
27240changes alone will change PulseAudio from one of the worst audio APIs to one of the best.
27241*/
27242
27243
27244/*
27245It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header
27246to check for type safety. We cannot do this when linking at run time because the header might not be available.
27247*/
27248#ifdef MA_NO_RUNTIME_LINKING
27249
27250/* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */
27251#if !defined(__cplusplus)
27252 #if defined(__STRICT_ANSI__)
27253 #if !defined(inline)
27254 #define inline __inline__ __attribute__((always_inline))
27255 #define MA_INLINE_DEFINED
27256 #endif
27257 #endif
27258#endif
27259#include <pulse/pulseaudio.h>
27260#if defined(MA_INLINE_DEFINED)
27261 #undef inline
27262 #undef MA_INLINE_DEFINED
27263#endif
27264
27265#define MA_PA_OK PA_OK
27266#define MA_PA_ERR_ACCESS PA_ERR_ACCESS
27267#define MA_PA_ERR_INVALID PA_ERR_INVALID
27268#define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY
27269#define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED
27270
27271#define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX
27272#define MA_PA_RATE_MAX PA_RATE_MAX
27273
27274typedef pa_context_flags_t ma_pa_context_flags_t;
27275#define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS
27276#define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN
27277#define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL
27278
27279typedef pa_stream_flags_t ma_pa_stream_flags_t;
27280#define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS
27281#define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED
27282#define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING
27283#define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC
27284#define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE
27285#define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS
27286#define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS
27287#define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT
27288#define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE
27289#define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS
27290#define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE
27291#define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE
27292#define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT
27293#define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED
27294#define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY
27295#define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS
27296#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
27297#define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
27298#define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
27299#define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
27300#define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
27301
27302typedef pa_sink_flags_t ma_pa_sink_flags_t;
27303#define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS
27304#define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL
27305#define MA_PA_SINK_LATENCY PA_SINK_LATENCY
27306#define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE
27307#define MA_PA_SINK_NETWORK PA_SINK_NETWORK
27308#define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL
27309#define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
27310#define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
27311#define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY
27312#define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS
27313
27314typedef pa_source_flags_t ma_pa_source_flags_t;
27315#define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS
27316#define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL
27317#define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY
27318#define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE
27319#define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK
27320#define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL
27321#define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME
27322#define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY
27323#define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME
27324
27325typedef pa_context_state_t ma_pa_context_state_t;
27326#define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED
27327#define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING
27328#define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING
27329#define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME
27330#define MA_PA_CONTEXT_READY PA_CONTEXT_READY
27331#define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED
27332#define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED
27333
27334typedef pa_stream_state_t ma_pa_stream_state_t;
27335#define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED
27336#define MA_PA_STREAM_CREATING PA_STREAM_CREATING
27337#define MA_PA_STREAM_READY PA_STREAM_READY
27338#define MA_PA_STREAM_FAILED PA_STREAM_FAILED
27339#define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED
27340
27341typedef pa_operation_state_t ma_pa_operation_state_t;
27342#define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING
27343#define MA_PA_OPERATION_DONE PA_OPERATION_DONE
27344#define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED
27345
27346typedef pa_sink_state_t ma_pa_sink_state_t;
27347#define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE
27348#define MA_PA_SINK_RUNNING PA_SINK_RUNNING
27349#define MA_PA_SINK_IDLE PA_SINK_IDLE
27350#define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED
27351
27352typedef pa_source_state_t ma_pa_source_state_t;
27353#define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE
27354#define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING
27355#define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE
27356#define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED
27357
27358typedef pa_seek_mode_t ma_pa_seek_mode_t;
27359#define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE
27360#define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE
27361#define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ
27362#define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END
27363
27364typedef pa_channel_position_t ma_pa_channel_position_t;
27365#define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID
27366#define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO
27367#define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT
27368#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT
27369#define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER
27370#define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER
27371#define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT
27372#define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT
27373#define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE
27374#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
27375#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
27376#define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT
27377#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT
27378#define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0
27379#define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1
27380#define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2
27381#define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3
27382#define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4
27383#define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5
27384#define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6
27385#define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7
27386#define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8
27387#define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9
27388#define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10
27389#define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11
27390#define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12
27391#define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13
27392#define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14
27393#define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15
27394#define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16
27395#define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17
27396#define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18
27397#define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19
27398#define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20
27399#define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21
27400#define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22
27401#define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23
27402#define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24
27403#define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25
27404#define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26
27405#define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27
27406#define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28
27407#define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29
27408#define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30
27409#define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31
27410#define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER
27411#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT
27412#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
27413#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER
27414#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT
27415#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT
27416#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER
27417#define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT
27418#define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT
27419#define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER
27420#define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER
27421
27422typedef pa_channel_map_def_t ma_pa_channel_map_def_t;
27423#define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF
27424#define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA
27425#define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX
27426#define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX
27427#define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS
27428#define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT
27429
27430typedef pa_sample_format_t ma_pa_sample_format_t;
27431#define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID
27432#define MA_PA_SAMPLE_U8 PA_SAMPLE_U8
27433#define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW
27434#define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW
27435#define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE
27436#define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE
27437#define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE
27438#define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE
27439#define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE
27440#define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE
27441#define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE
27442#define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE
27443#define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE
27444#define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE
27445
27446typedef pa_mainloop ma_pa_mainloop;
27447typedef pa_threaded_mainloop ma_pa_threaded_mainloop;
27448typedef pa_mainloop_api ma_pa_mainloop_api;
27449typedef pa_context ma_pa_context;
27450typedef pa_operation ma_pa_operation;
27451typedef pa_stream ma_pa_stream;
27452typedef pa_spawn_api ma_pa_spawn_api;
27453typedef pa_buffer_attr ma_pa_buffer_attr;
27454typedef pa_channel_map ma_pa_channel_map;
27455typedef pa_cvolume ma_pa_cvolume;
27456typedef pa_sample_spec ma_pa_sample_spec;
27457typedef pa_sink_info ma_pa_sink_info;
27458typedef pa_source_info ma_pa_source_info;
27459
27460typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t;
27461typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t;
27462typedef pa_source_info_cb_t ma_pa_source_info_cb_t;
27463typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t;
27464typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t;
27465typedef pa_stream_notify_cb_t ma_pa_stream_notify_cb_t;
27466typedef pa_free_cb_t ma_pa_free_cb_t;
27467#else
27468#define MA_PA_OK 0
27469#define MA_PA_ERR_ACCESS 1
27470#define MA_PA_ERR_INVALID 2
27471#define MA_PA_ERR_NOENTITY 5
27472#define MA_PA_ERR_NOTSUPPORTED 19
27473
27474#define MA_PA_CHANNELS_MAX 32
27475#define MA_PA_RATE_MAX 384000
27476
27477typedef int ma_pa_context_flags_t;
27478#define MA_PA_CONTEXT_NOFLAGS 0x00000000
27479#define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001
27480#define MA_PA_CONTEXT_NOFAIL 0x00000002
27481
27482typedef int ma_pa_stream_flags_t;
27483#define MA_PA_STREAM_NOFLAGS 0x00000000
27484#define MA_PA_STREAM_START_CORKED 0x00000001
27485#define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002
27486#define MA_PA_STREAM_NOT_MONOTONIC 0x00000004
27487#define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008
27488#define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010
27489#define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020
27490#define MA_PA_STREAM_FIX_FORMAT 0x00000040
27491#define MA_PA_STREAM_FIX_RATE 0x00000080
27492#define MA_PA_STREAM_FIX_CHANNELS 0x00000100
27493#define MA_PA_STREAM_DONT_MOVE 0x00000200
27494#define MA_PA_STREAM_VARIABLE_RATE 0x00000400
27495#define MA_PA_STREAM_PEAK_DETECT 0x00000800
27496#define MA_PA_STREAM_START_MUTED 0x00001000
27497#define MA_PA_STREAM_ADJUST_LATENCY 0x00002000
27498#define MA_PA_STREAM_EARLY_REQUESTS 0x00004000
27499#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000
27500#define MA_PA_STREAM_START_UNMUTED 0x00010000
27501#define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000
27502#define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000
27503#define MA_PA_STREAM_PASSTHROUGH 0x00080000
27504
27505typedef int ma_pa_sink_flags_t;
27506#define MA_PA_SINK_NOFLAGS 0x00000000
27507#define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001
27508#define MA_PA_SINK_LATENCY 0x00000002
27509#define MA_PA_SINK_HARDWARE 0x00000004
27510#define MA_PA_SINK_NETWORK 0x00000008
27511#define MA_PA_SINK_HW_MUTE_CTRL 0x00000010
27512#define MA_PA_SINK_DECIBEL_VOLUME 0x00000020
27513#define MA_PA_SINK_FLAT_VOLUME 0x00000040
27514#define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080
27515#define MA_PA_SINK_SET_FORMATS 0x00000100
27516
27517typedef int ma_pa_source_flags_t;
27518#define MA_PA_SOURCE_NOFLAGS 0x00000000
27519#define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001
27520#define MA_PA_SOURCE_LATENCY 0x00000002
27521#define MA_PA_SOURCE_HARDWARE 0x00000004
27522#define MA_PA_SOURCE_NETWORK 0x00000008
27523#define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010
27524#define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020
27525#define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040
27526#define MA_PA_SOURCE_FLAT_VOLUME 0x00000080
27527
27528typedef int ma_pa_context_state_t;
27529#define MA_PA_CONTEXT_UNCONNECTED 0
27530#define MA_PA_CONTEXT_CONNECTING 1
27531#define MA_PA_CONTEXT_AUTHORIZING 2
27532#define MA_PA_CONTEXT_SETTING_NAME 3
27533#define MA_PA_CONTEXT_READY 4
27534#define MA_PA_CONTEXT_FAILED 5
27535#define MA_PA_CONTEXT_TERMINATED 6
27536
27537typedef int ma_pa_stream_state_t;
27538#define MA_PA_STREAM_UNCONNECTED 0
27539#define MA_PA_STREAM_CREATING 1
27540#define MA_PA_STREAM_READY 2
27541#define MA_PA_STREAM_FAILED 3
27542#define MA_PA_STREAM_TERMINATED 4
27543
27544typedef int ma_pa_operation_state_t;
27545#define MA_PA_OPERATION_RUNNING 0
27546#define MA_PA_OPERATION_DONE 1
27547#define MA_PA_OPERATION_CANCELLED 2
27548
27549typedef int ma_pa_sink_state_t;
27550#define MA_PA_SINK_INVALID_STATE -1
27551#define MA_PA_SINK_RUNNING 0
27552#define MA_PA_SINK_IDLE 1
27553#define MA_PA_SINK_SUSPENDED 2
27554
27555typedef int ma_pa_source_state_t;
27556#define MA_PA_SOURCE_INVALID_STATE -1
27557#define MA_PA_SOURCE_RUNNING 0
27558#define MA_PA_SOURCE_IDLE 1
27559#define MA_PA_SOURCE_SUSPENDED 2
27560
27561typedef int ma_pa_seek_mode_t;
27562#define MA_PA_SEEK_RELATIVE 0
27563#define MA_PA_SEEK_ABSOLUTE 1
27564#define MA_PA_SEEK_RELATIVE_ON_READ 2
27565#define MA_PA_SEEK_RELATIVE_END 3
27566
27567typedef int ma_pa_channel_position_t;
27568#define MA_PA_CHANNEL_POSITION_INVALID -1
27569#define MA_PA_CHANNEL_POSITION_MONO 0
27570#define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1
27571#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2
27572#define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3
27573#define MA_PA_CHANNEL_POSITION_REAR_CENTER 4
27574#define MA_PA_CHANNEL_POSITION_REAR_LEFT 5
27575#define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6
27576#define MA_PA_CHANNEL_POSITION_LFE 7
27577#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8
27578#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9
27579#define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10
27580#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11
27581#define MA_PA_CHANNEL_POSITION_AUX0 12
27582#define MA_PA_CHANNEL_POSITION_AUX1 13
27583#define MA_PA_CHANNEL_POSITION_AUX2 14
27584#define MA_PA_CHANNEL_POSITION_AUX3 15
27585#define MA_PA_CHANNEL_POSITION_AUX4 16
27586#define MA_PA_CHANNEL_POSITION_AUX5 17
27587#define MA_PA_CHANNEL_POSITION_AUX6 18
27588#define MA_PA_CHANNEL_POSITION_AUX7 19
27589#define MA_PA_CHANNEL_POSITION_AUX8 20
27590#define MA_PA_CHANNEL_POSITION_AUX9 21
27591#define MA_PA_CHANNEL_POSITION_AUX10 22
27592#define MA_PA_CHANNEL_POSITION_AUX11 23
27593#define MA_PA_CHANNEL_POSITION_AUX12 24
27594#define MA_PA_CHANNEL_POSITION_AUX13 25
27595#define MA_PA_CHANNEL_POSITION_AUX14 26
27596#define MA_PA_CHANNEL_POSITION_AUX15 27
27597#define MA_PA_CHANNEL_POSITION_AUX16 28
27598#define MA_PA_CHANNEL_POSITION_AUX17 29
27599#define MA_PA_CHANNEL_POSITION_AUX18 30
27600#define MA_PA_CHANNEL_POSITION_AUX19 31
27601#define MA_PA_CHANNEL_POSITION_AUX20 32
27602#define MA_PA_CHANNEL_POSITION_AUX21 33
27603#define MA_PA_CHANNEL_POSITION_AUX22 34
27604#define MA_PA_CHANNEL_POSITION_AUX23 35
27605#define MA_PA_CHANNEL_POSITION_AUX24 36
27606#define MA_PA_CHANNEL_POSITION_AUX25 37
27607#define MA_PA_CHANNEL_POSITION_AUX26 38
27608#define MA_PA_CHANNEL_POSITION_AUX27 39
27609#define MA_PA_CHANNEL_POSITION_AUX28 40
27610#define MA_PA_CHANNEL_POSITION_AUX29 41
27611#define MA_PA_CHANNEL_POSITION_AUX30 42
27612#define MA_PA_CHANNEL_POSITION_AUX31 43
27613#define MA_PA_CHANNEL_POSITION_TOP_CENTER 44
27614#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45
27615#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46
27616#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47
27617#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48
27618#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49
27619#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50
27620#define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT
27621#define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT
27622#define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER
27623#define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE
27624
27625typedef int ma_pa_channel_map_def_t;
27626#define MA_PA_CHANNEL_MAP_AIFF 0
27627#define MA_PA_CHANNEL_MAP_ALSA 1
27628#define MA_PA_CHANNEL_MAP_AUX 2
27629#define MA_PA_CHANNEL_MAP_WAVEEX 3
27630#define MA_PA_CHANNEL_MAP_OSS 4
27631#define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF
27632
27633typedef int ma_pa_sample_format_t;
27634#define MA_PA_SAMPLE_INVALID -1
27635#define MA_PA_SAMPLE_U8 0
27636#define MA_PA_SAMPLE_ALAW 1
27637#define MA_PA_SAMPLE_ULAW 2
27638#define MA_PA_SAMPLE_S16LE 3
27639#define MA_PA_SAMPLE_S16BE 4
27640#define MA_PA_SAMPLE_FLOAT32LE 5
27641#define MA_PA_SAMPLE_FLOAT32BE 6
27642#define MA_PA_SAMPLE_S32LE 7
27643#define MA_PA_SAMPLE_S32BE 8
27644#define MA_PA_SAMPLE_S24LE 9
27645#define MA_PA_SAMPLE_S24BE 10
27646#define MA_PA_SAMPLE_S24_32LE 11
27647#define MA_PA_SAMPLE_S24_32BE 12
27648
27649typedef struct ma_pa_mainloop ma_pa_mainloop;
27650typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop;
27651typedef struct ma_pa_mainloop_api ma_pa_mainloop_api;
27652typedef struct ma_pa_context ma_pa_context;
27653typedef struct ma_pa_operation ma_pa_operation;
27654typedef struct ma_pa_stream ma_pa_stream;
27655typedef struct ma_pa_spawn_api ma_pa_spawn_api;
27656
27657typedef struct
27658{
27659 ma_uint32 maxlength;
27660 ma_uint32 tlength;
27661 ma_uint32 prebuf;
27662 ma_uint32 minreq;
27663 ma_uint32 fragsize;
27664} ma_pa_buffer_attr;
27665
27666typedef struct
27667{
27668 ma_uint8 channels;
27669 ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX];
27670} ma_pa_channel_map;
27671
27672typedef struct
27673{
27674 ma_uint8 channels;
27675 ma_uint32 values[MA_PA_CHANNELS_MAX];
27676} ma_pa_cvolume;
27677
27678typedef struct
27679{
27680 ma_pa_sample_format_t format;
27681 ma_uint32 rate;
27682 ma_uint8 channels;
27683} ma_pa_sample_spec;
27684
27685typedef struct
27686{
27687 const char* name;
27688 ma_uint32 index;
27689 const char* description;
27690 ma_pa_sample_spec sample_spec;
27691 ma_pa_channel_map channel_map;
27692 ma_uint32 owner_module;
27693 ma_pa_cvolume volume;
27694 int mute;
27695 ma_uint32 monitor_source;
27696 const char* monitor_source_name;
27697 ma_uint64 latency;
27698 const char* driver;
27699 ma_pa_sink_flags_t flags;
27700 void* proplist;
27701 ma_uint64 configured_latency;
27702 ma_uint32 base_volume;
27703 ma_pa_sink_state_t state;
27704 ma_uint32 n_volume_steps;
27705 ma_uint32 card;
27706 ma_uint32 n_ports;
27707 void** ports;
27708 void* active_port;
27709 ma_uint8 n_formats;
27710 void** formats;
27711} ma_pa_sink_info;
27712
27713typedef struct
27714{
27715 const char *name;
27716 ma_uint32 index;
27717 const char *description;
27718 ma_pa_sample_spec sample_spec;
27719 ma_pa_channel_map channel_map;
27720 ma_uint32 owner_module;
27721 ma_pa_cvolume volume;
27722 int mute;
27723 ma_uint32 monitor_of_sink;
27724 const char *monitor_of_sink_name;
27725 ma_uint64 latency;
27726 const char *driver;
27727 ma_pa_source_flags_t flags;
27728 void* proplist;
27729 ma_uint64 configured_latency;
27730 ma_uint32 base_volume;
27731 ma_pa_source_state_t state;
27732 ma_uint32 n_volume_steps;
27733 ma_uint32 card;
27734 ma_uint32 n_ports;
27735 void** ports;
27736 void* active_port;
27737 ma_uint8 n_formats;
27738 void** formats;
27739} ma_pa_source_info;
27740
27741typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata);
27742typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata);
27743typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata);
27744typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata);
27745typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata);
27746typedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata);
27747typedef void (* ma_pa_free_cb_t) (void* p);
27748#endif
27749
27750
27751typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void);
27752typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m);
27753typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval);
27754typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m);
27755typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval);
27756typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m);
27757typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void);
27758typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m);
27759typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m);
27760typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m);
27761typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m);
27762typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m);
27763typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m);
27764typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept);
27765typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m);
27766typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m);
27767typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m);
27768typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m);
27769typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name);
27770typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name);
27771typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c);
27772typedef 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);
27773typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c);
27774typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata);
27775typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c);
27776typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata);
27777typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata);
27778typedef 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);
27779typedef 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);
27780typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o);
27781typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o);
27782typedef 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);
27783typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m);
27784typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss);
27785typedef 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);
27786typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s);
27787typedef 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);
27788typedef 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);
27789typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s);
27790typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s);
27791typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s);
27792typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s);
27793typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s);
27794typedef 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);
27795typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s);
27796typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
27797typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
27798typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);
27799typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);
27800typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s);
27801typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
27802typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
27803typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s);
27804typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata);
27805typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
27806typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes);
27807typedef 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);
27808typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes);
27809typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s);
27810typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s);
27811typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s);
27812
27813typedef struct
27814{
27815 ma_uint32 count;
27816 ma_uint32 capacity;
27817 ma_device_info* pInfo;
27818} ma_pulse_device_enum_data;
27819
27820static ma_result ma_result_from_pulse(int result)
27821{
27822 if (result < 0) {
27823 return MA_ERROR;
27824 }
27825
27826 switch (result) {
27827 case MA_PA_OK: return MA_SUCCESS;
27828 case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED;
27829 case MA_PA_ERR_INVALID: return MA_INVALID_ARGS;
27830 case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE;
27831 default: return MA_ERROR;
27832 }
27833}
27834
27835#if 0
27836static ma_pa_sample_format_t ma_format_to_pulse(ma_format format)
27837{
27838 if (ma_is_little_endian()) {
27839 switch (format) {
27840 case ma_format_s16: return MA_PA_SAMPLE_S16LE;
27841 case ma_format_s24: return MA_PA_SAMPLE_S24LE;
27842 case ma_format_s32: return MA_PA_SAMPLE_S32LE;
27843 case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE;
27844 default: break;
27845 }
27846 } else {
27847 switch (format) {
27848 case ma_format_s16: return MA_PA_SAMPLE_S16BE;
27849 case ma_format_s24: return MA_PA_SAMPLE_S24BE;
27850 case ma_format_s32: return MA_PA_SAMPLE_S32BE;
27851 case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE;
27852 default: break;
27853 }
27854 }
27855
27856 /* Endian agnostic. */
27857 switch (format) {
27858 case ma_format_u8: return MA_PA_SAMPLE_U8;
27859 default: return MA_PA_SAMPLE_INVALID;
27860 }
27861}
27862#endif
27863
27864static ma_format ma_format_from_pulse(ma_pa_sample_format_t format)
27865{
27866 if (ma_is_little_endian()) {
27867 switch (format) {
27868 case MA_PA_SAMPLE_S16LE: return ma_format_s16;
27869 case MA_PA_SAMPLE_S24LE: return ma_format_s24;
27870 case MA_PA_SAMPLE_S32LE: return ma_format_s32;
27871 case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32;
27872 default: break;
27873 }
27874 } else {
27875 switch (format) {
27876 case MA_PA_SAMPLE_S16BE: return ma_format_s16;
27877 case MA_PA_SAMPLE_S24BE: return ma_format_s24;
27878 case MA_PA_SAMPLE_S32BE: return ma_format_s32;
27879 case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32;
27880 default: break;
27881 }
27882 }
27883
27884 /* Endian agnostic. */
27885 switch (format) {
27886 case MA_PA_SAMPLE_U8: return ma_format_u8;
27887 default: return ma_format_unknown;
27888 }
27889}
27890
27891static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position)
27892{
27893 switch (position)
27894 {
27895 case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE;
27896 case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO;
27897 case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
27898 case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
27899 case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
27900 case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER;
27901 case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT;
27902 case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT;
27903 case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE;
27904 case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
27905 case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
27906 case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
27907 case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
27908 case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0;
27909 case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1;
27910 case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2;
27911 case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3;
27912 case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4;
27913 case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5;
27914 case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6;
27915 case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7;
27916 case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8;
27917 case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9;
27918 case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10;
27919 case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11;
27920 case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12;
27921 case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13;
27922 case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14;
27923 case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15;
27924 case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16;
27925 case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17;
27926 case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18;
27927 case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19;
27928 case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20;
27929 case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21;
27930 case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22;
27931 case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23;
27932 case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24;
27933 case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25;
27934 case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26;
27935 case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27;
27936 case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28;
27937 case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29;
27938 case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30;
27939 case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31;
27940 case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
27941 case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
27942 case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
27943 case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
27944 case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
27945 case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
27946 case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
27947 default: return MA_CHANNEL_NONE;
27948 }
27949}
27950
27951#if 0
27952static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position)
27953{
27954 switch (position)
27955 {
27956 case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID;
27957 case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT;
27958 case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT;
27959 case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER;
27960 case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE;
27961 case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT;
27962 case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT;
27963 case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
27964 case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
27965 case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER;
27966 case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT;
27967 case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT;
27968 case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER;
27969 case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
27970 case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
27971 case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
27972 case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT;
27973 case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER;
27974 case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
27975 case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18;
27976 case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19;
27977 case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20;
27978 case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21;
27979 case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22;
27980 case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23;
27981 case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24;
27982 case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25;
27983 case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26;
27984 case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27;
27985 case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28;
27986 case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29;
27987 case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30;
27988 case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31;
27989 default: return (ma_pa_channel_position_t)position;
27990 }
27991}
27992#endif
27993
27994static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP)
27995{
27996 int resultPA;
27997 ma_pa_operation_state_t state;
27998
27999 MA_ASSERT(pContext != NULL);
28000 MA_ASSERT(pOP != NULL);
28001
28002 for (;;) {
28003 state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP);
28004 if (state != MA_PA_OPERATION_RUNNING) {
28005 break; /* Done. */
28006 }
28007
28008 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
28009 if (resultPA < 0) {
28010 return ma_result_from_pulse(resultPA);
28011 }
28012 }
28013
28014 return MA_SUCCESS;
28015}
28016
28017static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP)
28018{
28019 ma_result result;
28020
28021 if (pOP == NULL) {
28022 return MA_INVALID_ARGS;
28023 }
28024
28025 result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
28026 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
28027
28028 return result;
28029}
28030
28031static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext)
28032{
28033 int resultPA;
28034 ma_pa_context_state_t state;
28035
28036 for (;;) {
28037 state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext);
28038 if (state == MA_PA_CONTEXT_READY) {
28039 break; /* Done. */
28040 }
28041
28042 if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
28043 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context.");
28044 return MA_ERROR;
28045 }
28046
28047 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
28048 if (resultPA < 0) {
28049 return ma_result_from_pulse(resultPA);
28050 }
28051 }
28052
28053 /* Should never get here. */
28054 return MA_SUCCESS;
28055}
28056
28057static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream)
28058{
28059 int resultPA;
28060 ma_pa_stream_state_t state;
28061
28062 for (;;) {
28063 state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream);
28064 if (state == MA_PA_STREAM_READY) {
28065 break; /* Done. */
28066 }
28067
28068 if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) {
28069 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream.");
28070 return MA_ERROR;
28071 }
28072
28073 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
28074 if (resultPA < 0) {
28075 return ma_result_from_pulse(resultPA);
28076 }
28077 }
28078
28079 return MA_SUCCESS;
28080}
28081
28082
28083static 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)
28084{
28085 ma_result result;
28086 ma_ptr pMainLoop;
28087 ma_ptr pPulseContext;
28088
28089 MA_ASSERT(ppMainLoop != NULL);
28090 MA_ASSERT(ppPulseContext != NULL);
28091
28092 /* 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. */
28093 pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
28094 if (pMainLoop == NULL) {
28095 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop.");
28097 }
28098
28099 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);
28100 if (pPulseContext == NULL) {
28101 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context.");
28102 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));
28104 }
28105
28106 /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */
28107 result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL));
28108 if (result != MA_SUCCESS) {
28109 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context.");
28110 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));
28111 return result;
28112 }
28113
28114 /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */
28115 result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext);
28116 if (result != MA_SUCCESS) {
28117 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Waiting for connection failed.");
28118 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));
28119 return result;
28120 }
28121
28122 *ppMainLoop = pMainLoop;
28123 *ppPulseContext = pPulseContext;
28124
28125 return MA_SUCCESS;
28126}
28127
28128
28129static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
28130{
28131 ma_pa_sink_info* pInfoOut;
28132
28133 if (endOfList > 0) {
28134 return;
28135 }
28136
28137 pInfoOut = (ma_pa_sink_info*)pUserData;
28138 MA_ASSERT(pInfoOut != NULL);
28139
28140 *pInfoOut = *pInfo;
28141
28142 (void)pPulseContext; /* Unused. */
28143}
28144
28145static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
28146{
28147 ma_pa_source_info* pInfoOut;
28148
28149 if (endOfList > 0) {
28150 return;
28151 }
28152
28153 pInfoOut = (ma_pa_source_info*)pUserData;
28154 MA_ASSERT(pInfoOut != NULL);
28155
28156 *pInfoOut = *pInfo;
28157
28158 (void)pPulseContext; /* Unused. */
28159}
28160
28161#if 0
28162static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
28163{
28164 ma_device* pDevice;
28165
28166 if (endOfList > 0) {
28167 return;
28168 }
28169
28170 pDevice = (ma_device*)pUserData;
28171 MA_ASSERT(pDevice != NULL);
28172
28173 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1);
28174
28175 (void)pPulseContext; /* Unused. */
28176}
28177
28178static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
28179{
28180 ma_device* pDevice;
28181
28182 if (endOfList > 0) {
28183 return;
28184 }
28185
28186 pDevice = (ma_device*)pUserData;
28187 MA_ASSERT(pDevice != NULL);
28188
28189 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1);
28190
28191 (void)pPulseContext; /* Unused. */
28192}
28193#endif
28194
28195static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo)
28196{
28197 ma_pa_operation* pOP;
28198
28199 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);
28200 if (pOP == NULL) {
28201 return MA_ERROR;
28202 }
28203
28204 return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);
28205}
28206
28207static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo)
28208{
28209 ma_pa_operation* pOP;
28210
28211 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);
28212 if (pOP == NULL) {
28213 return MA_ERROR;
28214 }
28215
28216 return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);
28217}
28218
28219static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex)
28220{
28221 ma_result result;
28222
28223 MA_ASSERT(pContext != NULL);
28224 MA_ASSERT(pIndex != NULL);
28225
28226 if (pIndex != NULL) {
28227 *pIndex = (ma_uint32)-1;
28228 }
28229
28230 if (deviceType == ma_device_type_playback) {
28231 ma_pa_sink_info sinkInfo;
28232 result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo);
28233 if (result != MA_SUCCESS) {
28234 return result;
28235 }
28236
28237 if (pIndex != NULL) {
28238 *pIndex = sinkInfo.index;
28239 }
28240 }
28241
28242 if (deviceType == ma_device_type_capture) {
28243 ma_pa_source_info sourceInfo;
28244 result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo);
28245 if (result != MA_SUCCESS) {
28246 return result;
28247 }
28248
28249 if (pIndex != NULL) {
28250 *pIndex = sourceInfo.index;
28251 }
28252 }
28253
28254 return MA_SUCCESS;
28255}
28256
28257
28258typedef struct
28259{
28260 ma_context* pContext;
28262 void* pUserData;
28263 ma_bool32 isTerminated;
28264 ma_uint32 defaultDeviceIndexPlayback;
28265 ma_uint32 defaultDeviceIndexCapture;
28266} ma_context_enumerate_devices_callback_data__pulse;
28267
28268static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData)
28269{
28270 ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
28271 ma_device_info deviceInfo;
28272
28273 MA_ASSERT(pData != NULL);
28274
28275 if (endOfList || pData->isTerminated) {
28276 return;
28277 }
28278
28279 MA_ZERO_OBJECT(&deviceInfo);
28280
28281 /* The name from PulseAudio is the ID for miniaudio. */
28282 if (pSinkInfo->name != NULL) {
28283 ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1);
28284 }
28285
28286 /* The description from PulseAudio is the name for miniaudio. */
28287 if (pSinkInfo->description != NULL) {
28288 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1);
28289 }
28290
28291 if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) {
28292 deviceInfo.isDefault = MA_TRUE;
28293 }
28294
28295 pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData);
28296
28297 (void)pPulseContext; /* Unused. */
28298}
28299
28300static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData)
28301{
28302 ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
28303 ma_device_info deviceInfo;
28304
28305 MA_ASSERT(pData != NULL);
28306
28307 if (endOfList || pData->isTerminated) {
28308 return;
28309 }
28310
28311 MA_ZERO_OBJECT(&deviceInfo);
28312
28313 /* The name from PulseAudio is the ID for miniaudio. */
28314 if (pSourceInfo->name != NULL) {
28315 ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1);
28316 }
28317
28318 /* The description from PulseAudio is the name for miniaudio. */
28319 if (pSourceInfo->description != NULL) {
28320 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1);
28321 }
28322
28323 if (pSourceInfo->index == pData->defaultDeviceIndexCapture) {
28324 deviceInfo.isDefault = MA_TRUE;
28325 }
28326
28327 pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData);
28328
28329 (void)pPulseContext; /* Unused. */
28330}
28331
28332static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
28333{
28334 ma_result result = MA_SUCCESS;
28335 ma_context_enumerate_devices_callback_data__pulse callbackData;
28336 ma_pa_operation* pOP = NULL;
28337
28338 MA_ASSERT(pContext != NULL);
28339 MA_ASSERT(callback != NULL);
28340
28341 callbackData.pContext = pContext;
28342 callbackData.callback = callback;
28343 callbackData.pUserData = pUserData;
28344 callbackData.isTerminated = MA_FALSE;
28345 callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1;
28346 callbackData.defaultDeviceIndexCapture = (ma_uint32)-1;
28347
28348 /* We need to get the index of the default devices. */
28349 ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback);
28350 ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture);
28351
28352 /* Playback. */
28353 if (!callbackData.isTerminated) {
28354 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);
28355 if (pOP == NULL) {
28356 result = MA_ERROR;
28357 goto done;
28358 }
28359
28360 result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP);
28361 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
28362
28363 if (result != MA_SUCCESS) {
28364 goto done;
28365 }
28366 }
28367
28368
28369 /* Capture. */
28370 if (!callbackData.isTerminated) {
28371 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);
28372 if (pOP == NULL) {
28373 result = MA_ERROR;
28374 goto done;
28375 }
28376
28377 result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP);
28378 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
28379
28380 if (result != MA_SUCCESS) {
28381 goto done;
28382 }
28383 }
28384
28385done:
28386 return result;
28387}
28388
28389
28390typedef struct
28391{
28392 ma_device_info* pDeviceInfo;
28393 ma_uint32 defaultDeviceIndex;
28394 ma_bool32 foundDevice;
28395} ma_context_get_device_info_callback_data__pulse;
28396
28397static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
28398{
28399 ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
28400
28401 if (endOfList > 0) {
28402 return;
28403 }
28404
28405 MA_ASSERT(pData != NULL);
28406 pData->foundDevice = MA_TRUE;
28407
28408 if (pInfo->name != NULL) {
28409 ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
28410 }
28411
28412 if (pInfo->description != NULL) {
28413 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
28414 }
28415
28416 /*
28417 We're just reporting a single data format here. I think technically PulseAudio might support
28418 all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to
28419 report the "native" device format.
28420 */
28421 pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format);
28422 pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels;
28423 pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate;
28424 pData->pDeviceInfo->nativeDataFormats[0].flags = 0;
28425 pData->pDeviceInfo->nativeDataFormatCount = 1;
28426
28427 if (pData->defaultDeviceIndex == pInfo->index) {
28428 pData->pDeviceInfo->isDefault = MA_TRUE;
28429 }
28430
28431 (void)pPulseContext; /* Unused. */
28432}
28433
28434static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
28435{
28436 ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
28437
28438 if (endOfList > 0) {
28439 return;
28440 }
28441
28442 MA_ASSERT(pData != NULL);
28443 pData->foundDevice = MA_TRUE;
28444
28445 if (pInfo->name != NULL) {
28446 ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
28447 }
28448
28449 if (pInfo->description != NULL) {
28450 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
28451 }
28452
28453 /*
28454 We're just reporting a single data format here. I think technically PulseAudio might support
28455 all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to
28456 report the "native" device format.
28457 */
28458 pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format);
28459 pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels;
28460 pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate;
28461 pData->pDeviceInfo->nativeDataFormats[0].flags = 0;
28462 pData->pDeviceInfo->nativeDataFormatCount = 1;
28463
28464 if (pData->defaultDeviceIndex == pInfo->index) {
28465 pData->pDeviceInfo->isDefault = MA_TRUE;
28466 }
28467
28468 (void)pPulseContext; /* Unused. */
28469}
28470
28471static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
28472{
28473 ma_result result = MA_SUCCESS;
28474 ma_context_get_device_info_callback_data__pulse callbackData;
28475 ma_pa_operation* pOP = NULL;
28476 const char* pDeviceName = NULL;
28477
28478 MA_ASSERT(pContext != NULL);
28479
28480 callbackData.pDeviceInfo = pDeviceInfo;
28481 callbackData.foundDevice = MA_FALSE;
28482
28483 if (pDeviceID != NULL) {
28484 pDeviceName = pDeviceID->pulse;
28485 } else {
28486 pDeviceName = NULL;
28487 }
28488
28489 result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex);
28490
28491 if (deviceType == ma_device_type_playback) {
28492 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);
28493 } else {
28494 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);
28495 }
28496
28497 if (pOP != NULL) {
28498 ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);
28499 } else {
28500 result = MA_ERROR;
28501 goto done;
28502 }
28503
28504 if (!callbackData.foundDevice) {
28505 result = MA_NO_DEVICE;
28506 goto done;
28507 }
28508
28509done:
28510 return result;
28511}
28512
28513static ma_result ma_device_uninit__pulse(ma_device* pDevice)
28514{
28515 ma_context* pContext;
28516
28517 MA_ASSERT(pDevice != NULL);
28518
28519 pContext = pDevice->pContext;
28520 MA_ASSERT(pContext != NULL);
28521
28522 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28523 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
28524 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
28525 }
28526
28527 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
28528 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
28529 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
28530 }
28531
28532 if (pDevice->type == ma_device_type_duplex) {
28533 ma_duplex_rb_uninit(&pDevice->duplexRB);
28534 }
28535
28536 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);
28537 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);
28538 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
28539
28540 return MA_SUCCESS;
28541}
28542
28543static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss)
28544{
28545 ma_pa_buffer_attr attr;
28546 attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels);
28547 attr.tlength = attr.maxlength / periods;
28548 attr.prebuf = (ma_uint32)-1;
28549 attr.minreq = (ma_uint32)-1;
28550 attr.fragsize = attr.maxlength / periods;
28551
28552 return attr;
28553}
28554
28555static 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)
28556{
28557 static int g_StreamCounter = 0;
28558 char actualStreamName[256];
28559
28560 if (pStreamName != NULL) {
28561 ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1);
28562 } else {
28563 ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:");
28564 ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */
28565 }
28566 g_StreamCounter += 1;
28567
28568 return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap);
28569}
28570
28571
28572static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData)
28573{
28574 ma_device* pDevice = (ma_device*)pUserData;
28575 ma_uint32 bpf;
28576 ma_uint32 deviceState;
28577 ma_uint64 frameCount;
28578 ma_uint64 framesProcessed;
28579
28580 MA_ASSERT(pDevice != NULL);
28581
28582 /*
28583 Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio
28584 can fire this callback before the stream has even started. Ridiculous.
28585 */
28586 deviceState = ma_device_get_state(pDevice);
28587 if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {
28588 return;
28589 }
28590
28591 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
28592 MA_ASSERT(bpf > 0);
28593
28594 frameCount = byteCount / bpf;
28595 framesProcessed = 0;
28596
28597 while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) {
28598 const void* pMappedPCMFrames;
28599 size_t bytesMapped;
28600 ma_uint64 framesMapped;
28601
28602 int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped);
28603 if (pulseResult < 0) {
28604 break; /* Failed to map. Abort. */
28605 }
28606
28607 framesMapped = bytesMapped / bpf;
28608 if (framesMapped > 0) {
28609 if (pMappedPCMFrames != NULL) {
28610 ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped);
28611 } else {
28612 /* It's a hole. */
28613 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulse: Hole.\n");
28614 }
28615
28616 pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream);
28617 if (pulseResult < 0) {
28618 break; /* Failed to drop the buffer. */
28619 }
28620
28621 framesProcessed += framesMapped;
28622
28623 } else {
28624 /* Nothing was mapped. Just abort. */
28625 break;
28626 }
28627 }
28628}
28629
28630static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed)
28631{
28632 ma_result result = MA_SUCCESS;
28633 ma_uint64 framesProcessed = 0;
28634 size_t bytesMapped;
28635 ma_uint32 bpf;
28636 ma_uint32 deviceState;
28637
28638 MA_ASSERT(pDevice != NULL);
28639 MA_ASSERT(pStream != NULL);
28640
28641 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
28642 MA_ASSERT(bpf > 0);
28643
28644 deviceState = ma_device_get_state(pDevice);
28645
28646 bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream);
28647 if (bytesMapped != (size_t)-1) {
28648 if (bytesMapped > 0) {
28649 ma_uint64 framesMapped;
28650 void* pMappedPCMFrames;
28651 int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped);
28652 if (pulseResult < 0) {
28653 result = ma_result_from_pulse(pulseResult);
28654 goto done;
28655 }
28656
28657 framesMapped = bytesMapped / bpf;
28658
28659 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. */
28660 ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped);
28661 } else {
28662 /* Device is not started. Write silence. */
28663 ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels);
28664 }
28665
28666 pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE);
28667 if (pulseResult < 0) {
28668 result = ma_result_from_pulse(pulseResult);
28669 goto done; /* Failed to write data to stream. */
28670 }
28671
28672 framesProcessed += framesMapped;
28673 } else {
28674 result = MA_SUCCESS; /* No data available for writing. */
28675 goto done;
28676 }
28677 } else {
28678 result = MA_ERROR; /* Failed to retrieve the writable size. Abort. */
28679 goto done;
28680 }
28681
28682done:
28683 if (pFramesProcessed != NULL) {
28684 *pFramesProcessed = framesProcessed;
28685 }
28686
28687 return result;
28688}
28689
28690static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData)
28691{
28692 ma_device* pDevice = (ma_device*)pUserData;
28693 ma_uint32 bpf;
28694 ma_uint64 frameCount;
28695 ma_uint64 framesProcessed;
28696 ma_uint32 deviceState;
28697 ma_result result;
28698
28699 MA_ASSERT(pDevice != NULL);
28700
28701 /*
28702 Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio
28703 can fire this callback before the stream has even started. Ridiculous.
28704 */
28705 deviceState = ma_device_get_state(pDevice);
28706 if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {
28707 return;
28708 }
28709
28710 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
28711 MA_ASSERT(bpf > 0);
28712
28713 frameCount = byteCount / bpf;
28714 framesProcessed = 0;
28715
28716 while (framesProcessed < frameCount) {
28717 ma_uint64 framesProcessedThisIteration;
28718
28719 /* Don't keep trying to process frames if the device isn't started. */
28720 deviceState = ma_device_get_state(pDevice);
28721 if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {
28722 break;
28723 }
28724
28725 result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration);
28726 if (result != MA_SUCCESS) {
28727 break;
28728 }
28729
28730 framesProcessed += framesProcessedThisIteration;
28731 }
28732}
28733
28734static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData)
28735{
28736 ma_device* pDevice = (ma_device*)pUserData;
28737 int suspended;
28738
28739 (void)pStream;
28740
28741 suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream);
28742 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);
28743
28744 if (suspended < 0) {
28745 return;
28746 }
28747
28748 if (suspended == 1) {
28749 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n");
28750 ma_device__on_notification_stopped(pDevice);
28751 } else {
28752 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n");
28753 ma_device__on_notification_started(pDevice);
28754 }
28755}
28756
28757static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData)
28758{
28759 ma_device* pDevice = (ma_device*)pUserData;
28760
28761 (void)pStream;
28762 (void)pUserData;
28763
28764 ma_device__on_notification_rerouted(pDevice);
28765}
28766
28767static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
28768{
28769 /*
28770 There have been reports from users where buffers of < ~20ms result glitches when running through
28771 PipeWire. To work around this we're going to have to use a different default buffer size.
28772 */
28773 const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency = 25;
28774 const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE;
28775
28776 MA_ASSERT(nativeSampleRate != 0);
28777
28778 if (pDescriptor->periodSizeInFrames == 0) {
28779 if (pDescriptor->periodSizeInMilliseconds == 0) {
28780 if (performanceProfile == ma_performance_profile_low_latency) {
28781 return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate);
28782 } else {
28783 return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate);
28784 }
28785 } else {
28787 }
28788 } else {
28789 return pDescriptor->periodSizeInFrames;
28790 }
28791}
28792
28793static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
28794{
28795 /*
28796 Notes for PulseAudio:
28797
28798 - We're always using native format/channels/rate regardless of whether or not PulseAudio
28799 supports the format directly through their own data conversion system. I'm doing this to
28800 reduce as much variability from the PulseAudio side as possible because it's seems to be
28801 extremely unreliable at everything it does.
28802
28803 - When both the period size in frames and milliseconds are 0, we default to miniaudio's
28804 default buffer sizes rather than leaving it up to PulseAudio because I don't trust
28805 PulseAudio to give us any kind of reasonable latency by default.
28806
28807 - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this
28808 flag, capture mode will just not work properly until you open another PulseAudio app.
28809 */
28810
28811 ma_result result = MA_SUCCESS;
28812 int error = 0;
28813 const char* devPlayback = NULL;
28814 const char* devCapture = NULL;
28816 ma_uint32 channels = 0;
28817 ma_uint32 sampleRate = 0;
28818 ma_pa_sink_info sinkInfo;
28819 ma_pa_source_info sourceInfo;
28820 ma_pa_sample_spec ss;
28821 ma_pa_channel_map cmap;
28822 ma_pa_buffer_attr attr;
28823 const ma_pa_sample_spec* pActualSS = NULL;
28824 const ma_pa_channel_map* pActualCMap = NULL;
28825 const ma_pa_buffer_attr* pActualAttr = NULL;
28826 ma_uint32 iChannel;
28827 ma_pa_stream_flags_t streamFlags;
28828
28829 MA_ASSERT(pDevice != NULL);
28830 MA_ZERO_OBJECT(&pDevice->pulse);
28831
28832 if (pConfig->deviceType == ma_device_type_loopback) {
28834 }
28835
28836 /* No exclusive mode with the PulseAudio backend. */
28840 }
28841
28842 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
28843 if (pDescriptorPlayback->pDeviceID != NULL) {
28844 devPlayback = pDescriptorPlayback->pDeviceID->pulse;
28845 }
28846
28847 format = pDescriptorPlayback->format;
28848 channels = pDescriptorPlayback->channels;
28849 sampleRate = pDescriptorPlayback->sampleRate;
28850 }
28851
28852 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
28853 if (pDescriptorCapture->pDeviceID != NULL) {
28854 devCapture = pDescriptorCapture->pDeviceID->pulse;
28855 }
28856
28857 format = pDescriptorCapture->format;
28858 channels = pDescriptorCapture->channels;
28859 sampleRate = pDescriptorCapture->sampleRate;
28860 }
28861
28862
28863
28864 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);
28865 if (result != MA_SUCCESS) {
28866 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize PA mainloop and context for device.\n");
28867 return result;
28868 }
28869
28870 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
28871 result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo);
28872 if (result != MA_SUCCESS) {
28873 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device.");
28874 goto on_error0;
28875 }
28876
28877 ss = sourceInfo.sample_spec;
28878 cmap = sourceInfo.channel_map;
28879
28880 if (ma_format_from_pulse(ss.format) == ma_format_unknown) {
28881 if (ma_is_little_endian()) {
28882 ss.format = MA_PA_SAMPLE_FLOAT32LE;
28883 } else {
28884 ss.format = MA_PA_SAMPLE_FLOAT32BE;
28885 }
28886 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");
28887 }
28888 if (ss.rate == 0) {
28889 ss.rate = MA_DEFAULT_SAMPLE_RATE;
28890 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate);
28891 }
28892 if (ss.channels == 0) {
28893 ss.channels = MA_DEFAULT_CHANNELS;
28894 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels);
28895 }
28896
28897 /* We now have enough information to calculate our actual period size in frames. */
28898 pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile);
28899
28900 attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss);
28901 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);
28902
28903 pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap);
28904 if (pDevice->pulse.pStreamCapture == NULL) {
28905 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.\n");
28906 result = MA_ERROR;
28907 goto on_error0;
28908 }
28909
28910
28911 /* The callback needs to be set before connecting the stream. */
28912 ((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);
28913
28914 /* State callback for checking when the device has been corked. */
28915 ((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);
28916
28917 /* Rerouting notification. */
28918 ((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);
28919
28920
28921 /* Connect after we've got all of our internal state set up. */
28922 streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
28923 if (devCapture != NULL) {
28924 streamFlags |= MA_PA_STREAM_DONT_MOVE;
28925 }
28926
28927 error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags);
28928 if (error != MA_PA_OK) {
28929 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream.");
28930 result = ma_result_from_pulse(error);
28931 goto on_error1;
28932 }
28933
28934 result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture);
28935 if (result != MA_SUCCESS) {
28936 goto on_error2;
28937 }
28938
28939
28940 /* Internal format. */
28941 pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
28942 if (pActualSS != NULL) {
28943 ss = *pActualSS;
28944 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);
28945 } else {
28946 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve capture sample spec.\n");
28947 }
28948
28949 pDescriptorCapture->format = ma_format_from_pulse(ss.format);
28950 pDescriptorCapture->channels = ss.channels;
28951 pDescriptorCapture->sampleRate = ss.rate;
28952
28953 if (pDescriptorCapture->format == ma_format_unknown || pDescriptorCapture->channels == 0 || pDescriptorCapture->sampleRate == 0) {
28954 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);
28955 result = MA_ERROR;
28956 goto on_error4;
28957 }
28958
28959 /* Internal channel map. */
28960
28961 /*
28962 Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting
28963 the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono
28964 and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For
28965 all other channel counts we need to just put up with whatever PipeWire reports and hope it gets
28966 fixed sooner than later. I might remove this hack later.
28967 */
28968 if (pDescriptorCapture->channels > 2) {
28969 pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
28970 if (pActualCMap != NULL) {
28971 cmap = *pActualCMap;
28972 }
28973
28974 for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) {
28975 pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
28976 }
28977 } else {
28978 /* Hack for mono and stereo. */
28979 if (pDescriptorCapture->channels == 1) {
28980 pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO;
28981 } else if (pDescriptorCapture->channels == 2) {
28982 pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT;
28983 pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
28984 } else {
28985 MA_ASSERT(MA_FALSE); /* Should never hit this. */
28986 }
28987 }
28988
28989
28990 /* Buffer. */
28991 pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
28992 if (pActualAttr != NULL) {
28993 attr = *pActualAttr;
28994 }
28995
28996 if (attr.fragsize > 0) {
28997 pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1);
28998 } else {
28999 pDescriptorCapture->periodCount = 1;
29000 }
29001
29002 pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount;
29003 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);
29004 }
29005
29006 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
29007 result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo);
29008 if (result != MA_SUCCESS) {
29009 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.\n");
29010 goto on_error2;
29011 }
29012
29013 ss = sinkInfo.sample_spec;
29014 cmap = sinkInfo.channel_map;
29015
29016 if (ma_format_from_pulse(ss.format) == ma_format_unknown) {
29017 if (ma_is_little_endian()) {
29018 ss.format = MA_PA_SAMPLE_FLOAT32LE;
29019 } else {
29020 ss.format = MA_PA_SAMPLE_FLOAT32BE;
29021 }
29022 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");
29023 }
29024 if (ss.rate == 0) {
29025 ss.rate = MA_DEFAULT_SAMPLE_RATE;
29026 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate);
29027 }
29028 if (ss.channels == 0) {
29029 ss.channels = MA_DEFAULT_CHANNELS;
29030 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels);
29031 }
29032
29033 /* We now have enough information to calculate the actual buffer size in frames. */
29034 pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile);
29035
29036 attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss);
29037
29038 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);
29039
29040 pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap);
29041 if (pDevice->pulse.pStreamPlayback == NULL) {
29042 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.\n");
29043 result = MA_ERROR;
29044 goto on_error2;
29045 }
29046
29047
29048 /*
29049 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
29050 device state of ma_device_state_uninitialized.
29051 */
29052 ((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);
29053
29054 /* State callback for checking when the device has been corked. */
29055 ((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);
29056
29057 /* Rerouting notification. */
29058 ((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);
29059
29060
29061 /* Connect after we've got all of our internal state set up. */
29062 streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
29063 if (devPlayback != NULL) {
29064 streamFlags |= MA_PA_STREAM_DONT_MOVE;
29065 }
29066
29067 error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL);
29068 if (error != MA_PA_OK) {
29069 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream.");
29070 result = ma_result_from_pulse(error);
29071 goto on_error3;
29072 }
29073
29074 result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback);
29075 if (result != MA_SUCCESS) {
29076 goto on_error3;
29077 }
29078
29079
29080 /* Internal format. */
29081 pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
29082 if (pActualSS != NULL) {
29083 ss = *pActualSS;
29084 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);
29085 } else {
29086 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve playback sample spec.\n");
29087 }
29088
29089 pDescriptorPlayback->format = ma_format_from_pulse(ss.format);
29090 pDescriptorPlayback->channels = ss.channels;
29091 pDescriptorPlayback->sampleRate = ss.rate;
29092
29093 if (pDescriptorPlayback->format == ma_format_unknown || pDescriptorPlayback->channels == 0 || pDescriptorPlayback->sampleRate == 0) {
29094 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);
29095 result = MA_ERROR;
29096 goto on_error4;
29097 }
29098
29099 /* Internal channel map. */
29100
29101 /*
29102 Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting
29103 the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono
29104 and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For
29105 all other channel counts we need to just put up with whatever PipeWire reports and hope it gets
29106 fixed sooner than later. I might remove this hack later.
29107 */
29108 if (pDescriptorPlayback->channels > 2) {
29109 pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
29110 if (pActualCMap != NULL) {
29111 cmap = *pActualCMap;
29112 }
29113
29114 for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) {
29115 pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
29116 }
29117 } else {
29118 /* Hack for mono and stereo. */
29119 if (pDescriptorPlayback->channels == 1) {
29120 pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO;
29121 } else if (pDescriptorPlayback->channels == 2) {
29122 pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT;
29123 pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
29124 } else {
29125 MA_ASSERT(MA_FALSE); /* Should never hit this. */
29126 }
29127 }
29128
29129
29130 /* Buffer. */
29131 pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
29132 if (pActualAttr != NULL) {
29133 attr = *pActualAttr;
29134 }
29135
29136 if (attr.tlength > 0) {
29137 pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.tlength, 1);
29138 } else {
29139 pDescriptorPlayback->periodCount = 1;
29140 }
29141
29142 pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount;
29143 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);
29144 }
29145
29146
29147 /*
29148 We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main
29149 part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for
29150 us later on because that will only do it if it's a fully asynchronous backend - i.e. the
29151 onDeviceDataLoop callback is NULL, which is not the case for PulseAudio.
29152 */
29153 if (pConfig->deviceType == ma_device_type_duplex) {
29154 ma_format rbFormat = (format != ma_format_unknown) ? format : pDescriptorCapture->format;
29155 ma_uint32 rbChannels = (channels > 0) ? channels : pDescriptorCapture->channels;
29156 ma_uint32 rbSampleRate = (sampleRate > 0) ? sampleRate : pDescriptorCapture->sampleRate;
29157
29158 result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);
29159 if (result != MA_SUCCESS) {
29160 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer. %s.\n", ma_result_description(result));
29161 goto on_error4;
29162 }
29163 }
29164
29165 return MA_SUCCESS;
29166
29167
29168on_error4:
29169 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
29170 ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
29171 }
29172on_error3:
29173 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
29174 ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
29175 }
29176on_error2:
29177 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
29178 ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
29179 }
29180on_error1:
29181 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
29182 ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
29183 }
29184on_error0:
29185 return result;
29186}
29187
29188
29189static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData)
29190{
29191 ma_bool32* pIsSuccessful = (ma_bool32*)pUserData;
29192 MA_ASSERT(pIsSuccessful != NULL);
29193
29194 *pIsSuccessful = (ma_bool32)success;
29195
29196 (void)pStream; /* Unused. */
29197}
29198
29199static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork)
29200{
29201 ma_context* pContext = pDevice->pContext;
29202 ma_bool32 wasSuccessful;
29203 ma_pa_stream* pStream;
29204 ma_pa_operation* pOP;
29205 ma_result result;
29206
29207 /* This should not be called with a duplex device type. */
29208 if (deviceType == ma_device_type_duplex) {
29209 return MA_INVALID_ARGS;
29210 }
29211
29212 wasSuccessful = MA_FALSE;
29213
29214 pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback);
29215 MA_ASSERT(pStream != NULL);
29216
29217 pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful);
29218 if (pOP == NULL) {
29219 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream.");
29220 return MA_ERROR;
29221 }
29222
29223 result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP);
29224 if (result != MA_SUCCESS) {
29225 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork.");
29226 return result;
29227 }
29228
29229 if (!wasSuccessful) {
29230 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to %s PulseAudio stream.", (cork) ? "stop" : "start");
29231 return MA_ERROR;
29232 }
29233
29234 return MA_SUCCESS;
29235}
29236
29237static ma_result ma_device_start__pulse(ma_device* pDevice)
29238{
29239 ma_result result;
29240
29241 MA_ASSERT(pDevice != NULL);
29242
29243 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
29244 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0);
29245 if (result != MA_SUCCESS) {
29246 return result;
29247 }
29248 }
29249
29250 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
29251 /*
29252 We need to fill some data before uncorking. Not doing this will result in the write callback
29253 never getting fired. We're not going to abort if writing fails because I still want the device
29254 to get uncorked.
29255 */
29256 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.*/
29257
29258 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0);
29259 if (result != MA_SUCCESS) {
29260 return result;
29261 }
29262 }
29263
29264 return MA_SUCCESS;
29265}
29266
29267static ma_result ma_device_stop__pulse(ma_device* pDevice)
29268{
29269 ma_result result;
29270
29271 MA_ASSERT(pDevice != NULL);
29272
29273 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
29274 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1);
29275 if (result != MA_SUCCESS) {
29276 return result;
29277 }
29278 }
29279
29280 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
29281 /*
29282 Ideally we would drain the device here, but there's been cases where PulseAudio seems to be
29283 broken on some systems to the point where no audio processing seems to happen. When this
29284 happens, draining never completes and we get stuck here. For now I'm disabling draining of
29285 the device so we don't just freeze the application.
29286 */
29287 #if 0
29288 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);
29289 ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP);
29290 #endif
29291
29292 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1);
29293 if (result != MA_SUCCESS) {
29294 return result;
29295 }
29296 }
29297
29298 return MA_SUCCESS;
29299}
29300
29301static ma_result ma_device_data_loop__pulse(ma_device* pDevice)
29302{
29303 int resultPA;
29304
29305 MA_ASSERT(pDevice != NULL);
29306
29307 /* NOTE: Don't start the device here. It'll be done at a higher level. */
29308
29309 /*
29310 All data is handled through callbacks. All we need to do is iterate over the main loop and let
29311 the callbacks deal with it.
29312 */
29313 while (ma_device_get_state(pDevice) == ma_device_state_started) {
29314 resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
29315 if (resultPA < 0) {
29316 break;
29317 }
29318 }
29319
29320 /* NOTE: Don't stop the device here. It'll be done at a higher level. */
29321 return MA_SUCCESS;
29322}
29323
29324static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice)
29325{
29326 MA_ASSERT(pDevice != NULL);
29327
29328 ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
29329
29330 return MA_SUCCESS;
29331}
29332
29333static ma_result ma_context_uninit__pulse(ma_context* pContext)
29334{
29335 MA_ASSERT(pContext != NULL);
29336 MA_ASSERT(pContext->backend == ma_backend_pulseaudio);
29337
29338 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext);
29339 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext);
29340 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop);
29341
29342 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
29343 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
29344
29345#ifndef MA_NO_RUNTIME_LINKING
29346 ma_dlclose(pContext, pContext->pulse.pulseSO);
29347#endif
29348
29349 return MA_SUCCESS;
29350}
29351
29352static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
29353{
29354 ma_result result;
29355#ifndef MA_NO_RUNTIME_LINKING
29356 const char* libpulseNames[] = {
29357 "libpulse.so",
29358 "libpulse.so.0"
29359 };
29360 size_t i;
29361
29362 for (i = 0; i < ma_countof(libpulseNames); ++i) {
29363 pContext->pulse.pulseSO = ma_dlopen(pContext, libpulseNames[i]);
29364 if (pContext->pulse.pulseSO != NULL) {
29365 break;
29366 }
29367 }
29368
29369 if (pContext->pulse.pulseSO == NULL) {
29370 return MA_NO_BACKEND;
29371 }
29372
29373 pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_new");
29374 pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_free");
29375 pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_quit");
29376 pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_get_api");
29377 pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_iterate");
29378 pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_wakeup");
29379 pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_new");
29380 pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_free");
29381 pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_start");
29382 pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_stop");
29383 pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_lock");
29384 pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock");
29385 pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_wait");
29386 pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_signal");
29387 pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_accept");
29388 pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval");
29389 pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api");
29390 pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread");
29391 pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name");
29392 pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_new");
29393 pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_unref");
29394 pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_connect");
29395 pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_disconnect");
29396 pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_set_state_callback");
29397 pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_state");
29398 pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_list");
29399 pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_list");
29400 pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name");
29401 pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_by_name");
29402 pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_unref");
29403 pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_get_state");
29404 pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_init_extend");
29405 pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_valid");
29406 pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_compatible");
29407 pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_new");
29408 pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_unref");
29409 pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_playback");
29410 pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_record");
29411 pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_disconnect");
29412 pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_state");
29413 pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_sample_spec");
29414 pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_channel_map");
29415 pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_buffer_attr");
29416 pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_buffer_attr");
29417 pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_device_name");
29418 pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback");
29419 pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback");
29420 pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_suspended_callback");
29421 pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_moved_callback");
29422 pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_suspended");
29423 pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush");
29424 pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain");
29425 pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_corked");
29426 pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_cork");
29427 pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_trigger");
29428 pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_begin_write");
29429 pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_write");
29430 pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_peek");
29431 pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drop");
29432 pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_writable_size");
29433 pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_readable_size");
29434#else
29435 /* This strange assignment system is just for type safety. */
29436 ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new;
29437 ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free;
29438 ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit;
29439 ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api;
29440 ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate;
29441 ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup;
29442 ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new;
29443 ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free;
29444 ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start;
29445 ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop;
29446 ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock;
29447 ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock;
29448 ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait;
29449 ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal;
29450 ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept;
29451 ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval;
29452 ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api;
29453 ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread;
29454 ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name;
29455 ma_pa_context_new_proc _pa_context_new = pa_context_new;
29456 ma_pa_context_unref_proc _pa_context_unref = pa_context_unref;
29457 ma_pa_context_connect_proc _pa_context_connect = pa_context_connect;
29458 ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect;
29459 ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback;
29460 ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state;
29461 ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list;
29462 ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list;
29463 ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name;
29464 ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name;
29465 ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref;
29466 ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state;
29467 ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend;
29468 ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid;
29469 ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible;
29470 ma_pa_stream_new_proc _pa_stream_new = pa_stream_new;
29471 ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref;
29472 ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback;
29473 ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record;
29474 ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect;
29475 ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state;
29476 ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec;
29477 ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map;
29478 ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr;
29479 ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr;
29480 ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name;
29481 ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback;
29482 ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback;
29483 ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback;
29484 ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback;
29485 ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended;
29486 ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush;
29487 ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain;
29488 ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked;
29489 ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork;
29490 ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger;
29491 ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write;
29492 ma_pa_stream_write_proc _pa_stream_write = pa_stream_write;
29493 ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek;
29494 ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop;
29495 ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size;
29496 ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size;
29497
29498 pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new;
29499 pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free;
29500 pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit;
29501 pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api;
29502 pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate;
29503 pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup;
29504 pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new;
29505 pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free;
29506 pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start;
29507 pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop;
29508 pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock;
29509 pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock;
29510 pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait;
29511 pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal;
29512 pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept;
29513 pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval;
29514 pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api;
29515 pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread;
29516 pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name;
29517 pContext->pulse.pa_context_new = (ma_proc)_pa_context_new;
29518 pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref;
29519 pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect;
29520 pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect;
29521 pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback;
29522 pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state;
29523 pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list;
29524 pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list;
29525 pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name;
29526 pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name;
29527 pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref;
29528 pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state;
29529 pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend;
29530 pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid;
29531 pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible;
29532 pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new;
29533 pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref;
29534 pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback;
29535 pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record;
29536 pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect;
29537 pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state;
29538 pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec;
29539 pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map;
29540 pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr;
29541 pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr;
29542 pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name;
29543 pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback;
29544 pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback;
29545 pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback;
29546 pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback;
29547 pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended;
29548 pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush;
29549 pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain;
29550 pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked;
29551 pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork;
29552 pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger;
29553 pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write;
29554 pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write;
29555 pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek;
29556 pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop;
29557 pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size;
29558 pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size;
29559#endif
29560
29561 /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */
29562 pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks);
29563 if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) {
29564 return MA_OUT_OF_MEMORY;
29565 }
29566
29567 pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks);
29568 if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) {
29569 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
29570 return MA_OUT_OF_MEMORY;
29571 }
29572
29573 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);
29574 if (result != MA_SUCCESS) {
29575 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
29576 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
29577 #ifndef MA_NO_RUNTIME_LINKING
29578 ma_dlclose(pContext, pContext->pulse.pulseSO);
29579 #endif
29580 return result;
29581 }
29582
29583 /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */
29584 pCallbacks->onContextInit = ma_context_init__pulse;
29585 pCallbacks->onContextUninit = ma_context_uninit__pulse;
29586 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse;
29587 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse;
29588 pCallbacks->onDeviceInit = ma_device_init__pulse;
29589 pCallbacks->onDeviceUninit = ma_device_uninit__pulse;
29590 pCallbacks->onDeviceStart = ma_device_start__pulse;
29591 pCallbacks->onDeviceStop = ma_device_stop__pulse;
29592 pCallbacks->onDeviceRead = NULL; /* Not used because we're implementing onDeviceDataLoop. */
29593 pCallbacks->onDeviceWrite = NULL; /* Not used because we're implementing onDeviceDataLoop. */
29594 pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse;
29595 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse;
29596
29597 return MA_SUCCESS;
29598}
29599#endif
29600
29601
29602
29607#ifdef MA_HAS_JACK
29608
29609/* It is assumed jack.h is available when compile-time linking is being used. */
29610#ifdef MA_NO_RUNTIME_LINKING
29611#include <jack/jack.h>
29612
29613typedef jack_nframes_t ma_jack_nframes_t;
29614typedef jack_options_t ma_jack_options_t;
29615typedef jack_status_t ma_jack_status_t;
29616typedef jack_client_t ma_jack_client_t;
29617typedef jack_port_t ma_jack_port_t;
29618typedef JackProcessCallback ma_JackProcessCallback;
29619typedef JackBufferSizeCallback ma_JackBufferSizeCallback;
29620typedef JackShutdownCallback ma_JackShutdownCallback;
29621#define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE
29622#define ma_JackNoStartServer JackNoStartServer
29623#define ma_JackPortIsInput JackPortIsInput
29624#define ma_JackPortIsOutput JackPortIsOutput
29625#define ma_JackPortIsPhysical JackPortIsPhysical
29626#else
29627typedef ma_uint32 ma_jack_nframes_t;
29628typedef int ma_jack_options_t;
29629typedef int ma_jack_status_t;
29630typedef struct ma_jack_client_t ma_jack_client_t;
29631typedef struct ma_jack_port_t ma_jack_port_t;
29632typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg);
29633typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg);
29634typedef void (* ma_JackShutdownCallback) (void* arg);
29635#define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio"
29636#define ma_JackNoStartServer 1
29637#define ma_JackPortIsInput 1
29638#define ma_JackPortIsOutput 2
29639#define ma_JackPortIsPhysical 4
29640#endif
29641
29642typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...);
29643typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client);
29644typedef int (* ma_jack_client_name_size_proc) (void);
29645typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg);
29646typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg);
29647typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg);
29648typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client);
29649typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client);
29650typedef 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);
29651typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client);
29652typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client);
29653typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port);
29654typedef 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);
29655typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port);
29656typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes);
29657typedef void (* ma_jack_free_proc) (void* ptr);
29658
29659static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient)
29660{
29661 size_t maxClientNameSize;
29662 char clientName[256];
29663 ma_jack_status_t status;
29664 ma_jack_client_t* pClient;
29665
29666 MA_ASSERT(pContext != NULL);
29667 MA_ASSERT(ppClient != NULL);
29668
29669 if (ppClient) {
29670 *ppClient = NULL;
29671 }
29672
29673 maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */
29674 ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1);
29675
29676 pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL);
29677 if (pClient == NULL) {
29679 }
29680
29681 if (ppClient) {
29682 *ppClient = pClient;
29683 }
29684
29685 return MA_SUCCESS;
29686}
29687
29688
29689static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
29690{
29691 ma_bool32 cbResult = MA_TRUE;
29692
29693 MA_ASSERT(pContext != NULL);
29694 MA_ASSERT(callback != NULL);
29695
29696 /* Playback. */
29697 if (cbResult) {
29698 ma_device_info deviceInfo;
29699 MA_ZERO_OBJECT(&deviceInfo);
29700 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
29701 deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */
29702 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
29703 }
29704
29705 /* Capture. */
29706 if (cbResult) {
29707 ma_device_info deviceInfo;
29708 MA_ZERO_OBJECT(&deviceInfo);
29709 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
29710 deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */
29711 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
29712 }
29713
29714 (void)cbResult; /* For silencing a static analysis warning. */
29715
29716 return MA_SUCCESS;
29717}
29718
29719static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
29720{
29721 ma_jack_client_t* pClient;
29722 ma_result result;
29723 const char** ppPorts;
29724
29725 MA_ASSERT(pContext != NULL);
29726
29727 if (pDeviceID != NULL && pDeviceID->jack != 0) {
29728 return MA_NO_DEVICE; /* Don't know the device. */
29729 }
29730
29731 /* Name / Description */
29732 if (deviceType == ma_device_type_playback) {
29733 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
29734 } else {
29735 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
29736 }
29737
29738 /* Jack only uses default devices. */
29739 pDeviceInfo->isDefault = MA_TRUE;
29740
29741 /* Jack only supports f32 and has a specific channel count and sample rate. */
29742 pDeviceInfo->nativeDataFormats[0].format = ma_format_f32;
29743
29744 /* The channel count and sample rate can only be determined by opening the device. */
29745 result = ma_context_open_client__jack(pContext, &pClient);
29746 if (result != MA_SUCCESS) {
29747 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.");
29748 return result;
29749 }
29750
29751 pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient);
29752 pDeviceInfo->nativeDataFormats[0].channels = 0;
29753
29754 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));
29755 if (ppPorts == NULL) {
29756 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
29757 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.");
29759 }
29760
29761 while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) {
29762 pDeviceInfo->nativeDataFormats[0].channels += 1;
29763 }
29764
29765 pDeviceInfo->nativeDataFormats[0].flags = 0;
29766 pDeviceInfo->nativeDataFormatCount = 1;
29767
29768 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
29769 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
29770
29771 (void)pContext;
29772 return MA_SUCCESS;
29773}
29774
29775
29776static ma_result ma_device_uninit__jack(ma_device* pDevice)
29777{
29778 ma_context* pContext;
29779
29780 MA_ASSERT(pDevice != NULL);
29781
29782 pContext = pDevice->pContext;
29783 MA_ASSERT(pContext != NULL);
29784
29785 if (pDevice->jack.pClient != NULL) {
29786 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient);
29787 }
29788
29789 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
29790 ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
29791 ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks);
29792 }
29793
29794 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
29795 ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
29796 ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks);
29797 }
29798
29799 return MA_SUCCESS;
29800}
29801
29802static void ma_device__jack_shutdown_callback(void* pUserData)
29803{
29804 /* JACK died. Stop the device. */
29805 ma_device* pDevice = (ma_device*)pUserData;
29806 MA_ASSERT(pDevice != NULL);
29807
29808 ma_device_stop(pDevice);
29809}
29810
29811static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData)
29812{
29813 ma_device* pDevice = (ma_device*)pUserData;
29814 MA_ASSERT(pDevice != NULL);
29815
29816 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
29817 size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
29818 float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks);
29819 if (pNewBuffer == NULL) {
29820 return MA_OUT_OF_MEMORY;
29821 }
29822
29823 ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
29824
29825 pDevice->jack.pIntermediaryBufferCapture = pNewBuffer;
29826 pDevice->playback.internalPeriodSizeInFrames = frameCount;
29827 }
29828
29829 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
29830 size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
29831 float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks);
29832 if (pNewBuffer == NULL) {
29833 return MA_OUT_OF_MEMORY;
29834 }
29835
29836 ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
29837
29838 pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer;
29839 pDevice->playback.internalPeriodSizeInFrames = frameCount;
29840 }
29841
29842 return 0;
29843}
29844
29845static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData)
29846{
29847 ma_device* pDevice;
29848 ma_context* pContext;
29849 ma_uint32 iChannel;
29850
29851 pDevice = (ma_device*)pUserData;
29852 MA_ASSERT(pDevice != NULL);
29853
29854 pContext = pDevice->pContext;
29855 MA_ASSERT(pContext != NULL);
29856
29857 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
29858 /* Channels need to be interleaved. */
29859 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
29860 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);
29861 if (pSrc != NULL) {
29862 float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel;
29863 ma_jack_nframes_t iFrame;
29864 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
29865 *pDst = *pSrc;
29866
29867 pDst += pDevice->capture.internalChannels;
29868 pSrc += 1;
29869 }
29870 }
29871 }
29872
29873 ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount);
29874 }
29875
29876 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
29877 ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount);
29878
29879 /* Channels need to be deinterleaved. */
29880 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
29881 float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount);
29882 if (pDst != NULL) {
29883 const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel;
29884 ma_jack_nframes_t iFrame;
29885 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
29886 *pDst = *pSrc;
29887
29888 pDst += 1;
29889 pSrc += pDevice->playback.internalChannels;
29890 }
29891 }
29892 }
29893 }
29894
29895 return 0;
29896}
29897
29898static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
29899{
29900 ma_result result;
29901 ma_uint32 periodSizeInFrames;
29902
29903 MA_ASSERT(pConfig != NULL);
29904 MA_ASSERT(pDevice != NULL);
29905
29906 if (pConfig->deviceType == ma_device_type_loopback) {
29907 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported.");
29909 }
29910
29911 /* Only supporting default devices with JACK. */
29912 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) ||
29913 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) {
29914 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported.");
29915 return MA_NO_DEVICE;
29916 }
29917
29918 /* No exclusive mode with the JACK backend. */
29919 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
29920 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
29921 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported.");
29923 }
29924
29925 /* Open the client. */
29926 result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient);
29927 if (result != MA_SUCCESS) {
29928 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.");
29929 return result;
29930 }
29931
29932 /* Callbacks. */
29933 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) {
29934 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback.");
29936 }
29937 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) {
29938 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback.");
29940 }
29941
29942 ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice);
29943
29944
29945 /* The buffer size in frames can change. */
29946 periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient);
29947
29948 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
29949 ma_uint32 iPort;
29950 const char** ppPorts;
29951
29952 pDescriptorCapture->format = ma_format_f32;
29953 pDescriptorCapture->channels = 0;
29954 pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
29955 ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
29956
29957 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);
29958 if (ppPorts == NULL) {
29959 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.");
29961 }
29962
29963 /* Need to count the number of ports first so we can allocate some memory. */
29964 while (ppPorts[pDescriptorCapture->channels] != NULL) {
29965 pDescriptorCapture->channels += 1;
29966 }
29967
29968 pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks);
29969 if (pDevice->jack.ppPortsCapture == NULL) {
29970 return MA_OUT_OF_MEMORY;
29971 }
29972
29973 for (iPort = 0; iPort < pDescriptorCapture->channels; iPort += 1) {
29974 char name[64];
29975 ma_strcpy_s(name, sizeof(name), "capture");
29976 ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */
29977
29978 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);
29979 if (pDevice->jack.ppPortsCapture[iPort] == NULL) {
29980 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
29981 ma_device_uninit__jack(pDevice);
29982 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.");
29984 }
29985 }
29986
29987 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
29988
29989 pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
29990 pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */
29991
29992 pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks);
29993 if (pDevice->jack.pIntermediaryBufferCapture == NULL) {
29994 ma_device_uninit__jack(pDevice);
29995 return MA_OUT_OF_MEMORY;
29996 }
29997 }
29998
29999 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
30000 ma_uint32 iPort;
30001 const char** ppPorts;
30002
30003 pDescriptorPlayback->format = ma_format_f32;
30004 pDescriptorPlayback->channels = 0;
30005 pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
30006 ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);
30007
30008 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);
30009 if (ppPorts == NULL) {
30010 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.");
30012 }
30013
30014 /* Need to count the number of ports first so we can allocate some memory. */
30015 while (ppPorts[pDescriptorPlayback->channels] != NULL) {
30016 pDescriptorPlayback->channels += 1;
30017 }
30018
30019 pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks);
30020 if (pDevice->jack.ppPortsPlayback == NULL) {
30021 ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks);
30022 return MA_OUT_OF_MEMORY;
30023 }
30024
30025 for (iPort = 0; iPort < pDescriptorPlayback->channels; iPort += 1) {
30026 char name[64];
30027 ma_strcpy_s(name, sizeof(name), "playback");
30028 ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */
30029
30030 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);
30031 if (pDevice->jack.ppPortsPlayback[iPort] == NULL) {
30032 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
30033 ma_device_uninit__jack(pDevice);
30034 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.");
30036 }
30037 }
30038
30039 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
30040
30041 pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
30042 pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */
30043
30044 pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks);
30045 if (pDevice->jack.pIntermediaryBufferPlayback == NULL) {
30046 ma_device_uninit__jack(pDevice);
30047 return MA_OUT_OF_MEMORY;
30048 }
30049 }
30050
30051 return MA_SUCCESS;
30052}
30053
30054
30055static ma_result ma_device_start__jack(ma_device* pDevice)
30056{
30057 ma_context* pContext = pDevice->pContext;
30058 int resultJACK;
30059 size_t i;
30060
30061 resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient);
30062 if (resultJACK != 0) {
30063 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client.");
30065 }
30066
30067 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
30068 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);
30069 if (ppServerPorts == NULL) {
30070 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
30071 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.");
30072 return MA_ERROR;
30073 }
30074
30075 for (i = 0; ppServerPorts[i] != NULL; ++i) {
30076 const char* pServerPort = ppServerPorts[i];
30077 const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]);
30078
30079 resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort);
30080 if (resultJACK != 0) {
30081 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
30082 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
30083 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.");
30084 return MA_ERROR;
30085 }
30086 }
30087
30088 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
30089 }
30090
30091 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
30092 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);
30093 if (ppServerPorts == NULL) {
30094 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
30095 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.");
30096 return MA_ERROR;
30097 }
30098
30099 for (i = 0; ppServerPorts[i] != NULL; ++i) {
30100 const char* pServerPort = ppServerPorts[i];
30101 const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]);
30102
30103 resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort);
30104 if (resultJACK != 0) {
30105 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
30106 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
30107 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.");
30108 return MA_ERROR;
30109 }
30110 }
30111
30112 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
30113 }
30114
30115 return MA_SUCCESS;
30116}
30117
30118static ma_result ma_device_stop__jack(ma_device* pDevice)
30119{
30120 ma_context* pContext = pDevice->pContext;
30121
30122 if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) {
30123 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.");
30124 return MA_ERROR;
30125 }
30126
30127 ma_device__on_notification_stopped(pDevice);
30128
30129 return MA_SUCCESS;
30130}
30131
30132
30133static ma_result ma_context_uninit__jack(ma_context* pContext)
30134{
30135 MA_ASSERT(pContext != NULL);
30136 MA_ASSERT(pContext->backend == ma_backend_jack);
30137
30138 ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
30139 pContext->jack.pClientName = NULL;
30140
30141#ifndef MA_NO_RUNTIME_LINKING
30142 ma_dlclose(pContext, pContext->jack.jackSO);
30143#endif
30144
30145 return MA_SUCCESS;
30146}
30147
30148static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
30149{
30150#ifndef MA_NO_RUNTIME_LINKING
30151 const char* libjackNames[] = {
30152#ifdef MA_WIN32
30153 "libjack.dll",
30154 "libjack64.dll"
30155#else
30156 "libjack.so",
30157 "libjack.so.0"
30158#endif
30159 };
30160 size_t i;
30161
30162 for (i = 0; i < ma_countof(libjackNames); ++i) {
30163 pContext->jack.jackSO = ma_dlopen(pContext, libjackNames[i]);
30164 if (pContext->jack.jackSO != NULL) {
30165 break;
30166 }
30167 }
30168
30169 if (pContext->jack.jackSO == NULL) {
30170 return MA_NO_BACKEND;
30171 }
30172
30173 pContext->jack.jack_client_open = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_open");
30174 pContext->jack.jack_client_close = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_close");
30175 pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_name_size");
30176 pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_process_callback");
30177 pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_buffer_size_callback");
30178 pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_on_shutdown");
30179 pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_sample_rate");
30180 pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_buffer_size");
30181 pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_ports");
30182 pContext->jack.jack_activate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_activate");
30183 pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_deactivate");
30184 pContext->jack.jack_connect = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_connect");
30185 pContext->jack.jack_port_register = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_register");
30186 pContext->jack.jack_port_name = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_name");
30187 pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_get_buffer");
30188 pContext->jack.jack_free = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_free");
30189#else
30190 /*
30191 This strange assignment system is here just to ensure type safety of miniaudio's function pointer
30192 types. If anything differs slightly the compiler should throw a warning.
30193 */
30194 ma_jack_client_open_proc _jack_client_open = jack_client_open;
30195 ma_jack_client_close_proc _jack_client_close = jack_client_close;
30196 ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size;
30197 ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback;
30198 ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback;
30199 ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown;
30200 ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate;
30201 ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size;
30202 ma_jack_get_ports_proc _jack_get_ports = jack_get_ports;
30203 ma_jack_activate_proc _jack_activate = jack_activate;
30204 ma_jack_deactivate_proc _jack_deactivate = jack_deactivate;
30205 ma_jack_connect_proc _jack_connect = jack_connect;
30206 ma_jack_port_register_proc _jack_port_register = jack_port_register;
30207 ma_jack_port_name_proc _jack_port_name = jack_port_name;
30208 ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer;
30209 ma_jack_free_proc _jack_free = jack_free;
30210
30211 pContext->jack.jack_client_open = (ma_proc)_jack_client_open;
30212 pContext->jack.jack_client_close = (ma_proc)_jack_client_close;
30213 pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size;
30214 pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback;
30215 pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback;
30216 pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown;
30217 pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate;
30218 pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size;
30219 pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports;
30220 pContext->jack.jack_activate = (ma_proc)_jack_activate;
30221 pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate;
30222 pContext->jack.jack_connect = (ma_proc)_jack_connect;
30223 pContext->jack.jack_port_register = (ma_proc)_jack_port_register;
30224 pContext->jack.jack_port_name = (ma_proc)_jack_port_name;
30225 pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer;
30226 pContext->jack.jack_free = (ma_proc)_jack_free;
30227#endif
30228
30229 if (pConfig->jack.pClientName != NULL) {
30230 pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks);
30231 }
30232 pContext->jack.tryStartServer = pConfig->jack.tryStartServer;
30233
30234 /*
30235 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
30236 a temporary client.
30237 */
30238 {
30239 ma_jack_client_t* pDummyClient;
30240 ma_result result = ma_context_open_client__jack(pContext, &pDummyClient);
30241 if (result != MA_SUCCESS) {
30242 ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
30243 #ifndef MA_NO_RUNTIME_LINKING
30244 ma_dlclose(pContext, pContext->jack.jackSO);
30245 #endif
30246 return MA_NO_BACKEND;
30247 }
30248
30249 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient);
30250 }
30251
30252
30253 pCallbacks->onContextInit = ma_context_init__jack;
30254 pCallbacks->onContextUninit = ma_context_uninit__jack;
30255 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack;
30256 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack;
30257 pCallbacks->onDeviceInit = ma_device_init__jack;
30258 pCallbacks->onDeviceUninit = ma_device_uninit__jack;
30259 pCallbacks->onDeviceStart = ma_device_start__jack;
30260 pCallbacks->onDeviceStop = ma_device_stop__jack;
30261 pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */
30262 pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */
30263 pCallbacks->onDeviceDataLoop = NULL; /* Not used because JACK is asynchronous. */
30264
30265 return MA_SUCCESS;
30266}
30267#endif /* JACK */
30268
30269
30270
30271
30281#ifdef MA_HAS_COREAUDIO
30282#include <TargetConditionals.h>
30283
30284#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1
30285 #define MA_APPLE_MOBILE
30286 #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
30287 #define MA_APPLE_TV
30288 #endif
30289 #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
30290 #define MA_APPLE_WATCH
30291 #endif
30292 #if __has_feature(objc_arc)
30293 #define MA_BRIDGE_TRANSFER __bridge_transfer
30294 #define MA_BRIDGE_RETAINED __bridge_retained
30295 #else
30296 #define MA_BRIDGE_TRANSFER
30297 #define MA_BRIDGE_RETAINED
30298 #endif
30299#else
30300 #define MA_APPLE_DESKTOP
30301#endif
30302
30303#if defined(MA_APPLE_DESKTOP)
30304#include <CoreAudio/CoreAudio.h>
30305#else
30306#include <AVFoundation/AVFoundation.h>
30307#endif
30308
30309#include <AudioToolbox/AudioToolbox.h>
30310
30311/* CoreFoundation */
30312typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding);
30313typedef void (* ma_CFRelease_proc)(CFTypeRef cf);
30314
30315/* CoreAudio */
30316#if defined(MA_APPLE_DESKTOP)
30317typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData);
30318typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);
30319typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
30320typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
30321typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
30322#endif
30323
30324/* AudioToolbox */
30325typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc);
30326typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance);
30327typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance);
30328typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit);
30329typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit);
30330typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData);
30331typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable);
30332typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize);
30333typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
30334typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit);
30335typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
30336
30337
30338#define MA_COREAUDIO_OUTPUT_BUS 0
30339#define MA_COREAUDIO_INPUT_BUS 1
30340
30341#if defined(MA_APPLE_DESKTOP)
30342static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit);
30343#endif
30344
30345/*
30346Core Audio
30347
30348So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation
30349apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose
30350needing to figure out how this darn thing works, I'm going to outline a few things here.
30351
30352Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be
30353able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen
30354that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent
30355and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the
30356distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API.
30357
30358Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When
30359retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific
30360data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the
30361devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be
30362the central APIs for retrieving information about the system and specific devices.
30363
30364To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a
30365structure with three variables and is used to identify which property you are getting or setting. The first is the "selector"
30366which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is
30367typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and
30368kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to
30369kAudioObjectPropertyElementMaster in miniaudio's case. I don't know of any cases where this would be set to anything different.
30370
30371Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size
30372of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property
30373address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the
30374size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of
30375AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count.
30376*/
30377
30378static ma_result ma_result_from_OSStatus(OSStatus status)
30379{
30380 switch (status)
30381 {
30382 case noErr: return MA_SUCCESS;
30383 #if defined(MA_APPLE_DESKTOP)
30384 case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED;
30385 case kAudioHardwareUnspecifiedError: return MA_ERROR;
30386 case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS;
30387 case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION;
30388 case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION;
30389 case kAudioHardwareBadObjectError: return MA_INVALID_ARGS;
30390 case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS;
30391 case kAudioHardwareBadStreamError: return MA_INVALID_ARGS;
30392 case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION;
30393 case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED;
30394 case kAudioDevicePermissionsError: return MA_ACCESS_DENIED;
30395 #endif
30396 default: return MA_ERROR;
30397 }
30398}
30399
30400#if 0
30401static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit)
30402{
30403 switch (bit)
30404 {
30405 case kAudioChannelBit_Left: return MA_CHANNEL_LEFT;
30406 case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT;
30407 case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER;
30408 case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE;
30409 case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT;
30410 case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT;
30411 case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
30412 case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
30413 case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER;
30414 case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
30415 case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
30416 case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
30417 case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
30418 case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
30419 case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
30420 case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
30421 case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
30422 case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
30423 default: return MA_CHANNEL_NONE;
30424 }
30425}
30426#endif
30427
30428static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut)
30429{
30430 MA_ASSERT(pDescription != NULL);
30431 MA_ASSERT(pFormatOut != NULL);
30432
30433 *pFormatOut = ma_format_unknown; /* Safety. */
30434
30435 /* There's a few things miniaudio doesn't support. */
30436 if (pDescription->mFormatID != kAudioFormatLinearPCM) {
30438 }
30439
30440 /* We don't support any non-packed formats that are aligned high. */
30441 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) {
30443 }
30444
30445 /* Only supporting native-endian. */
30446 if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) {
30448 }
30449
30450 /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */
30451 /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) {
30452 return MA_FORMAT_NOT_SUPPORTED;
30453 }*/
30454
30455 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) {
30456 if (pDescription->mBitsPerChannel == 32) {
30457 *pFormatOut = ma_format_f32;
30458 return MA_SUCCESS;
30459 }
30460 } else {
30461 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) {
30462 if (pDescription->mBitsPerChannel == 16) {
30463 *pFormatOut = ma_format_s16;
30464 return MA_SUCCESS;
30465 } else if (pDescription->mBitsPerChannel == 24) {
30466 if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) {
30467 *pFormatOut = ma_format_s24;
30468 return MA_SUCCESS;
30469 } else {
30470 if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) {
30471 /* TODO: Implement ma_format_s24_32. */
30473 /*return MA_SUCCESS;*/
30475 }
30476 }
30477 } else if (pDescription->mBitsPerChannel == 32) {
30478 *pFormatOut = ma_format_s32;
30479 return MA_SUCCESS;
30480 }
30481 } else {
30482 if (pDescription->mBitsPerChannel == 8) {
30483 *pFormatOut = ma_format_u8;
30484 return MA_SUCCESS;
30485 }
30486 }
30487 }
30488
30489 /* Getting here means the format is not supported. */
30491}
30492
30493#if defined(MA_APPLE_DESKTOP)
30494static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label)
30495{
30496 switch (label)
30497 {
30498 case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE;
30499 case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE;
30500 case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE;
30501 case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT;
30502 case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT;
30503 case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER;
30504 case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE;
30505 case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT;
30506 case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT;
30507 case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
30508 case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
30509 case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER;
30510 case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
30511 case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
30512 case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
30513 case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
30514 case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
30515 case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
30516 case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
30517 case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
30518 case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
30519 case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT;
30520 case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT;
30521 case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT;
30522 case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT;
30523 case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE;
30524 case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT;
30525 case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT;
30526 case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE;
30527 case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO;
30528 case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO;
30529 case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO;
30530 case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER;
30531 case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE;
30532 case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE;
30533 case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE;
30534 case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE;
30535 case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE;
30536 case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT;
30537 case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT;
30538 case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT;
30539 case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT;
30540 case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT;
30541 case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT;
30542 case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE;
30543 case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE;
30544 case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE;
30545 case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0;
30546 case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1;
30547 case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2;
30548 case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3;
30549 case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4;
30550 case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5;
30551 case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6;
30552 case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7;
30553 case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8;
30554 case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9;
30555 case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10;
30556 case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11;
30557 case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12;
30558 case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13;
30559 case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14;
30560 case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15;
30561 case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE;
30562
30563 #if 0 /* Introduced in a later version of macOS. */
30564 case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE;
30565 case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0;
30566 case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1;
30567 case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2;
30568 case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3;
30569 case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4;
30570 case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5;
30571 case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6;
30572 case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7;
30573 case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8;
30574 case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9;
30575 case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10;
30576 case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11;
30577 case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12;
30578 case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13;
30579 case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14;
30580 case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15;
30581 case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE;
30582 #endif
30583
30584 default: return MA_CHANNEL_NONE;
30585 }
30586}
30587
30588static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap)
30589{
30590 MA_ASSERT(pChannelLayout != NULL);
30591
30592 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
30593 UInt32 iChannel;
30594 for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) {
30595 pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);
30596 }
30597 } else
30598#if 0
30599 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
30600 /* This is the same kind of system that's used by Windows audio APIs. */
30601 UInt32 iChannel = 0;
30602 UInt32 iBit;
30603 AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap;
30604 for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) {
30605 AudioChannelBitmap bit = bitmap & (1 << iBit);
30606 if (bit != 0) {
30607 pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit);
30608 }
30609 }
30610 } else
30611#endif
30612 {
30613 /*
30614 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
30615 be updated to determine the mapping based on the tag.
30616 */
30617 UInt32 channelCount;
30618
30619 /* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */
30620 if (channelMapCap > 0xFFFFFFFF) {
30621 channelMapCap = 0xFFFFFFFF;
30622 }
30623
30624 channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap);
30625
30626 switch (pChannelLayout->mChannelLayoutTag)
30627 {
30628 case kAudioChannelLayoutTag_Mono:
30629 case kAudioChannelLayoutTag_Stereo:
30630 case kAudioChannelLayoutTag_StereoHeadphones:
30631 case kAudioChannelLayoutTag_MatrixStereo:
30632 case kAudioChannelLayoutTag_MidSide:
30633 case kAudioChannelLayoutTag_XY:
30634 case kAudioChannelLayoutTag_Binaural:
30635 case kAudioChannelLayoutTag_Ambisonic_B_Format:
30636 {
30637 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
30638 } break;
30639
30640 case kAudioChannelLayoutTag_Octagonal:
30641 {
30642 pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
30643 pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
30644 } /* Intentional fallthrough. */
30645 case kAudioChannelLayoutTag_Hexagonal:
30646 {
30647 pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
30648 } /* Intentional fallthrough. */
30649 case kAudioChannelLayoutTag_Pentagonal:
30650 {
30651 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
30652 } /* Intentional fallghrough. */
30653 case kAudioChannelLayoutTag_Quadraphonic:
30654 {
30655 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
30656 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
30657 pChannelMap[1] = MA_CHANNEL_RIGHT;
30658 pChannelMap[0] = MA_CHANNEL_LEFT;
30659 } break;
30660
30661 /* TODO: Add support for more tags here. */
30662
30663 default:
30664 {
30665 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
30666 } break;
30667 }
30668 }
30669
30670 return MA_SUCCESS;
30671}
30672
30673static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */
30674{
30675 AudioObjectPropertyAddress propAddressDevices;
30676 UInt32 deviceObjectsDataSize;
30677 OSStatus status;
30678 AudioObjectID* pDeviceObjectIDs;
30679
30680 MA_ASSERT(pContext != NULL);
30681 MA_ASSERT(pDeviceCount != NULL);
30682 MA_ASSERT(ppDeviceObjectIDs != NULL);
30683
30684 /* Safety. */
30685 *pDeviceCount = 0;
30686 *ppDeviceObjectIDs = NULL;
30687
30688 propAddressDevices.mSelector = kAudioHardwarePropertyDevices;
30689 propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal;
30690 propAddressDevices.mElement = kAudioObjectPropertyElementMaster;
30691
30692 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize);
30693 if (status != noErr) {
30694 return ma_result_from_OSStatus(status);
30695 }
30696
30697 pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks);
30698 if (pDeviceObjectIDs == NULL) {
30699 return MA_OUT_OF_MEMORY;
30700 }
30701
30702 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs);
30703 if (status != noErr) {
30704 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
30705 return ma_result_from_OSStatus(status);
30706 }
30707
30708 *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID);
30709 *ppDeviceObjectIDs = pDeviceObjectIDs;
30710
30711 return MA_SUCCESS;
30712}
30713
30714static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID)
30715{
30716 AudioObjectPropertyAddress propAddress;
30717 UInt32 dataSize;
30718 OSStatus status;
30719
30720 MA_ASSERT(pContext != NULL);
30721
30722 propAddress.mSelector = kAudioDevicePropertyDeviceUID;
30723 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
30724 propAddress.mElement = kAudioObjectPropertyElementMaster;
30725
30726 dataSize = sizeof(*pUID);
30727 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID);
30728 if (status != noErr) {
30729 return ma_result_from_OSStatus(status);
30730 }
30731
30732 return MA_SUCCESS;
30733}
30734
30735static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
30736{
30737 CFStringRef uid;
30738 ma_result result;
30739
30740 MA_ASSERT(pContext != NULL);
30741
30742 result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid);
30743 if (result != MA_SUCCESS) {
30744 return result;
30745 }
30746
30747 if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
30748 return MA_ERROR;
30749 }
30750
30751 ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid);
30752 return MA_SUCCESS;
30753}
30754
30755static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
30756{
30757 AudioObjectPropertyAddress propAddress;
30758 CFStringRef deviceName = NULL;
30759 UInt32 dataSize;
30760 OSStatus status;
30761
30762 MA_ASSERT(pContext != NULL);
30763
30764 propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
30765 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
30766 propAddress.mElement = kAudioObjectPropertyElementMaster;
30767
30768 dataSize = sizeof(deviceName);
30769 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName);
30770 if (status != noErr) {
30771 return ma_result_from_OSStatus(status);
30772 }
30773
30774 if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
30775 return MA_ERROR;
30776 }
30777
30778 ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName);
30779 return MA_SUCCESS;
30780}
30781
30782static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope)
30783{
30784 AudioObjectPropertyAddress propAddress;
30785 UInt32 dataSize;
30786 OSStatus status;
30787 AudioBufferList* pBufferList;
30788 ma_bool32 isSupported;
30789
30790 MA_ASSERT(pContext != NULL);
30791
30792 /* 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. */
30793 propAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
30794 propAddress.mScope = scope;
30795 propAddress.mElement = kAudioObjectPropertyElementMaster;
30796
30797 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
30798 if (status != noErr) {
30799 return MA_FALSE;
30800 }
30801
30802 pBufferList = (AudioBufferList*)ma_malloc(dataSize, &pContext->allocationCallbacks);
30803 if (pBufferList == NULL) {
30804 return MA_FALSE; /* Out of memory. */
30805 }
30806
30807 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList);
30808 if (status != noErr) {
30809 ma_free(pBufferList, &pContext->allocationCallbacks);
30810 return MA_FALSE;
30811 }
30812
30813 isSupported = MA_FALSE;
30814 if (pBufferList->mNumberBuffers > 0) {
30815 isSupported = MA_TRUE;
30816 }
30817
30818 ma_free(pBufferList, &pContext->allocationCallbacks);
30819 return isSupported;
30820}
30821
30822static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID)
30823{
30824 return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput);
30825}
30826
30827static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID)
30828{
30829 return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput);
30830}
30831
30832
30833static 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(). */
30834{
30835 AudioObjectPropertyAddress propAddress;
30836 UInt32 dataSize;
30837 OSStatus status;
30838 AudioStreamRangedDescription* pDescriptions;
30839
30840 MA_ASSERT(pContext != NULL);
30841 MA_ASSERT(pDescriptionCount != NULL);
30842 MA_ASSERT(ppDescriptions != NULL);
30843
30844 /*
30845 TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My
30846 MacBook Pro uses s24/32 format, however, which miniaudio does not currently support.
30847 */
30848 propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/
30849 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
30850 propAddress.mElement = kAudioObjectPropertyElementMaster;
30851
30852 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
30853 if (status != noErr) {
30854 return ma_result_from_OSStatus(status);
30855 }
30856
30857 pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks);
30858 if (pDescriptions == NULL) {
30859 return MA_OUT_OF_MEMORY;
30860 }
30861
30862 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions);
30863 if (status != noErr) {
30864 ma_free(pDescriptions, &pContext->allocationCallbacks);
30865 return ma_result_from_OSStatus(status);
30866 }
30867
30868 *pDescriptionCount = dataSize / sizeof(*pDescriptions);
30869 *ppDescriptions = pDescriptions;
30870 return MA_SUCCESS;
30871}
30872
30873
30874static 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(). */
30875{
30876 AudioObjectPropertyAddress propAddress;
30877 UInt32 dataSize;
30878 OSStatus status;
30879 AudioChannelLayout* pChannelLayout;
30880
30881 MA_ASSERT(pContext != NULL);
30882 MA_ASSERT(ppChannelLayout != NULL);
30883
30884 *ppChannelLayout = NULL; /* Safety. */
30885
30886 propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
30887 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
30888 propAddress.mElement = kAudioObjectPropertyElementMaster;
30889
30890 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
30891 if (status != noErr) {
30892 return ma_result_from_OSStatus(status);
30893 }
30894
30895 pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks);
30896 if (pChannelLayout == NULL) {
30897 return MA_OUT_OF_MEMORY;
30898 }
30899
30900 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout);
30901 if (status != noErr) {
30902 ma_free(pChannelLayout, &pContext->allocationCallbacks);
30903 return ma_result_from_OSStatus(status);
30904 }
30905
30906 *ppChannelLayout = pChannelLayout;
30907 return MA_SUCCESS;
30908}
30909
30910static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount)
30911{
30912 AudioChannelLayout* pChannelLayout;
30913 ma_result result;
30914
30915 MA_ASSERT(pContext != NULL);
30916 MA_ASSERT(pChannelCount != NULL);
30917
30918 *pChannelCount = 0; /* Safety. */
30919
30920 result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
30921 if (result != MA_SUCCESS) {
30922 return result;
30923 }
30924
30925 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
30926 *pChannelCount = pChannelLayout->mNumberChannelDescriptions;
30927 } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
30928 *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap);
30929 } else {
30930 *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
30931 }
30932
30933 ma_free(pChannelLayout, &pContext->allocationCallbacks);
30934 return MA_SUCCESS;
30935}
30936
30937#if 0
30938static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
30939{
30940 AudioChannelLayout* pChannelLayout;
30941 ma_result result;
30942
30943 MA_ASSERT(pContext != NULL);
30944
30945 result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
30946 if (result != MA_SUCCESS) {
30947 return result; /* Rather than always failing here, would it be more robust to simply assume a default? */
30948 }
30949
30950 result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
30951 if (result != MA_SUCCESS) {
30952 ma_free(pChannelLayout, &pContext->allocationCallbacks);
30953 return result;
30954 }
30955
30956 ma_free(pChannelLayout, &pContext->allocationCallbacks);
30957 return result;
30958}
30959#endif
30960
30961static 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(). */
30962{
30963 AudioObjectPropertyAddress propAddress;
30964 UInt32 dataSize;
30965 OSStatus status;
30966 AudioValueRange* pSampleRateRanges;
30967
30968 MA_ASSERT(pContext != NULL);
30969 MA_ASSERT(pSampleRateRangesCount != NULL);
30970 MA_ASSERT(ppSampleRateRanges != NULL);
30971
30972 /* Safety. */
30973 *pSampleRateRangesCount = 0;
30974 *ppSampleRateRanges = NULL;
30975
30976 propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
30977 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
30978 propAddress.mElement = kAudioObjectPropertyElementMaster;
30979
30980 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
30981 if (status != noErr) {
30982 return ma_result_from_OSStatus(status);
30983 }
30984
30985 pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks);
30986 if (pSampleRateRanges == NULL) {
30987 return MA_OUT_OF_MEMORY;
30988 }
30989
30990 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges);
30991 if (status != noErr) {
30992 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
30993 return ma_result_from_OSStatus(status);
30994 }
30995
30996 *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges);
30997 *ppSampleRateRanges = pSampleRateRanges;
30998 return MA_SUCCESS;
30999}
31000
31001#if 0
31002static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut)
31003{
31004 UInt32 sampleRateRangeCount;
31005 AudioValueRange* pSampleRateRanges;
31006 ma_result result;
31007
31008 MA_ASSERT(pContext != NULL);
31009 MA_ASSERT(pSampleRateOut != NULL);
31010
31011 *pSampleRateOut = 0; /* Safety. */
31012
31013 result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
31014 if (result != MA_SUCCESS) {
31015 return result;
31016 }
31017
31018 if (sampleRateRangeCount == 0) {
31019 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
31020 return MA_ERROR; /* Should never hit this case should we? */
31021 }
31022
31023 if (sampleRateIn == 0) {
31024 /* Search in order of miniaudio's preferred priority. */
31025 UInt32 iMALSampleRate;
31026 for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) {
31027 ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate];
31028 UInt32 iCASampleRate;
31029 for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) {
31030 AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate];
31031 if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) {
31032 *pSampleRateOut = malSampleRate;
31033 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
31034 return MA_SUCCESS;
31035 }
31036 }
31037 }
31038
31039 /*
31040 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
31041 case we just fall back to the first one reported by Core Audio.
31042 */
31043 MA_ASSERT(sampleRateRangeCount > 0);
31044
31045 *pSampleRateOut = pSampleRateRanges[0].mMinimum;
31046 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
31047 return MA_SUCCESS;
31048 } else {
31049 /* Find the closest match to this sample rate. */
31050 UInt32 currentAbsoluteDifference = INT32_MAX;
31051 UInt32 iCurrentClosestRange = (UInt32)-1;
31052 UInt32 iRange;
31053 for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) {
31054 if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) {
31055 *pSampleRateOut = sampleRateIn;
31056 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
31057 return MA_SUCCESS;
31058 } else {
31059 UInt32 absoluteDifference;
31060 if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) {
31061 absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn;
31062 } else {
31063 absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum;
31064 }
31065
31066 if (currentAbsoluteDifference > absoluteDifference) {
31067 currentAbsoluteDifference = absoluteDifference;
31068 iCurrentClosestRange = iRange;
31069 }
31070 }
31071 }
31072
31073 MA_ASSERT(iCurrentClosestRange != (UInt32)-1);
31074
31075 *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum;
31076 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
31077 return MA_SUCCESS;
31078 }
31079
31080 /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */
31081 /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/
31082 /*return MA_ERROR;*/
31083}
31084#endif
31085
31086static 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)
31087{
31088 AudioObjectPropertyAddress propAddress;
31089 AudioValueRange bufferSizeRange;
31090 UInt32 dataSize;
31091 OSStatus status;
31092
31093 MA_ASSERT(pContext != NULL);
31094 MA_ASSERT(pBufferSizeInFramesOut != NULL);
31095
31096 *pBufferSizeInFramesOut = 0; /* Safety. */
31097
31098 propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
31099 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
31100 propAddress.mElement = kAudioObjectPropertyElementMaster;
31101
31102 dataSize = sizeof(bufferSizeRange);
31103 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange);
31104 if (status != noErr) {
31105 return ma_result_from_OSStatus(status);
31106 }
31107
31108 /* This is just a clamp. */
31109 if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) {
31110 *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum;
31111 } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) {
31112 *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum;
31113 } else {
31114 *pBufferSizeInFramesOut = bufferSizeInFramesIn;
31115 }
31116
31117 return MA_SUCCESS;
31118}
31119
31120static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut)
31121{
31122 ma_result result;
31123 ma_uint32 chosenBufferSizeInFrames;
31124 AudioObjectPropertyAddress propAddress;
31125 UInt32 dataSize;
31126 OSStatus status;
31127
31128 MA_ASSERT(pContext != NULL);
31129
31130 result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames);
31131 if (result != MA_SUCCESS) {
31132 return result;
31133 }
31134
31135 /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */
31136 propAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
31137 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
31138 propAddress.mElement = kAudioObjectPropertyElementMaster;
31139
31140 ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames);
31141
31142 /* Get the actual size of the buffer. */
31143 dataSize = sizeof(*pPeriodSizeInOut);
31144 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames);
31145 if (status != noErr) {
31146 return ma_result_from_OSStatus(status);
31147 }
31148
31149 *pPeriodSizeInOut = chosenBufferSizeInFrames;
31150 return MA_SUCCESS;
31151}
31152
31153static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID)
31154{
31155 AudioObjectPropertyAddress propAddressDefaultDevice;
31156 UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
31157 AudioObjectID defaultDeviceObjectID;
31158 OSStatus status;
31159
31160 MA_ASSERT(pContext != NULL);
31161 MA_ASSERT(pDeviceObjectID != NULL);
31162
31163 /* Safety. */
31164 *pDeviceObjectID = 0;
31165
31166 propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal;
31167 propAddressDefaultDevice.mElement = kAudioObjectPropertyElementMaster;
31168 if (deviceType == ma_device_type_playback) {
31169 propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
31170 } else {
31171 propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice;
31172 }
31173
31174 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
31175 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID);
31176 if (status == noErr) {
31177 *pDeviceObjectID = defaultDeviceObjectID;
31178 return MA_SUCCESS;
31179 }
31180
31181 /* If we get here it means we couldn't find the device. */
31182 return MA_NO_DEVICE;
31183}
31184
31185static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID)
31186{
31187 MA_ASSERT(pContext != NULL);
31188 MA_ASSERT(pDeviceObjectID != NULL);
31189
31190 /* Safety. */
31191 *pDeviceObjectID = 0;
31192
31193 if (pDeviceID == NULL) {
31194 /* Default device. */
31195 return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID);
31196 } else {
31197 /* Explicit device. */
31198 UInt32 deviceCount;
31199 AudioObjectID* pDeviceObjectIDs;
31200 ma_result result;
31201 UInt32 iDevice;
31202
31203 result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
31204 if (result != MA_SUCCESS) {
31205 return result;
31206 }
31207
31208 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
31209 AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
31210
31211 char uid[256];
31212 if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) {
31213 continue;
31214 }
31215
31216 if (deviceType == ma_device_type_playback) {
31217 if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
31218 if (strcmp(uid, pDeviceID->coreaudio) == 0) {
31219 *pDeviceObjectID = deviceObjectID;
31220 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
31221 return MA_SUCCESS;
31222 }
31223 }
31224 } else {
31225 if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
31226 if (strcmp(uid, pDeviceID->coreaudio) == 0) {
31227 *pDeviceObjectID = deviceObjectID;
31228 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
31229 return MA_SUCCESS;
31230 }
31231 }
31232 }
31233 }
31234
31235 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
31236 }
31237
31238 /* If we get here it means we couldn't find the device. */
31239 return MA_NO_DEVICE;
31240}
31241
31242
31243static 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)
31244{
31245 UInt32 deviceFormatDescriptionCount;
31246 AudioStreamRangedDescription* pDeviceFormatDescriptions;
31247 ma_result result;
31248 ma_uint32 desiredSampleRate;
31249 ma_uint32 desiredChannelCount;
31250 ma_format desiredFormat;
31251 AudioStreamBasicDescription bestDeviceFormatSoFar;
31252 ma_bool32 hasSupportedFormat;
31253 UInt32 iFormat;
31254
31255 result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions);
31256 if (result != MA_SUCCESS) {
31257 return result;
31258 }
31259
31260 desiredSampleRate = sampleRate;
31261 if (desiredSampleRate == 0) {
31262 desiredSampleRate = pOrigFormat->mSampleRate;
31263 }
31264
31265 desiredChannelCount = channels;
31266 if (desiredChannelCount == 0) {
31267 desiredChannelCount = pOrigFormat->mChannelsPerFrame;
31268 }
31269
31270 desiredFormat = format;
31271 if (desiredFormat == ma_format_unknown) {
31272 result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat);
31273 if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) {
31274 desiredFormat = g_maFormatPriorities[0];
31275 }
31276 }
31277
31278 /*
31279 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
31280 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.
31281 */
31282 MA_ZERO_OBJECT(&bestDeviceFormatSoFar);
31283
31284 hasSupportedFormat = MA_FALSE;
31285 for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
31286 ma_format format;
31287 ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &format);
31288 if (formatResult == MA_SUCCESS && format != ma_format_unknown) {
31289 hasSupportedFormat = MA_TRUE;
31290 bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat;
31291 break;
31292 }
31293 }
31294
31295 if (!hasSupportedFormat) {
31296 ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
31298 }
31299
31300
31301 for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
31302 AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat;
31303 ma_format thisSampleFormat;
31304 ma_result formatResult;
31305 ma_format bestSampleFormatSoFar;
31306
31307 /* If the format is not supported by miniaudio we need to skip this one entirely. */
31308 formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat);
31309 if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) {
31310 continue; /* The format is not supported by miniaudio. Skip. */
31311 }
31312
31313 ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar);
31314
31315 /* Getting here means the format is supported by miniaudio which makes this format a candidate. */
31316 if (thisDeviceFormat.mSampleRate != desiredSampleRate) {
31317 /*
31318 The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format
31319 so far has an equal sample rate we can just ignore this one.
31320 */
31321 if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) {
31322 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. */
31323 } else {
31324 /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */
31325 if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) {
31326 /* This format has a different sample rate _and_ a different channel count. */
31327 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
31328 continue; /* No change to the best format. */
31329 } else {
31330 /*
31331 Both this format and the best so far have different sample rates and different channel counts. Whichever has the
31332 best format is the new best.
31333 */
31334 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
31335 bestDeviceFormatSoFar = thisDeviceFormat;
31336 continue;
31337 } else {
31338 continue; /* No change to the best format. */
31339 }
31340 }
31341 } else {
31342 /* This format has a different sample rate but the desired channel count. */
31343 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
31344 /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */
31345 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
31346 bestDeviceFormatSoFar = thisDeviceFormat;
31347 continue;
31348 } else {
31349 continue; /* No change to the best format for now. */
31350 }
31351 } else {
31352 /* This format has the desired channel count, but the best so far does not. We have a new best. */
31353 bestDeviceFormatSoFar = thisDeviceFormat;
31354 continue;
31355 }
31356 }
31357 }
31358 } else {
31359 /*
31360 The sample rates match which makes this format a very high priority contender. If the best format so far has a different
31361 sample rate it needs to be replaced with this one.
31362 */
31363 if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) {
31364 bestDeviceFormatSoFar = thisDeviceFormat;
31365 continue;
31366 } else {
31367 /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */
31368 if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) {
31369 /*
31370 In this case this format has the same channel count as what the client is requesting. If the best format so far has
31371 a different count, this one becomes the new best.
31372 */
31373 if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) {
31374 bestDeviceFormatSoFar = thisDeviceFormat;
31375 continue;
31376 } else {
31377 /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */
31378 if (thisSampleFormat == desiredFormat) {
31379 bestDeviceFormatSoFar = thisDeviceFormat;
31380 break; /* Found the exact match. */
31381 } else {
31382 /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */
31383 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
31384 bestDeviceFormatSoFar = thisDeviceFormat;
31385 continue;
31386 } else {
31387 continue; /* No change to the best format for now. */
31388 }
31389 }
31390 }
31391 } else {
31392 /*
31393 In this case the channel count is different to what the client has requested. If the best so far has the same channel
31394 count as the requested count then it remains the best.
31395 */
31396 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
31397 continue;
31398 } else {
31399 /*
31400 This is the case where both have the same sample rate (good) but different channel counts. Right now both have about
31401 the same priority, but we need to compare the format now.
31402 */
31403 if (thisSampleFormat == bestSampleFormatSoFar) {
31404 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
31405 bestDeviceFormatSoFar = thisDeviceFormat;
31406 continue;
31407 } else {
31408 continue; /* No change to the best format for now. */
31409 }
31410 }
31411 }
31412 }
31413 }
31414 }
31415 }
31416
31417 *pFormat = bestDeviceFormatSoFar;
31418
31419 ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
31420 return MA_SUCCESS;
31421}
31422
31423static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
31424{
31425 AudioUnitScope deviceScope;
31426 AudioUnitElement deviceBus;
31427 UInt32 channelLayoutSize;
31428 OSStatus status;
31429 AudioChannelLayout* pChannelLayout;
31430 ma_result result;
31431
31432 MA_ASSERT(pContext != NULL);
31433
31434 if (deviceType == ma_device_type_playback) {
31435 deviceScope = kAudioUnitScope_Input;
31436 deviceBus = MA_COREAUDIO_OUTPUT_BUS;
31437 } else {
31438 deviceScope = kAudioUnitScope_Output;
31439 deviceBus = MA_COREAUDIO_INPUT_BUS;
31440 }
31441
31442 status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL);
31443 if (status != noErr) {
31444 return ma_result_from_OSStatus(status);
31445 }
31446
31447 pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks);
31448 if (pChannelLayout == NULL) {
31449 return MA_OUT_OF_MEMORY;
31450 }
31451
31452 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize);
31453 if (status != noErr) {
31454 ma_free(pChannelLayout, &pContext->allocationCallbacks);
31455 return ma_result_from_OSStatus(status);
31456 }
31457
31458 result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
31459 if (result != MA_SUCCESS) {
31460 ma_free(pChannelLayout, &pContext->allocationCallbacks);
31461 return result;
31462 }
31463
31464 ma_free(pChannelLayout, &pContext->allocationCallbacks);
31465 return MA_SUCCESS;
31466}
31467#endif /* MA_APPLE_DESKTOP */
31468
31469
31470#if !defined(MA_APPLE_DESKTOP)
31471static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo)
31472{
31473 MA_ZERO_OBJECT(pInfo);
31474 ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1);
31475 ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1);
31476}
31477#endif
31478
31479static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
31480{
31481#if defined(MA_APPLE_DESKTOP)
31482 UInt32 deviceCount;
31483 AudioObjectID* pDeviceObjectIDs;
31484 AudioObjectID defaultDeviceObjectIDPlayback;
31485 AudioObjectID defaultDeviceObjectIDCapture;
31486 ma_result result;
31487 UInt32 iDevice;
31488
31489 ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */
31490 ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */
31491
31492 result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
31493 if (result != MA_SUCCESS) {
31494 return result;
31495 }
31496
31497 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
31498 AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
31499 ma_device_info info;
31500
31501 MA_ZERO_OBJECT(&info);
31502 if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) {
31503 continue;
31504 }
31505 if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) {
31506 continue;
31507 }
31508
31509 if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
31510 if (deviceObjectID == defaultDeviceObjectIDPlayback) {
31511 info.isDefault = MA_TRUE;
31512 }
31513
31514 if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
31515 break;
31516 }
31517 }
31518 if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
31519 if (deviceObjectID == defaultDeviceObjectIDCapture) {
31520 info.isDefault = MA_TRUE;
31521 }
31522
31523 if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
31524 break;
31525 }
31526 }
31527 }
31528
31529 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
31530#else
31531 ma_device_info info;
31532 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
31533 NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
31534
31535 for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
31536 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
31537 if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
31538 return MA_SUCCESS;
31539 }
31540 }
31541
31542 for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
31543 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
31544 if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
31545 return MA_SUCCESS;
31546 }
31547 }
31548#endif
31549
31550 return MA_SUCCESS;
31551}
31552
31553static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
31554{
31555 ma_result result;
31556
31557 MA_ASSERT(pContext != NULL);
31558
31559#if defined(MA_APPLE_DESKTOP)
31560 /* Desktop */
31561 {
31562 AudioObjectID deviceObjectID;
31563 AudioObjectID defaultDeviceObjectID;
31564 UInt32 streamDescriptionCount;
31565 AudioStreamRangedDescription* pStreamDescriptions;
31566 UInt32 iStreamDescription;
31567 UInt32 sampleRateRangeCount;
31568 AudioValueRange* pSampleRateRanges;
31569
31570 ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */
31571
31572 result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
31573 if (result != MA_SUCCESS) {
31574 return result;
31575 }
31576
31577 result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio);
31578 if (result != MA_SUCCESS) {
31579 return result;
31580 }
31581
31582 result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name);
31583 if (result != MA_SUCCESS) {
31584 return result;
31585 }
31586
31587 if (deviceObjectID == defaultDeviceObjectID) {
31588 pDeviceInfo->isDefault = MA_TRUE;
31589 }
31590
31591 /*
31592 There could be a large number of permutations here. Fortunately there is only a single channel count
31593 being reported which reduces this quite a bit. For sample rates we're only reporting those that are
31594 one of miniaudio's recognized "standard" rates. If there are still more formats than can fit into
31595 our fixed sized array we'll just need to truncate them. This is unlikely and will probably only happen
31596 if some driver performs software data conversion and therefore reports every possible format and
31597 sample rate.
31598 */
31599 pDeviceInfo->nativeDataFormatCount = 0;
31600
31601 /* Formats. */
31602 {
31603 ma_format uniqueFormats[ma_format_count];
31604 ma_uint32 uniqueFormatCount = 0;
31605 ma_uint32 channels;
31606
31607 /* Channels. */
31608 result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels);
31609 if (result != MA_SUCCESS) {
31610 return result;
31611 }
31612
31613 /* Formats. */
31614 result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);
31615 if (result != MA_SUCCESS) {
31616 return result;
31617 }
31618
31619 for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) {
31620 ma_format format;
31621 ma_bool32 hasFormatBeenHandled = MA_FALSE;
31622 ma_uint32 iOutputFormat;
31623 ma_uint32 iSampleRate;
31624
31625 result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format);
31626 if (result != MA_SUCCESS) {
31627 continue;
31628 }
31629
31630 MA_ASSERT(format != ma_format_unknown);
31631
31632 /* Make sure the format isn't already in the output list. */
31633 for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) {
31634 if (uniqueFormats[iOutputFormat] == format) {
31635 hasFormatBeenHandled = MA_TRUE;
31636 break;
31637 }
31638 }
31639
31640 /* If we've already handled this format just skip it. */
31641 if (hasFormatBeenHandled) {
31642 continue;
31643 }
31644
31645 uniqueFormats[uniqueFormatCount] = format;
31646 uniqueFormatCount += 1;
31647
31648 /* Sample Rates */
31649 result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
31650 if (result != MA_SUCCESS) {
31651 return result;
31652 }
31653
31654 /*
31655 Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are
31656 between this range.
31657 */
31658 for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) {
31659 ma_uint32 iStandardSampleRate;
31660 for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {
31661 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];
31662 if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) {
31663 /* We have a new data format. Add it to the list. */
31664 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
31665 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
31666 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate;
31667 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
31668 pDeviceInfo->nativeDataFormatCount += 1;
31669
31670 if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
31671 break; /* No more room for any more formats. */
31672 }
31673 }
31674 }
31675 }
31676
31677 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
31678
31679 if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
31680 break; /* No more room for any more formats. */
31681 }
31682 }
31683
31684 ma_free(pStreamDescriptions, &pContext->allocationCallbacks);
31685 }
31686 }
31687#else
31688 /* Mobile */
31689 {
31690 AudioComponentDescription desc;
31691 AudioComponent component;
31692 AudioUnit audioUnit;
31693 OSStatus status;
31694 AudioUnitScope formatScope;
31695 AudioUnitElement formatElement;
31696 AudioStreamBasicDescription bestFormat;
31697 UInt32 propSize;
31698
31699 /* We want to ensure we use a consistent device name to device enumeration. */
31700 if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') {
31701 ma_bool32 found = MA_FALSE;
31702 if (deviceType == ma_device_type_playback) {
31703 NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
31704 for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
31705 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
31706 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
31707 found = MA_TRUE;
31708 break;
31709 }
31710 }
31711 } else {
31712 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
31713 for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
31714 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
31715 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
31716 found = MA_TRUE;
31717 break;
31718 }
31719 }
31720 }
31721
31722 if (!found) {
31723 return MA_DOES_NOT_EXIST;
31724 }
31725 } else {
31726 if (deviceType == ma_device_type_playback) {
31727 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
31728 } else {
31729 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
31730 }
31731 }
31732
31733
31734 /*
31735 Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is
31736 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
31737 retrieve from the AVAudioSession shared instance.
31738 */
31739 desc.componentType = kAudioUnitType_Output;
31740 desc.componentSubType = kAudioUnitSubType_RemoteIO;
31741 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
31742 desc.componentFlags = 0;
31743 desc.componentFlagsMask = 0;
31744
31745 component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
31746 if (component == NULL) {
31748 }
31749
31750 status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit);
31751 if (status != noErr) {
31752 return ma_result_from_OSStatus(status);
31753 }
31754
31755 formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
31756 formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
31757
31758 propSize = sizeof(bestFormat);
31759 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
31760 if (status != noErr) {
31761 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
31762 return ma_result_from_OSStatus(status);
31763 }
31764
31765 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
31766 audioUnit = NULL;
31767
31768 /* Only a single format is being reported for iOS. */
31769 pDeviceInfo->nativeDataFormatCount = 1;
31770
31771 result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format);
31772 if (result != MA_SUCCESS) {
31773 return result;
31774 }
31775
31776 pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame;
31777
31778 /*
31779 It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do
31780 this we just get the shared instance and inspect.
31781 */
31782 @autoreleasepool {
31783 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
31784 MA_ASSERT(pAudioSession != NULL);
31785
31786 pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate;
31787 }
31788 }
31789#endif
31790
31791 (void)pDeviceInfo; /* Unused. */
31792 return MA_SUCCESS;
31793}
31794
31795static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks)
31796{
31797 AudioBufferList* pBufferList;
31798 UInt32 audioBufferSizeInBytes;
31799 size_t allocationSize;
31800
31801 MA_ASSERT(sizeInFrames > 0);
31802 MA_ASSERT(format != ma_format_unknown);
31803 MA_ASSERT(channels > 0);
31804
31805 allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */
31806 if (layout == ma_stream_layout_interleaved) {
31807 /* Interleaved case. This is the simple case because we just have one buffer. */
31808 allocationSize += sizeof(AudioBuffer) * 1;
31809 } else {
31810 /* Non-interleaved case. This is the more complex case because there's more than one buffer. */
31811 allocationSize += sizeof(AudioBuffer) * channels;
31812 }
31813
31814 allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels);
31815
31816 pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks);
31817 if (pBufferList == NULL) {
31818 return NULL;
31819 }
31820
31821 audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format));
31822
31823 if (layout == ma_stream_layout_interleaved) {
31824 pBufferList->mNumberBuffers = 1;
31825 pBufferList->mBuffers[0].mNumberChannels = channels;
31826 pBufferList->mBuffers[0].mDataByteSize = audioBufferSizeInBytes * channels;
31827 pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList);
31828 } else {
31829 ma_uint32 iBuffer;
31830 pBufferList->mNumberBuffers = channels;
31831 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
31832 pBufferList->mBuffers[iBuffer].mNumberChannels = 1;
31833 pBufferList->mBuffers[iBuffer].mDataByteSize = audioBufferSizeInBytes;
31834 pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer);
31835 }
31836 }
31837
31838 return pBufferList;
31839}
31840
31841static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout)
31842{
31843 MA_ASSERT(pDevice != NULL);
31844 MA_ASSERT(format != ma_format_unknown);
31845 MA_ASSERT(channels > 0);
31846
31847 /* Only resize the buffer if necessary. */
31848 if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) {
31849 AudioBufferList* pNewAudioBufferList;
31850
31851 pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks);
31852 if (pNewAudioBufferList == NULL) {
31853 return MA_OUT_OF_MEMORY;
31854 }
31855
31856 /* At this point we'll have a new AudioBufferList and we can free the old one. */
31857 ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
31858 pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList;
31859 pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames;
31860 }
31861
31862 /* Getting here means the capacity of the audio is fine. */
31863 return MA_SUCCESS;
31864}
31865
31866
31867static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList)
31868{
31869 ma_device* pDevice = (ma_device*)pUserData;
31870 ma_stream_layout layout;
31871
31872 MA_ASSERT(pDevice != NULL);
31873
31874 /*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);*/
31875
31876 /* 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. */
31878 if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) {
31880 }
31881
31882 if (layout == ma_stream_layout_interleaved) {
31883 /* For now we can assume everything is interleaved. */
31884 UInt32 iBuffer;
31885 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
31886 if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) {
31887 ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
31888 if (frameCountForThisBuffer > 0) {
31889 ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer);
31890 }
31891
31892 /*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);*/
31893 } else {
31894 /*
31895 This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
31896 not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just
31897 output silence here.
31898 */
31899 MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
31900 /*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);*/
31901 }
31902 }
31903 } else {
31904 /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */
31905 MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should heve been validated at initialization time. */
31906
31907 /*
31908 For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
31909 very strange has happened and we're not going to support it.
31910 */
31911 if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) {
31912 ma_uint8 tempBuffer[4096];
31913 UInt32 iBuffer;
31914
31915 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) {
31916 ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat);
31917 ma_uint32 framesRemaining = frameCountPerBuffer;
31918
31919 while (framesRemaining > 0) {
31920 void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
31921 ma_uint32 iChannel;
31922 ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
31923 if (framesToRead > framesRemaining) {
31924 framesToRead = framesRemaining;
31925 }
31926
31927 ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead);
31928
31929 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
31930 ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
31931 }
31932
31933 ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
31934
31935 framesRemaining -= framesToRead;
31936 }
31937 }
31938 }
31939 }
31940
31941 (void)pActionFlags;
31942 (void)pTimeStamp;
31943 (void)busNumber;
31944 (void)frameCount;
31945
31946 return noErr;
31947}
31948
31949static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList)
31950{
31951 ma_device* pDevice = (ma_device*)pUserData;
31952 AudioBufferList* pRenderedBufferList;
31953 ma_result result;
31954 ma_stream_layout layout;
31955 ma_uint32 iBuffer;
31956 OSStatus status;
31957
31958 MA_ASSERT(pDevice != NULL);
31959
31960 pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
31961 MA_ASSERT(pRenderedBufferList);
31962
31963 /* 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. */
31965 if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) {
31967 }
31968
31969 /*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);*/
31970
31971 /*
31972 There has been a situation reported where frame count passed into this function is greater than the capacity of
31973 our capture buffer. There doesn't seem to be a reliable way to determine what the maximum frame count will be,
31974 so we need to instead resort to dynamically reallocating our buffer to ensure it's large enough to capture the
31975 number of frames requested by this callback.
31976 */
31977 result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout);
31978 if (result != MA_SUCCESS) {
31979 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n");
31980 return noErr;
31981 }
31982
31983 pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
31984 MA_ASSERT(pRenderedBufferList);
31985
31986 /*
31987 When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes
31988 that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer
31989 being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a
31990 problem when a future call to this callback specifies a larger number of frames.
31991
31992 To work around this we need to explicitly set the size of each buffer to their respective size in bytes.
31993 */
31994 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
31995 pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels;
31996 }
31997
31998 status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList);
31999 if (status != noErr) {
32000 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " ERROR: AudioUnitRender() failed with %d.\n", (int)status);
32001 return status;
32002 }
32003
32004 if (layout == ma_stream_layout_interleaved) {
32005 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
32006 if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) {
32007 ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount);
32008 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d.\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/
32009 } else {
32010 /*
32011 This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
32012 not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams.
32013 */
32014 ma_uint8 silentBuffer[4096];
32015 ma_uint32 framesRemaining;
32016
32017 MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer));
32018
32019 framesRemaining = frameCount;
32020 while (framesRemaining > 0) {
32021 ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
32022 if (framesToSend > framesRemaining) {
32023 framesToSend = framesRemaining;
32024 }
32025
32026 ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend);
32027
32028 framesRemaining -= framesToSend;
32029 }
32030
32031 /*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);*/
32032 }
32033 }
32034 } else {
32035 /* 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. */
32036 MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */
32037
32038 /*
32039 For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
32040 very strange has happened and we're not going to support it.
32041 */
32042 if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) {
32043 ma_uint8 tempBuffer[4096];
32044 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) {
32045 ma_uint32 framesRemaining = frameCount;
32046 while (framesRemaining > 0) {
32047 void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
32048 ma_uint32 iChannel;
32049 ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
32050 if (framesToSend > framesRemaining) {
32051 framesToSend = framesRemaining;
32052 }
32053
32054 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
32055 ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
32056 }
32057
32058 ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);
32059 ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend);
32060
32061 framesRemaining -= framesToSend;
32062 }
32063 }
32064 }
32065 }
32066
32067 (void)pActionFlags;
32068 (void)pTimeStamp;
32069 (void)busNumber;
32070 (void)frameCount;
32071 (void)pUnusedBufferList;
32072
32073 return noErr;
32074}
32075
32076static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element)
32077{
32078 ma_device* pDevice = (ma_device*)pUserData;
32079 MA_ASSERT(pDevice != NULL);
32080
32081 /* Don't do anything if it looks like we're just reinitializing due to a device switch. */
32082 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
32083 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
32084 return;
32085 }
32086
32087 /*
32088 There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like
32089 AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit)
32090 can try waiting on the same lock. I'm going to try working around this by not calling any Core
32091 Audio APIs in the callback when the device has been stopped or uninitialized.
32092 */
32094 ma_device__on_notification_stopped(pDevice);
32095 } else {
32096 UInt32 isRunning;
32097 UInt32 isRunningSize = sizeof(isRunning);
32098 OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);
32099 if (status != noErr) {
32100 goto done; /* Don't really know what to do in this case... just ignore it, I suppose... */
32101 }
32102
32103 if (!isRunning) {
32104 /*
32105 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:
32106
32107 1) When the device is unplugged, this will be called _before_ the default device change notification.
32108 2) When the device is changed via the default device change notification, this will be called _after_ the switch.
32109
32110 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.
32111 */
32112 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) ||
32113 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) {
32114 /*
32115 It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device
32116 via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the
32117 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
32118 hasn't!).
32119 */
32120 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
32121 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
32122 goto done;
32123 }
32124
32125 /*
32126 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
32127 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
32128 likely be successful in switching to the new device.
32129
32130 TODO: Try to predict if Core Audio will switch devices. If not, the stopped callback needs to be posted.
32131 */
32132 goto done;
32133 }
32134
32135 /* Getting here means we need to stop the device. */
32136 ma_device__on_notification_stopped(pDevice);
32137 }
32138 }
32139
32140 (void)propertyID; /* Unused. */
32141
32142done:
32143 /* Always signal the stop event. It's possible for the "else" case to get hit which can happen during an interruption. */
32144 ma_event_signal(&pDevice->coreaudio.stopEvent);
32145}
32146
32147#if defined(MA_APPLE_DESKTOP)
32148static 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. */
32149static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0;
32150static ma_mutex g_DeviceTrackingMutex_CoreAudio;
32151static ma_device** g_ppTrackedDevices_CoreAudio = NULL;
32152static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0;
32153static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0;
32154
32155static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData)
32156{
32157 ma_device_type deviceType;
32158
32159 /* Not sure if I really need to check this, but it makes me feel better. */
32160 if (addressCount == 0) {
32161 return noErr;
32162 }
32163
32164 if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) {
32165 deviceType = ma_device_type_playback;
32166 } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) {
32167 deviceType = ma_device_type_capture;
32168 } else {
32169 return noErr; /* Should never hit this. */
32170 }
32171
32172 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
32173 {
32174 ma_uint32 iDevice;
32175 for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
32176 ma_result reinitResult;
32177 ma_device* pDevice;
32178
32179 pDevice = g_ppTrackedDevices_CoreAudio[iDevice];
32180 if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) {
32181 if (deviceType == ma_device_type_playback) {
32182 pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE;
32183 reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
32184 pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE;
32185 } else {
32186 pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE;
32187 reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
32188 pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE;
32189 }
32190
32191 if (reinitResult == MA_SUCCESS) {
32192 ma_device__post_init_setup(pDevice, deviceType);
32193
32194 /* Restart the device if required. If this fails we need to stop the device entirely. */
32196 OSStatus status;
32197 if (deviceType == ma_device_type_playback) {
32198 status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
32199 if (status != noErr) {
32200 if (pDevice->type == ma_device_type_duplex) {
32201 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
32202 }
32203 ma_device__set_state(pDevice, ma_device_state_stopped);
32204 }
32205 } else if (deviceType == ma_device_type_capture) {
32206 status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
32207 if (status != noErr) {
32208 if (pDevice->type == ma_device_type_duplex) {
32209 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
32210 }
32211 ma_device__set_state(pDevice, ma_device_state_stopped);
32212 }
32213 }
32214 }
32215
32216 ma_device__on_notification_rerouted(pDevice);
32217 }
32218 }
32219 }
32220 }
32221 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
32222
32223 /* Unused parameters. */
32224 (void)objectID;
32225 (void)pUserData;
32226
32227 return noErr;
32228}
32229
32230static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext)
32231{
32232 MA_ASSERT(pContext != NULL);
32233
32234 ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
32235 {
32236 /* Don't do anything if we've already initializd device tracking. */
32237 if (g_DeviceTrackingInitCounter_CoreAudio == 0) {
32238 AudioObjectPropertyAddress propAddress;
32239 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
32240 propAddress.mElement = kAudioObjectPropertyElementMaster;
32241
32242 ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio);
32243
32244 propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
32245 ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
32246
32247 propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
32248 ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
32249
32250 }
32251 g_DeviceTrackingInitCounter_CoreAudio += 1;
32252 }
32253 ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
32254
32255 return MA_SUCCESS;
32256}
32257
32258static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext)
32259{
32260 MA_ASSERT(pContext != NULL);
32261
32262 ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
32263 {
32264 if (g_DeviceTrackingInitCounter_CoreAudio > 0)
32265 g_DeviceTrackingInitCounter_CoreAudio -= 1;
32266
32267 if (g_DeviceTrackingInitCounter_CoreAudio == 0) {
32268 AudioObjectPropertyAddress propAddress;
32269 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
32270 propAddress.mElement = kAudioObjectPropertyElementMaster;
32271
32272 propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
32273 ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
32274
32275 propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
32276 ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
32277
32278 /* At this point there should be no tracked devices. If not there's an error somewhere. */
32279 if (g_ppTrackedDevices_CoreAudio != NULL) {
32280 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active.");
32281 ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
32282 return MA_INVALID_OPERATION;
32283 }
32284
32285 ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio);
32286 }
32287 }
32288 ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
32289
32290 return MA_SUCCESS;
32291}
32292
32293static ma_result ma_device__track__coreaudio(ma_device* pDevice)
32294{
32295 MA_ASSERT(pDevice != NULL);
32296
32297 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
32298 {
32299 /* Allocate memory if required. */
32300 if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) {
32301 ma_uint32 newCap;
32302 ma_device** ppNewDevices;
32303
32304 newCap = g_TrackedDeviceCap_CoreAudio * 2;
32305 if (newCap == 0) {
32306 newCap = 1;
32307 }
32308
32309 ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks);
32310 if (ppNewDevices == NULL) {
32311 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
32312 return MA_OUT_OF_MEMORY;
32313 }
32314
32315 g_ppTrackedDevices_CoreAudio = ppNewDevices;
32316 g_TrackedDeviceCap_CoreAudio = newCap;
32317 }
32318
32319 g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice;
32320 g_TrackedDeviceCount_CoreAudio += 1;
32321 }
32322 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
32323
32324 return MA_SUCCESS;
32325}
32326
32327static ma_result ma_device__untrack__coreaudio(ma_device* pDevice)
32328{
32329 MA_ASSERT(pDevice != NULL);
32330
32331 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
32332 {
32333 ma_uint32 iDevice;
32334 for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
32335 if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) {
32336 /* We've found the device. We now need to remove it from the list. */
32337 ma_uint32 jDevice;
32338 for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) {
32339 g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1];
32340 }
32341
32342 g_TrackedDeviceCount_CoreAudio -= 1;
32343
32344 /* If there's nothing else in the list we need to free memory. */
32345 if (g_TrackedDeviceCount_CoreAudio == 0) {
32346 ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks);
32347 g_ppTrackedDevices_CoreAudio = NULL;
32348 g_TrackedDeviceCap_CoreAudio = 0;
32349 }
32350
32351 break;
32352 }
32353 }
32354 }
32355 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
32356
32357 return MA_SUCCESS;
32358}
32359#endif
32360
32361#if defined(MA_APPLE_MOBILE)
32362@interface ma_ios_notification_handler:NSObject {
32363 ma_device* m_pDevice;
32364}
32365@end
32366
32367@implementation ma_ios_notification_handler
32368-(id)init:(ma_device*)pDevice
32369{
32370 self = [super init];
32371 m_pDevice = pDevice;
32372
32373 /* For route changes. */
32374 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
32375
32376 /* For interruptions. */
32377 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
32378
32379 return self;
32380}
32381
32382-(void)dealloc
32383{
32384 [self remove_handler];
32385}
32386
32387-(void)remove_handler
32388{
32389 [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
32390 [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil];
32391}
32392
32393-(void)handle_interruption:(NSNotification*)pNotification
32394{
32395 NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue];
32396 switch (type)
32397 {
32398 case AVAudioSessionInterruptionTypeBegan:
32399 {
32400 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n");
32401
32402 /*
32403 Core Audio will have stopped the internal device automatically, but we need explicitly
32404 stop it at a higher level to ensure miniaudio-specific state is updated for consistency.
32405 */
32406 ma_device_stop(m_pDevice);
32407
32408 /*
32409 Fire the notification after the device has been stopped to ensure it's in the correct
32410 state when the notification handler is invoked.
32411 */
32412 ma_device__on_notification_interruption_began(m_pDevice);
32413 } break;
32414
32415 case AVAudioSessionInterruptionTypeEnded:
32416 {
32417 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n");
32418 ma_device__on_notification_interruption_ended(m_pDevice);
32419 } break;
32420 }
32421}
32422
32423-(void)handle_route_change:(NSNotification*)pNotification
32424{
32425 AVAudioSession* pSession = [AVAudioSession sharedInstance];
32426
32427 NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
32428 switch (reason)
32429 {
32430 case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
32431 {
32432 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n");
32433 } break;
32434
32435 case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
32436 {
32437 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n");
32438 } break;
32439
32440 case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
32441 {
32442 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n");
32443 } break;
32444
32445 case AVAudioSessionRouteChangeReasonWakeFromSleep:
32446 {
32447 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n");
32448 } break;
32449
32450 case AVAudioSessionRouteChangeReasonOverride:
32451 {
32452 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n");
32453 } break;
32454
32455 case AVAudioSessionRouteChangeReasonCategoryChange:
32456 {
32457 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n");
32458 } break;
32459
32460 case AVAudioSessionRouteChangeReasonUnknown:
32461 default:
32462 {
32463 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n");
32464 } break;
32465 }
32466
32467 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);
32468
32469 /* Let the application know about the route change. */
32470 ma_device__on_notification_rerouted(m_pDevice);
32471}
32472@end
32473#endif
32474
32475static ma_result ma_device_uninit__coreaudio(ma_device* pDevice)
32476{
32477 MA_ASSERT(pDevice != NULL);
32479
32480#if defined(MA_APPLE_DESKTOP)
32481 /*
32482 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
32483 just gracefully ignore it.
32484 */
32485 ma_device__untrack__coreaudio(pDevice);
32486#endif
32487#if defined(MA_APPLE_MOBILE)
32488 if (pDevice->coreaudio.pNotificationHandler != NULL) {
32489 ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler;
32490 [pNotificationHandler remove_handler];
32491 }
32492#endif
32493
32494 if (pDevice->coreaudio.audioUnitCapture != NULL) {
32495 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
32496 }
32497 if (pDevice->coreaudio.audioUnitPlayback != NULL) {
32498 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
32499 }
32500
32501 if (pDevice->coreaudio.pAudioBufferList) {
32502 ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
32503 }
32504
32505 return MA_SUCCESS;
32506}
32507
32508typedef struct
32509{
32510 ma_bool32 allowNominalSampleRateChange;
32511
32512 /* Input. */
32513 ma_format formatIn;
32514 ma_uint32 channelsIn;
32515 ma_uint32 sampleRateIn;
32516 ma_channel channelMapIn[MA_MAX_CHANNELS];
32517 ma_uint32 periodSizeInFramesIn;
32518 ma_uint32 periodSizeInMillisecondsIn;
32519 ma_uint32 periodsIn;
32520 ma_share_mode shareMode;
32521 ma_performance_profile performanceProfile;
32522 ma_bool32 registerStopEvent;
32523
32524 /* Output. */
32525#if defined(MA_APPLE_DESKTOP)
32526 AudioObjectID deviceObjectID;
32527#endif
32528 AudioComponent component;
32529 AudioUnit audioUnit;
32530 AudioBufferList* pAudioBufferList; /* Only used for input devices. */
32531 ma_format formatOut;
32532 ma_uint32 channelsOut;
32533 ma_uint32 sampleRateOut;
32534 ma_channel channelMapOut[MA_MAX_CHANNELS];
32535 ma_uint32 periodSizeInFramesOut;
32536 ma_uint32 periodsOut;
32537 char deviceName[256];
32538} ma_device_init_internal_data__coreaudio;
32539
32540static 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. */
32541{
32542 ma_result result;
32543 OSStatus status;
32544 UInt32 enableIOFlag;
32545 AudioStreamBasicDescription bestFormat;
32546 UInt32 actualPeriodSizeInFrames;
32547 AURenderCallbackStruct callbackInfo;
32548#if defined(MA_APPLE_DESKTOP)
32549 AudioObjectID deviceObjectID;
32550#endif
32551
32552 /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */
32553 if (deviceType == ma_device_type_duplex) {
32554 return MA_INVALID_ARGS;
32555 }
32556
32557 MA_ASSERT(pContext != NULL);
32558 MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture);
32559
32560#if defined(MA_APPLE_DESKTOP)
32561 pData->deviceObjectID = 0;
32562#endif
32563 pData->component = NULL;
32564 pData->audioUnit = NULL;
32565 pData->pAudioBufferList = NULL;
32566
32567#if defined(MA_APPLE_DESKTOP)
32568 result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
32569 if (result != MA_SUCCESS) {
32570 return result;
32571 }
32572
32573 pData->deviceObjectID = deviceObjectID;
32574#endif
32575
32576 /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */
32577 pData->periodsOut = pData->periodsIn;
32578 if (pData->periodsOut == 0) {
32579 pData->periodsOut = MA_DEFAULT_PERIODS;
32580 }
32581 if (pData->periodsOut > 16) {
32582 pData->periodsOut = 16;
32583 }
32584
32585
32586 /* Audio unit. */
32587 status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit);
32588 if (status != noErr) {
32589 return ma_result_from_OSStatus(status);
32590 }
32591
32592
32593 /* 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. */
32594 enableIOFlag = 1;
32595 if (deviceType == ma_device_type_capture) {
32596 enableIOFlag = 0;
32597 }
32598
32599 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
32600 if (status != noErr) {
32601 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
32602 return ma_result_from_OSStatus(status);
32603 }
32604
32605 enableIOFlag = (enableIOFlag == 0) ? 1 : 0;
32606 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
32607 if (status != noErr) {
32608 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
32609 return ma_result_from_OSStatus(status);
32610 }
32611
32612
32613 /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */
32614#if defined(MA_APPLE_DESKTOP)
32615 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID));
32616 if (status != noErr) {
32617 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
32618 return ma_result_from_OSStatus(result);
32619 }
32620#else
32621 /*
32622 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
32623 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.
32624 */
32625 if (pDeviceID != NULL) {
32626 if (deviceType == ma_device_type_capture) {
32627 ma_bool32 found = MA_FALSE;
32628 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
32629 for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
32630 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
32631 [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil];
32632 found = MA_TRUE;
32633 break;
32634 }
32635 }
32636
32637 if (found == MA_FALSE) {
32638 return MA_DOES_NOT_EXIST;
32639 }
32640 }
32641 }
32642#endif
32643
32644 /*
32645 Format. This is the hardest part of initialization because there's a few variables to take into account.
32646 1) The format must be supported by the device.
32647 2) The format must be supported miniaudio.
32648 3) There's a priority that miniaudio prefers.
32649
32650 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
32651 most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same
32652 for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely.
32653
32654 On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to.
32655 */
32656 {
32657 AudioStreamBasicDescription origFormat;
32658 UInt32 origFormatSize = sizeof(origFormat);
32659 AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
32660 AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
32661
32662 if (deviceType == ma_device_type_playback) {
32663 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize);
32664 } else {
32665 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize);
32666 }
32667 if (status != noErr) {
32668 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
32669 return ma_result_from_OSStatus(status);
32670 }
32671
32672 #if defined(MA_APPLE_DESKTOP)
32673 result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat);
32674 if (result != MA_SUCCESS) {
32675 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
32676 return result;
32677 }
32678
32679 /*
32680 Technical Note TN2091: Device input using the HAL Output Audio Unit
32681 https://developer.apple.com/library/archive/technotes/tn2091/_index.html
32682
32683 This documentation says the following:
32684
32685 The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY
32686 variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate
32687 conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with
32688 another AudioConverter.
32689
32690 The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We
32691 therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it
32692 safe and apply the same rule to output as well.
32693
32694 I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender()
32695 returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but
32696 this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format.
32697
32698 Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with
32699 this, however, is that it actually changes the sample rate at the operating system level and not just the application. This
32700 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
32701 configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample
32702 rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run
32703 the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is
32704 changed by miniaudio.
32705 */
32706 if (pData->allowNominalSampleRateChange) {
32707 AudioValueRange sampleRateRange;
32708 AudioObjectPropertyAddress propAddress;
32709
32710 sampleRateRange.mMinimum = bestFormat.mSampleRate;
32711 sampleRateRange.mMaximum = bestFormat.mSampleRate;
32712
32713 propAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
32714 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
32715 propAddress.mElement = kAudioObjectPropertyElementMaster;
32716
32717 status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange);
32718 if (status != noErr) {
32719 bestFormat.mSampleRate = origFormat.mSampleRate;
32720 }
32721 } else {
32722 bestFormat.mSampleRate = origFormat.mSampleRate;
32723 }
32724
32725 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
32726 if (status != noErr) {
32727 /* We failed to set the format, so fall back to the current format of the audio unit. */
32728 bestFormat = origFormat;
32729 }
32730 #else
32731 bestFormat = origFormat;
32732
32733 /*
32734 Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try
32735 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
32736 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
32737 can tell, it looks like the sample rate is shared between playback and capture for everything.
32738 */
32739 @autoreleasepool {
32740 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
32741 MA_ASSERT(pAudioSession != NULL);
32742
32743 [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil];
32744 bestFormat.mSampleRate = pAudioSession.sampleRate;
32745
32746 /*
32747 I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with
32748 AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead.
32749 */
32750 if (deviceType == ma_device_type_playback) {
32751 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels;
32752 }
32753 if (deviceType == ma_device_type_capture) {
32754 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels;
32755 }
32756 }
32757
32758 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
32759 if (status != noErr) {
32760 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
32761 return ma_result_from_OSStatus(status);
32762 }
32763 #endif
32764
32765 result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut);
32766 if (result != MA_SUCCESS) {
32767 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
32768 return result;
32769 }
32770
32771 if (pData->formatOut == ma_format_unknown) {
32772 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
32774 }
32775
32776 pData->channelsOut = bestFormat.mChannelsPerFrame;
32777 pData->sampleRateOut = bestFormat.mSampleRate;
32778 }
32779
32780 /* Clamp the channel count for safety. */
32781 if (pData->channelsOut > MA_MAX_CHANNELS) {
32782 pData->channelsOut = MA_MAX_CHANNELS;
32783 }
32784
32785 /*
32786 Internal channel map. This is weird in my testing. If I use the AudioObject to get the
32787 channel map, the channel descriptions are set to "Unknown" for some reason. To work around
32788 this it looks like retrieving it from the AudioUnit will work. However, and this is where
32789 it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore
32790 I'm going to fall back to a default assumption in these cases.
32791 */
32792#if defined(MA_APPLE_DESKTOP)
32793 result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut);
32794 if (result != MA_SUCCESS) {
32795 #if 0
32796 /* Try falling back to the channel map from the AudioObject. */
32797 result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut);
32798 if (result != MA_SUCCESS) {
32799 return result;
32800 }
32801 #else
32802 /* Fall back to default assumptions. */
32803 ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);
32804 #endif
32805 }
32806#else
32807 /* TODO: Figure out how to get the channel map using AVAudioSession. */
32808 ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);
32809#endif
32810
32811
32812 /* Buffer size. Not allowing this to be configurable on iOS. */
32813 if (pData->periodSizeInFramesIn == 0) {
32814 if (pData->periodSizeInMillisecondsIn == 0) {
32815 if (pData->performanceProfile == ma_performance_profile_low_latency) {
32816 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut);
32817 } else {
32818 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut);
32819 }
32820 } else {
32821 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut);
32822 }
32823 } else {
32824 actualPeriodSizeInFrames = pData->periodSizeInFramesIn;
32825 }
32826
32827#if defined(MA_APPLE_DESKTOP)
32828 result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames);
32829 if (result != MA_SUCCESS) {
32830 return result;
32831 }
32832#else
32833 /*
32834 On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point
32835 number. I don't trust any potential truncation errors due to converting from float to integer
32836 so I'm going to explicitly set the actual period size to the next power of 2.
32837 */
32838 @autoreleasepool {
32839 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
32840 MA_ASSERT(pAudioSession != NULL);
32841
32842 [pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil];
32843 actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate));
32844 }
32845#endif
32846
32847
32848 /*
32849 During testing I discovered that the buffer size can be too big. You'll get an error like this:
32850
32851 kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512
32852
32853 Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that
32854 of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice.
32855 */
32856 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames));
32857 if (status != noErr) {
32858 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
32859 return ma_result_from_OSStatus(status);
32860 }
32861
32862 pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames;
32863
32864 /* We need a buffer list if this is an input device. We render into this in the input callback. */
32865 if (deviceType == ma_device_type_capture) {
32866 ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
32867 AudioBufferList* pBufferList;
32868
32869 pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks);
32870 if (pBufferList == NULL) {
32871 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
32872 return MA_OUT_OF_MEMORY;
32873 }
32874
32875 pData->pAudioBufferList = pBufferList;
32876 }
32877
32878 /* Callbacks. */
32879 callbackInfo.inputProcRefCon = pDevice_DoNotReference;
32880 if (deviceType == ma_device_type_playback) {
32881 callbackInfo.inputProc = ma_on_output__coreaudio;
32882 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));
32883 if (status != noErr) {
32884 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
32885 return ma_result_from_OSStatus(status);
32886 }
32887 } else {
32888 callbackInfo.inputProc = ma_on_input__coreaudio;
32889 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));
32890 if (status != noErr) {
32891 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
32892 return ma_result_from_OSStatus(status);
32893 }
32894 }
32895
32896 /* We need to listen for stop events. */
32897 if (pData->registerStopEvent) {
32898 status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference);
32899 if (status != noErr) {
32900 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
32901 return ma_result_from_OSStatus(status);
32902 }
32903 }
32904
32905 /* Initialize the audio unit. */
32906 status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit);
32907 if (status != noErr) {
32908 ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks);
32909 pData->pAudioBufferList = NULL;
32910 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
32911 return ma_result_from_OSStatus(status);
32912 }
32913
32914 /* Grab the name. */
32915#if defined(MA_APPLE_DESKTOP)
32916 ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName);
32917#else
32918 if (deviceType == ma_device_type_playback) {
32919 ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
32920 } else {
32921 ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME);
32922 }
32923#endif
32924
32925 return result;
32926}
32927
32928#if defined(MA_APPLE_DESKTOP)
32929static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit)
32930{
32931 ma_device_init_internal_data__coreaudio data;
32932 ma_result result;
32933
32934 /* This should only be called for playback or capture, not duplex. */
32935 if (deviceType == ma_device_type_duplex) {
32936 return MA_INVALID_ARGS;
32937 }
32938
32939 data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */
32940
32941 if (deviceType == ma_device_type_capture) {
32942 data.formatIn = pDevice->capture.format;
32943 data.channelsIn = pDevice->capture.channels;
32944 data.sampleRateIn = pDevice->sampleRate;
32945 MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
32946 data.shareMode = pDevice->capture.shareMode;
32947 data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile;
32948 data.registerStopEvent = MA_TRUE;
32949
32950 if (disposePreviousAudioUnit) {
32951 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
32952 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
32953 }
32954 if (pDevice->coreaudio.pAudioBufferList) {
32955 ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
32956 }
32957 } else if (deviceType == ma_device_type_playback) {
32958 data.formatIn = pDevice->playback.format;
32959 data.channelsIn = pDevice->playback.channels;
32960 data.sampleRateIn = pDevice->sampleRate;
32961 MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
32962 data.shareMode = pDevice->playback.shareMode;
32963 data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile;
32964 data.registerStopEvent = (pDevice->type != ma_device_type_duplex);
32965
32966 if (disposePreviousAudioUnit) {
32967 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
32968 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
32969 }
32970 }
32971 data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames;
32972 data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds;
32973 data.periodsIn = pDevice->coreaudio.originalPeriods;
32974
32975 /* Need at least 3 periods for duplex. */
32976 if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) {
32977 data.periodsIn = 3;
32978 }
32979
32980 result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice);
32981 if (result != MA_SUCCESS) {
32982 return result;
32983 }
32984
32985 if (deviceType == ma_device_type_capture) {
32986 #if defined(MA_APPLE_DESKTOP)
32987 pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
32988 ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio);
32989 #endif
32990 pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
32991 pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
32992 pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut;
32993
32994 pDevice->capture.internalFormat = data.formatOut;
32995 pDevice->capture.internalChannels = data.channelsOut;
32996 pDevice->capture.internalSampleRate = data.sampleRateOut;
32997 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
32998 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
32999 pDevice->capture.internalPeriods = data.periodsOut;
33000 } else if (deviceType == ma_device_type_playback) {
33001 #if defined(MA_APPLE_DESKTOP)
33002 pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
33003 ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio);
33004 #endif
33005 pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
33006
33007 pDevice->playback.internalFormat = data.formatOut;
33008 pDevice->playback.internalChannels = data.channelsOut;
33009 pDevice->playback.internalSampleRate = data.sampleRateOut;
33010 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
33011 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
33012 pDevice->playback.internalPeriods = data.periodsOut;
33013 }
33014
33015 return MA_SUCCESS;
33016}
33017#endif /* MA_APPLE_DESKTOP */
33018
33019static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
33020{
33021 ma_result result;
33022
33023 MA_ASSERT(pDevice != NULL);
33024 MA_ASSERT(pConfig != NULL);
33025
33026 if (pConfig->deviceType == ma_device_type_loopback) {
33028 }
33029
33030 /* No exclusive mode with the Core Audio backend for now. */
33031 if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) ||
33032 ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) {
33034 }
33035
33036 /* Capture needs to be initialized first. */
33037 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
33038 ma_device_init_internal_data__coreaudio data;
33039 data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange;
33040 data.formatIn = pDescriptorCapture->format;
33041 data.channelsIn = pDescriptorCapture->channels;
33042 data.sampleRateIn = pDescriptorCapture->sampleRate;
33043 MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
33044 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
33045 data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds;
33046 data.periodsIn = pDescriptorCapture->periodCount;
33047 data.shareMode = pDescriptorCapture->shareMode;
33048 data.performanceProfile = pConfig->performanceProfile;
33049 data.registerStopEvent = MA_TRUE;
33050
33051 /* Need at least 3 periods for duplex. */
33052 if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) {
33053 data.periodsIn = 3;
33054 }
33055
33056 result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice);
33057 if (result != MA_SUCCESS) {
33058 return result;
33059 }
33060
33061 pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL);
33062 #if defined(MA_APPLE_DESKTOP)
33063 pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
33064 #endif
33065 pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
33066 pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
33067 pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut;
33068 pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
33069 pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;
33070 pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount;
33071 pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile;
33072
33073 pDescriptorCapture->format = data.formatOut;
33074 pDescriptorCapture->channels = data.channelsOut;
33075 pDescriptorCapture->sampleRate = data.sampleRateOut;
33076 MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
33077 pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut;
33078 pDescriptorCapture->periodCount = data.periodsOut;
33079
33080 #if defined(MA_APPLE_DESKTOP)
33081 ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio);
33082
33083 /*
33084 If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
33085 switch the device in the background.
33086 */
33087 if (pConfig->capture.pDeviceID == NULL) {
33088 ma_device__track__coreaudio(pDevice);
33089 }
33090 #endif
33091 }
33092
33093 /* Playback. */
33094 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
33095 ma_device_init_internal_data__coreaudio data;
33096 data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange;
33097 data.formatIn = pDescriptorPlayback->format;
33098 data.channelsIn = pDescriptorPlayback->channels;
33099 data.sampleRateIn = pDescriptorPlayback->sampleRate;
33100 MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
33101 data.shareMode = pDescriptorPlayback->shareMode;
33102 data.performanceProfile = pConfig->performanceProfile;
33103
33104 /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */
33105 if (pConfig->deviceType == ma_device_type_duplex) {
33106 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
33107 data.periodsIn = pDescriptorCapture->periodCount;
33108 data.registerStopEvent = MA_FALSE;
33109 } else {
33110 data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames;
33111 data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
33112 data.periodsIn = pDescriptorPlayback->periodCount;
33113 data.registerStopEvent = MA_TRUE;
33114 }
33115
33116 result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice);
33117 if (result != MA_SUCCESS) {
33118 if (pConfig->deviceType == ma_device_type_duplex) {
33119 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
33120 if (pDevice->coreaudio.pAudioBufferList) {
33121 ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
33122 }
33123 }
33124 return result;
33125 }
33126
33127 pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL);
33128 #if defined(MA_APPLE_DESKTOP)
33129 pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
33130 #endif
33131 pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
33132 pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
33133 pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
33134 pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount;
33135 pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile;
33136
33137 pDescriptorPlayback->format = data.formatOut;
33138 pDescriptorPlayback->channels = data.channelsOut;
33139 pDescriptorPlayback->sampleRate = data.sampleRateOut;
33140 MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
33141 pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut;
33142 pDescriptorPlayback->periodCount = data.periodsOut;
33143
33144 #if defined(MA_APPLE_DESKTOP)
33145 ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio);
33146
33147 /*
33148 If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
33149 switch the device in the background.
33150 */
33151 if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) {
33152 ma_device__track__coreaudio(pDevice);
33153 }
33154 #endif
33155 }
33156
33157
33158
33159 /*
33160 When stopping the device, a callback is called on another thread. We need to wait for this callback
33161 before returning from ma_device_stop(). This event is used for this.
33162 */
33163 ma_event_init(&pDevice->coreaudio.stopEvent);
33164
33165 /*
33166 We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done
33167 differently on non-Desktop Apple platforms.
33168 */
33169#if defined(MA_APPLE_MOBILE)
33170 pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice];
33171#endif
33172
33173 return MA_SUCCESS;
33174}
33175
33176
33177static ma_result ma_device_start__coreaudio(ma_device* pDevice)
33178{
33179 MA_ASSERT(pDevice != NULL);
33180
33181 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
33182 OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
33183 if (status != noErr) {
33184 return ma_result_from_OSStatus(status);
33185 }
33186 }
33187
33188 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
33189 OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
33190 if (status != noErr) {
33191 if (pDevice->type == ma_device_type_duplex) {
33192 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
33193 }
33194 return ma_result_from_OSStatus(status);
33195 }
33196 }
33197
33198 return MA_SUCCESS;
33199}
33200
33201static ma_result ma_device_stop__coreaudio(ma_device* pDevice)
33202{
33203 MA_ASSERT(pDevice != NULL);
33204
33205 /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */
33206
33207 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
33208 OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
33209 if (status != noErr) {
33210 return ma_result_from_OSStatus(status);
33211 }
33212 }
33213
33214 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
33215 OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
33216 if (status != noErr) {
33217 return ma_result_from_OSStatus(status);
33218 }
33219 }
33220
33221 /* We need to wait for the callback to finish before returning. */
33222 ma_event_wait(&pDevice->coreaudio.stopEvent);
33223 return MA_SUCCESS;
33224}
33225
33226
33227static ma_result ma_context_uninit__coreaudio(ma_context* pContext)
33228{
33229 MA_ASSERT(pContext != NULL);
33230 MA_ASSERT(pContext->backend == ma_backend_coreaudio);
33231
33232#if defined(MA_APPLE_MOBILE)
33233 if (!pContext->coreaudio.noAudioSessionDeactivate) {
33234 if (![[AVAudioSession sharedInstance] setActive:false error:nil]) {
33235 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session.");
33237 }
33238 }
33239#endif
33240
33241#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
33242 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
33243 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
33244 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
33245#endif
33246
33247#if !defined(MA_APPLE_MOBILE)
33248 ma_context__uninit_device_tracking__coreaudio(pContext);
33249#endif
33250
33251 (void)pContext;
33252 return MA_SUCCESS;
33253}
33254
33255#if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0)
33256static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category)
33257{
33258 /* The "default" and "none" categories are treated different and should not be used as an input into this function. */
33259 MA_ASSERT(category != ma_ios_session_category_default);
33260 MA_ASSERT(category != ma_ios_session_category_none);
33261
33262 switch (category) {
33263 case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient;
33264 case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient;
33265 case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback;
33266 case ma_ios_session_category_record: return AVAudioSessionCategoryRecord;
33267 case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord;
33268 case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute;
33269 case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient;
33270 case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient;
33271 default: return AVAudioSessionCategoryAmbient;
33272 }
33273}
33274#endif
33275
33276static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
33277{
33278#if !defined(MA_APPLE_MOBILE)
33279 ma_result result;
33280#endif
33281
33282 MA_ASSERT(pConfig != NULL);
33283 MA_ASSERT(pContext != NULL);
33284
33285#if defined(MA_APPLE_MOBILE)
33286 @autoreleasepool {
33287 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
33288 AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions;
33289
33290 MA_ASSERT(pAudioSession != NULL);
33291
33293 /*
33294 I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails
33295 we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category.
33296 */
33297 #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH)
33298 options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
33299 #endif
33300
33301 if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) {
33302 /* Using PlayAndRecord */
33303 } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) {
33304 /* Using Playback */
33305 } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) {
33306 /* Using Record */
33307 } else {
33308 /* Leave as default? */
33309 }
33310 } else {
33312 #if defined(__IPHONE_12_0)
33313 if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) {
33314 return MA_INVALID_OPERATION; /* Failed to set session category. */
33315 }
33316 #else
33317 /* Ignore the session category on version 11 and older, but post a warning. */
33318 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer.");
33319 #endif
33320 }
33321 }
33322
33323 if (!pConfig->coreaudio.noAudioSessionActivate) {
33324 if (![pAudioSession setActive:true error:nil]) {
33325 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session.");
33327 }
33328 }
33329 }
33330#endif
33331
33332#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
33333 pContext->coreaudio.hCoreFoundation = ma_dlopen(pContext, "CoreFoundation.framework/CoreFoundation");
33334 if (pContext->coreaudio.hCoreFoundation == NULL) {
33335 return MA_API_NOT_FOUND;
33336 }
33337
33338 pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFStringGetCString");
33339 pContext->coreaudio.CFRelease = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFRelease");
33340
33341
33342 pContext->coreaudio.hCoreAudio = ma_dlopen(pContext, "CoreAudio.framework/CoreAudio");
33343 if (pContext->coreaudio.hCoreAudio == NULL) {
33344 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
33345 return MA_API_NOT_FOUND;
33346 }
33347
33348 pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData");
33349 pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize");
33350 pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData");
33351 pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener");
33352 pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener");
33353
33354 /*
33355 It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still
33356 defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback.
33357 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
33358 AudioToolbox.
33359 */
33360 pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioUnit.framework/AudioUnit");
33361 if (pContext->coreaudio.hAudioUnit == NULL) {
33362 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
33363 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
33364 return MA_API_NOT_FOUND;
33365 }
33366
33367 if (ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) {
33368 /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */
33369 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
33370 pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioToolbox.framework/AudioToolbox");
33371 if (pContext->coreaudio.hAudioUnit == NULL) {
33372 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
33373 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
33374 return MA_API_NOT_FOUND;
33375 }
33376 }
33377
33378 pContext->coreaudio.AudioComponentFindNext = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext");
33379 pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose");
33380 pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew");
33381 pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart");
33382 pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop");
33383 pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener");
33384 pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo");
33385 pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty");
33386 pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty");
33387 pContext->coreaudio.AudioUnitInitialize = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitInitialize");
33388 pContext->coreaudio.AudioUnitRender = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitRender");
33389#else
33390 pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString;
33391 pContext->coreaudio.CFRelease = (ma_proc)CFRelease;
33392
33393 #if defined(MA_APPLE_DESKTOP)
33394 pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData;
33395 pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize;
33396 pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData;
33397 pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener;
33398 pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener;
33399 #endif
33400
33401 pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext;
33402 pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose;
33403 pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew;
33404 pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart;
33405 pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop;
33406 pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener;
33407 pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo;
33408 pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty;
33409 pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty;
33410 pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize;
33411 pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender;
33412#endif
33413
33414 /* Audio component. */
33415 {
33416 AudioComponentDescription desc;
33417 desc.componentType = kAudioUnitType_Output;
33418 #if defined(MA_APPLE_DESKTOP)
33419 desc.componentSubType = kAudioUnitSubType_HALOutput;
33420 #else
33421 desc.componentSubType = kAudioUnitSubType_RemoteIO;
33422 #endif
33423 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
33424 desc.componentFlags = 0;
33425 desc.componentFlagsMask = 0;
33426
33427 pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
33428 if (pContext->coreaudio.component == NULL) {
33429 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
33430 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
33431 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
33432 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
33433 #endif
33435 }
33436 }
33437
33438#if !defined(MA_APPLE_MOBILE)
33439 result = ma_context__init_device_tracking__coreaudio(pContext);
33440 if (result != MA_SUCCESS) {
33441 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
33442 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
33443 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
33444 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
33445 #endif
33446 return result;
33447 }
33448#endif
33449
33450 pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate;
33451
33452 pCallbacks->onContextInit = ma_context_init__coreaudio;
33453 pCallbacks->onContextUninit = ma_context_uninit__coreaudio;
33454 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio;
33455 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio;
33456 pCallbacks->onDeviceInit = ma_device_init__coreaudio;
33457 pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio;
33458 pCallbacks->onDeviceStart = ma_device_start__coreaudio;
33459 pCallbacks->onDeviceStop = ma_device_stop__coreaudio;
33460 pCallbacks->onDeviceRead = NULL;
33461 pCallbacks->onDeviceWrite = NULL;
33462 pCallbacks->onDeviceDataLoop = NULL;
33463
33464 return MA_SUCCESS;
33465}
33466#endif /* Core Audio */
33467
33468
33469
33470
33475#ifdef MA_HAS_SNDIO
33476#include <fcntl.h>
33477
33478/*
33479Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due
33480to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device
33481just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's
33482demand for it or if I can get it tested and debugged more thoroughly.
33483*/
33484#if 0
33485#if defined(__NetBSD__) || defined(__OpenBSD__)
33486#include <sys/audioio.h>
33487#endif
33488#if defined(__FreeBSD__) || defined(__DragonFly__)
33489#include <sys/soundcard.h>
33490#endif
33491#endif
33492
33493#define MA_SIO_DEVANY "default"
33494#define MA_SIO_PLAY 1
33495#define MA_SIO_REC 2
33496#define MA_SIO_NENC 8
33497#define MA_SIO_NCHAN 8
33498#define MA_SIO_NRATE 16
33499#define MA_SIO_NCONF 4
33500
33501struct ma_sio_hdl; /* <-- Opaque */
33502
33503struct ma_sio_par
33504{
33505 unsigned int bits;
33506 unsigned int bps;
33507 unsigned int sig;
33508 unsigned int le;
33509 unsigned int msb;
33510 unsigned int rchan;
33511 unsigned int pchan;
33512 unsigned int rate;
33513 unsigned int bufsz;
33514 unsigned int xrun;
33515 unsigned int round;
33516 unsigned int appbufsz;
33517 int __pad[3];
33518 unsigned int __magic;
33519};
33520
33521struct ma_sio_enc
33522{
33523 unsigned int bits;
33524 unsigned int bps;
33525 unsigned int sig;
33526 unsigned int le;
33527 unsigned int msb;
33528};
33529
33530struct ma_sio_conf
33531{
33532 unsigned int enc;
33533 unsigned int rchan;
33534 unsigned int pchan;
33535 unsigned int rate;
33536};
33537
33538struct ma_sio_cap
33539{
33540 struct ma_sio_enc enc[MA_SIO_NENC];
33541 unsigned int rchan[MA_SIO_NCHAN];
33542 unsigned int pchan[MA_SIO_NCHAN];
33543 unsigned int rate[MA_SIO_NRATE];
33544 int __pad[7];
33545 unsigned int nconf;
33546 struct ma_sio_conf confs[MA_SIO_NCONF];
33547};
33548
33549typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int);
33550typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*);
33551typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
33552typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
33553typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*);
33554typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t);
33555typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t);
33556typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*);
33557typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*);
33558typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*);
33559
33560static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */
33561{
33562 ma_uint32 i;
33563 for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) {
33564 if (g_maStandardSampleRatePriorities[i] == sampleRate) {
33565 return i;
33566 }
33567 }
33568
33569 return (ma_uint32)-1;
33570}
33571
33572static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb)
33573{
33574 /* We only support native-endian right now. */
33575 if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) {
33576 return ma_format_unknown;
33577 }
33578
33579 if (bits == 8 && bps == 1 && sig == 0) {
33580 return ma_format_u8;
33581 }
33582 if (bits == 16 && bps == 2 && sig == 1) {
33583 return ma_format_s16;
33584 }
33585 if (bits == 24 && bps == 3 && sig == 1) {
33586 return ma_format_s24;
33587 }
33588 if (bits == 24 && bps == 4 && sig == 1 && msb == 0) {
33589 /*return ma_format_s24_32;*/
33590 }
33591 if (bits == 32 && bps == 4 && sig == 1) {
33592 return ma_format_s32;
33593 }
33594
33595 return ma_format_unknown;
33596}
33597
33598static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps)
33599{
33600 ma_format bestFormat;
33601 unsigned int iConfig;
33602
33603 MA_ASSERT(caps != NULL);
33604
33605 bestFormat = ma_format_unknown;
33606 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
33607 unsigned int iEncoding;
33608 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
33609 unsigned int bits;
33610 unsigned int bps;
33611 unsigned int sig;
33612 unsigned int le;
33613 unsigned int msb;
33614 ma_format format;
33615
33616 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
33617 continue;
33618 }
33619
33620 bits = caps->enc[iEncoding].bits;
33621 bps = caps->enc[iEncoding].bps;
33622 sig = caps->enc[iEncoding].sig;
33623 le = caps->enc[iEncoding].le;
33624 msb = caps->enc[iEncoding].msb;
33625 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
33626 if (format == ma_format_unknown) {
33627 continue; /* Format not supported. */
33628 }
33629
33630 if (bestFormat == ma_format_unknown) {
33631 bestFormat = format;
33632 } else {
33633 if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */
33634 bestFormat = format;
33635 }
33636 }
33637 }
33638 }
33639
33640 return bestFormat;
33641}
33642
33643static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat)
33644{
33645 ma_uint32 maxChannels;
33646 unsigned int iConfig;
33647
33648 MA_ASSERT(caps != NULL);
33649 MA_ASSERT(requiredFormat != ma_format_unknown);
33650
33651 /* Just pick whatever configuration has the most channels. */
33652 maxChannels = 0;
33653 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
33654 /* The encoding should be of requiredFormat. */
33655 unsigned int iEncoding;
33656 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
33657 unsigned int iChannel;
33658 unsigned int bits;
33659 unsigned int bps;
33660 unsigned int sig;
33661 unsigned int le;
33662 unsigned int msb;
33663 ma_format format;
33664
33665 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
33666 continue;
33667 }
33668
33669 bits = caps->enc[iEncoding].bits;
33670 bps = caps->enc[iEncoding].bps;
33671 sig = caps->enc[iEncoding].sig;
33672 le = caps->enc[iEncoding].le;
33673 msb = caps->enc[iEncoding].msb;
33674 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
33675 if (format != requiredFormat) {
33676 continue;
33677 }
33678
33679 /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
33680 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
33681 unsigned int chan = 0;
33682 unsigned int channels;
33683
33684 if (deviceType == ma_device_type_playback) {
33685 chan = caps->confs[iConfig].pchan;
33686 } else {
33687 chan = caps->confs[iConfig].rchan;
33688 }
33689
33690 if ((chan & (1UL << iChannel)) == 0) {
33691 continue;
33692 }
33693
33694 if (deviceType == ma_device_type_playback) {
33695 channels = caps->pchan[iChannel];
33696 } else {
33697 channels = caps->rchan[iChannel];
33698 }
33699
33700 if (maxChannels < channels) {
33701 maxChannels = channels;
33702 }
33703 }
33704 }
33705 }
33706
33707 return maxChannels;
33708}
33709
33710static 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)
33711{
33712 ma_uint32 firstSampleRate;
33713 ma_uint32 bestSampleRate;
33714 unsigned int iConfig;
33715
33716 MA_ASSERT(caps != NULL);
33717 MA_ASSERT(requiredFormat != ma_format_unknown);
33718 MA_ASSERT(requiredChannels > 0);
33719 MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS);
33720
33721 firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */
33722 bestSampleRate = 0;
33723
33724 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
33725 /* The encoding should be of requiredFormat. */
33726 unsigned int iEncoding;
33727 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
33728 unsigned int iChannel;
33729 unsigned int bits;
33730 unsigned int bps;
33731 unsigned int sig;
33732 unsigned int le;
33733 unsigned int msb;
33734 ma_format format;
33735
33736 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
33737 continue;
33738 }
33739
33740 bits = caps->enc[iEncoding].bits;
33741 bps = caps->enc[iEncoding].bps;
33742 sig = caps->enc[iEncoding].sig;
33743 le = caps->enc[iEncoding].le;
33744 msb = caps->enc[iEncoding].msb;
33745 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
33746 if (format != requiredFormat) {
33747 continue;
33748 }
33749
33750 /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
33751 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
33752 unsigned int chan = 0;
33753 unsigned int channels;
33754 unsigned int iRate;
33755
33756 if (deviceType == ma_device_type_playback) {
33757 chan = caps->confs[iConfig].pchan;
33758 } else {
33759 chan = caps->confs[iConfig].rchan;
33760 }
33761
33762 if ((chan & (1UL << iChannel)) == 0) {
33763 continue;
33764 }
33765
33766 if (deviceType == ma_device_type_playback) {
33767 channels = caps->pchan[iChannel];
33768 } else {
33769 channels = caps->rchan[iChannel];
33770 }
33771
33772 if (channels != requiredChannels) {
33773 continue;
33774 }
33775
33776 /* Getting here means we have found a compatible encoding/channel pair. */
33777 for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
33778 ma_uint32 rate = (ma_uint32)caps->rate[iRate];
33779 ma_uint32 ratePriority;
33780
33781 if (firstSampleRate == 0) {
33782 firstSampleRate = rate;
33783 }
33784
33785 /* Disregard this rate if it's not a standard one. */
33786 ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate);
33787 if (ratePriority == (ma_uint32)-1) {
33788 continue;
33789 }
33790
33791 if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) { /* Lower = better. */
33792 bestSampleRate = rate;
33793 }
33794 }
33795 }
33796 }
33797 }
33798
33799 /* If a standard sample rate was not found just fall back to the first one that was iterated. */
33800 if (bestSampleRate == 0) {
33801 bestSampleRate = firstSampleRate;
33802 }
33803
33804 return bestSampleRate;
33805}
33806
33807
33808static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
33809{
33810 ma_bool32 isTerminating = MA_FALSE;
33811 struct ma_sio_hdl* handle;
33812
33813 MA_ASSERT(pContext != NULL);
33814 MA_ASSERT(callback != NULL);
33815
33816 /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */
33817
33818 /* Playback. */
33819 if (!isTerminating) {
33820 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0);
33821 if (handle != NULL) {
33822 /* Supports playback. */
33823 ma_device_info deviceInfo;
33824 MA_ZERO_OBJECT(&deviceInfo);
33825 ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY);
33826 ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
33827
33828 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
33829
33830 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
33831 }
33832 }
33833
33834 /* Capture. */
33835 if (!isTerminating) {
33836 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0);
33837 if (handle != NULL) {
33838 /* Supports capture. */
33839 ma_device_info deviceInfo;
33840 MA_ZERO_OBJECT(&deviceInfo);
33841 ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default");
33842 ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME);
33843
33844 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
33845
33846 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
33847 }
33848 }
33849
33850 return MA_SUCCESS;
33851}
33852
33853static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
33854{
33855 char devid[256];
33856 struct ma_sio_hdl* handle;
33857 struct ma_sio_cap caps;
33858 unsigned int iConfig;
33859
33860 MA_ASSERT(pContext != NULL);
33861
33862 /* We need to open the device before we can get information about it. */
33863 if (pDeviceID == NULL) {
33864 ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY);
33865 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME);
33866 } else {
33867 ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio);
33868 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid);
33869 }
33870
33871 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0);
33872 if (handle == NULL) {
33873 return MA_NO_DEVICE;
33874 }
33875
33876 if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) {
33877 return MA_ERROR;
33878 }
33879
33880 pDeviceInfo->nativeDataFormatCount = 0;
33881
33882 for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) {
33883 /*
33884 The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give
33885 preference to some formats over others.
33886 */
33887 unsigned int iEncoding;
33888 unsigned int iChannel;
33889 unsigned int iRate;
33890
33891 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
33892 unsigned int bits;
33893 unsigned int bps;
33894 unsigned int sig;
33895 unsigned int le;
33896 unsigned int msb;
33897 ma_format format;
33898
33899 if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) {
33900 continue;
33901 }
33902
33903 bits = caps.enc[iEncoding].bits;
33904 bps = caps.enc[iEncoding].bps;
33905 sig = caps.enc[iEncoding].sig;
33906 le = caps.enc[iEncoding].le;
33907 msb = caps.enc[iEncoding].msb;
33908 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
33909 if (format == ma_format_unknown) {
33910 continue; /* Format not supported. */
33911 }
33912
33913
33914 /* Channels. */
33915 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
33916 unsigned int chan = 0;
33917 unsigned int channels;
33918
33919 if (deviceType == ma_device_type_playback) {
33920 chan = caps.confs[iConfig].pchan;
33921 } else {
33922 chan = caps.confs[iConfig].rchan;
33923 }
33924
33925 if ((chan & (1UL << iChannel)) == 0) {
33926 continue;
33927 }
33928
33929 if (deviceType == ma_device_type_playback) {
33930 channels = caps.pchan[iChannel];
33931 } else {
33932 channels = caps.rchan[iChannel];
33933 }
33934
33935
33936 /* Sample Rates. */
33937 for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
33938 if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) {
33939 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0);
33940 }
33941 }
33942 }
33943 }
33944 }
33945
33946 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
33947 return MA_SUCCESS;
33948}
33949
33950static ma_result ma_device_uninit__sndio(ma_device* pDevice)
33951{
33952 MA_ASSERT(pDevice != NULL);
33953
33954 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
33955 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
33956 }
33957
33958 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
33959 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
33960 }
33961
33962 return MA_SUCCESS;
33963}
33964
33965static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
33966{
33967 const char* pDeviceName;
33968 ma_ptr handle;
33969 int openFlags = 0;
33970 struct ma_sio_cap caps;
33971 struct ma_sio_par par;
33972 const ma_device_id* pDeviceID;
33973 ma_format format;
33974 ma_uint32 channels;
33975 ma_uint32 sampleRate;
33976 ma_format internalFormat;
33977 ma_uint32 internalChannels;
33978 ma_uint32 internalSampleRate;
33979 ma_uint32 internalPeriodSizeInFrames;
33980 ma_uint32 internalPeriods;
33981
33982 MA_ASSERT(pConfig != NULL);
33983 MA_ASSERT(deviceType != ma_device_type_duplex);
33984 MA_ASSERT(pDevice != NULL);
33985
33986 if (deviceType == ma_device_type_capture) {
33987 openFlags = MA_SIO_REC;
33988 } else {
33989 openFlags = MA_SIO_PLAY;
33990 }
33991
33992 pDeviceID = pDescriptor->pDeviceID;
33993 format = pDescriptor->format;
33994 channels = pDescriptor->channels;
33995 sampleRate = pDescriptor->sampleRate;
33996
33997 pDeviceName = MA_SIO_DEVANY;
33998 if (pDeviceID != NULL) {
33999 pDeviceName = pDeviceID->sndio;
34000 }
34001
34002 handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0);
34003 if (handle == NULL) {
34004 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device.");
34006 }
34007
34008 /* We need to retrieve the device caps to determine the most appropriate format to use. */
34009 if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) {
34010 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
34011 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps.");
34012 return MA_ERROR;
34013 }
34014
34015 /*
34016 Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real
34017 way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this
34018 to the requested channels, regardless of whether or not the default channel count is requested.
34019
34020 For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the
34021 value returned by ma_find_best_channels_from_sio_cap__sndio().
34022 */
34023 if (deviceType == ma_device_type_capture) {
34024 if (format == ma_format_unknown) {
34025 format = ma_find_best_format_from_sio_cap__sndio(&caps);
34026 }
34027
34028 if (channels == 0) {
34029 if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
34030 channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
34031 } else {
34032 channels = MA_DEFAULT_CHANNELS;
34033 }
34034 }
34035 } else {
34036 if (format == ma_format_unknown) {
34037 format = ma_find_best_format_from_sio_cap__sndio(&caps);
34038 }
34039
34040 if (channels == 0) {
34041 if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
34042 channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
34043 } else {
34044 channels = MA_DEFAULT_CHANNELS;
34045 }
34046 }
34047 }
34048
34049 if (sampleRate == 0) {
34050 sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels);
34051 }
34052
34053
34054 ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par);
34055 par.msb = 0;
34056 par.le = ma_is_little_endian();
34057
34058 switch (format) {
34059 case ma_format_u8:
34060 {
34061 par.bits = 8;
34062 par.bps = 1;
34063 par.sig = 0;
34064 } break;
34065
34066 case ma_format_s24:
34067 {
34068 par.bits = 24;
34069 par.bps = 3;
34070 par.sig = 1;
34071 } break;
34072
34073 case ma_format_s32:
34074 {
34075 par.bits = 32;
34076 par.bps = 4;
34077 par.sig = 1;
34078 } break;
34079
34080 case ma_format_s16:
34081 case ma_format_f32:
34082 case ma_format_unknown:
34083 default:
34084 {
34085 par.bits = 16;
34086 par.bps = 2;
34087 par.sig = 1;
34088 } break;
34089 }
34090
34091 if (deviceType == ma_device_type_capture) {
34092 par.rchan = channels;
34093 } else {
34094 par.pchan = channels;
34095 }
34096
34097 par.rate = sampleRate;
34098
34099 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile);
34100
34101 par.round = internalPeriodSizeInFrames;
34102 par.appbufsz = par.round * pDescriptor->periodCount;
34103
34104 if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) {
34105 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
34106 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.");
34107 return MA_ERROR;
34108 }
34109
34110 if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) {
34111 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
34112 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size.");
34113 return MA_ERROR;
34114 }
34115
34116 internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb);
34117 internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan;
34118 internalSampleRate = par.rate;
34119 internalPeriods = par.appbufsz / par.round;
34120 internalPeriodSizeInFrames = par.round;
34121
34122 if (deviceType == ma_device_type_capture) {
34123 pDevice->sndio.handleCapture = handle;
34124 } else {
34125 pDevice->sndio.handlePlayback = handle;
34126 }
34127
34128 pDescriptor->format = internalFormat;
34129 pDescriptor->channels = internalChannels;
34130 pDescriptor->sampleRate = internalSampleRate;
34131 ma_channel_map_init_standard(ma_standard_channel_map_sndio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels);
34132 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
34133 pDescriptor->periodCount = internalPeriods;
34134
34135 return MA_SUCCESS;
34136}
34137
34138static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
34139{
34140 MA_ASSERT(pDevice != NULL);
34141
34142 MA_ZERO_OBJECT(&pDevice->sndio);
34143
34144 if (pConfig->deviceType == ma_device_type_loopback) {
34146 }
34147
34148 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
34149 ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
34150 if (result != MA_SUCCESS) {
34151 return result;
34152 }
34153 }
34154
34155 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
34156 ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
34157 if (result != MA_SUCCESS) {
34158 return result;
34159 }
34160 }
34161
34162 return MA_SUCCESS;
34163}
34164
34165static ma_result ma_device_start__sndio(ma_device* pDevice)
34166{
34167 MA_ASSERT(pDevice != NULL);
34168
34169 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
34170 ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
34171 }
34172
34173 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
34174 ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */
34175 }
34176
34177 return MA_SUCCESS;
34178}
34179
34180static ma_result ma_device_stop__sndio(ma_device* pDevice)
34181{
34182 MA_ASSERT(pDevice != NULL);
34183
34184 /*
34185 From the documentation:
34186
34187 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
34188 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
34189 buffer is drained. In no case are samples in the play buffer discarded.
34190
34191 Therefore, sio_stop() performs all of the necessary draining for us.
34192 */
34193
34194 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
34195 ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
34196 }
34197
34198 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
34199 ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
34200 }
34201
34202 return MA_SUCCESS;
34203}
34204
34205static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
34206{
34207 int result;
34208
34209 if (pFramesWritten != NULL) {
34210 *pFramesWritten = 0;
34211 }
34212
34213 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));
34214 if (result == 0) {
34215 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device.");
34216 return MA_IO_ERROR;
34217 }
34218
34219 if (pFramesWritten != NULL) {
34220 *pFramesWritten = frameCount;
34221 }
34222
34223 return MA_SUCCESS;
34224}
34225
34226static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
34227{
34228 int result;
34229
34230 if (pFramesRead != NULL) {
34231 *pFramesRead = 0;
34232 }
34233
34234 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));
34235 if (result == 0) {
34236 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.");
34237 return MA_IO_ERROR;
34238 }
34239
34240 if (pFramesRead != NULL) {
34241 *pFramesRead = frameCount;
34242 }
34243
34244 return MA_SUCCESS;
34245}
34246
34247static ma_result ma_context_uninit__sndio(ma_context* pContext)
34248{
34249 MA_ASSERT(pContext != NULL);
34250 MA_ASSERT(pContext->backend == ma_backend_sndio);
34251
34252 (void)pContext;
34253 return MA_SUCCESS;
34254}
34255
34256static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
34257{
34258#ifndef MA_NO_RUNTIME_LINKING
34259 const char* libsndioNames[] = {
34260 "libsndio.so"
34261 };
34262 size_t i;
34263
34264 for (i = 0; i < ma_countof(libsndioNames); ++i) {
34265 pContext->sndio.sndioSO = ma_dlopen(pContext, libsndioNames[i]);
34266 if (pContext->sndio.sndioSO != NULL) {
34267 break;
34268 }
34269 }
34270
34271 if (pContext->sndio.sndioSO == NULL) {
34272 return MA_NO_BACKEND;
34273 }
34274
34275 pContext->sndio.sio_open = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_open");
34276 pContext->sndio.sio_close = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_close");
34277 pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_setpar");
34278 pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getpar");
34279 pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getcap");
34280 pContext->sndio.sio_write = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_write");
34281 pContext->sndio.sio_read = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_read");
34282 pContext->sndio.sio_start = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_start");
34283 pContext->sndio.sio_stop = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_stop");
34284 pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_initpar");
34285#else
34286 pContext->sndio.sio_open = sio_open;
34287 pContext->sndio.sio_close = sio_close;
34288 pContext->sndio.sio_setpar = sio_setpar;
34289 pContext->sndio.sio_getpar = sio_getpar;
34290 pContext->sndio.sio_getcap = sio_getcap;
34291 pContext->sndio.sio_write = sio_write;
34292 pContext->sndio.sio_read = sio_read;
34293 pContext->sndio.sio_start = sio_start;
34294 pContext->sndio.sio_stop = sio_stop;
34295 pContext->sndio.sio_initpar = sio_initpar;
34296#endif
34297
34298 pCallbacks->onContextInit = ma_context_init__sndio;
34299 pCallbacks->onContextUninit = ma_context_uninit__sndio;
34300 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio;
34301 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio;
34302 pCallbacks->onDeviceInit = ma_device_init__sndio;
34303 pCallbacks->onDeviceUninit = ma_device_uninit__sndio;
34304 pCallbacks->onDeviceStart = ma_device_start__sndio;
34305 pCallbacks->onDeviceStop = ma_device_stop__sndio;
34306 pCallbacks->onDeviceRead = ma_device_read__sndio;
34307 pCallbacks->onDeviceWrite = ma_device_write__sndio;
34308 pCallbacks->onDeviceDataLoop = NULL;
34309
34310 (void)pConfig;
34311 return MA_SUCCESS;
34312}
34313#endif /* sndio */
34314
34315
34316
34317
34322#ifdef MA_HAS_AUDIO4
34323#include <fcntl.h>
34324#include <poll.h>
34325#include <errno.h>
34326#include <sys/stat.h>
34327#include <sys/types.h>
34328#include <sys/ioctl.h>
34329#include <sys/audioio.h>
34330
34331#if defined(__OpenBSD__)
34332 #include <sys/param.h>
34333 #if defined(OpenBSD) && OpenBSD >= 201709
34334 #define MA_AUDIO4_USE_NEW_API
34335 #endif
34336#endif
34337
34338static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex)
34339{
34340 size_t baseLen;
34341
34342 MA_ASSERT(id != NULL);
34343 MA_ASSERT(idSize > 0);
34344 MA_ASSERT(deviceIndex >= 0);
34345
34346 baseLen = strlen(base);
34347 MA_ASSERT(idSize > baseLen);
34348
34349 ma_strcpy_s(id, idSize, base);
34350 ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10);
34351}
34352
34353static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut)
34354{
34355 size_t idLen;
34356 size_t baseLen;
34357 const char* deviceIndexStr;
34358
34359 MA_ASSERT(id != NULL);
34360 MA_ASSERT(base != NULL);
34361 MA_ASSERT(pIndexOut != NULL);
34362
34363 idLen = strlen(id);
34364 baseLen = strlen(base);
34365 if (idLen <= baseLen) {
34366 return MA_ERROR; /* Doesn't look like the id starts with the base. */
34367 }
34368
34369 if (strncmp(id, base, baseLen) != 0) {
34370 return MA_ERROR; /* ID does not begin with base. */
34371 }
34372
34373 deviceIndexStr = id + baseLen;
34374 if (deviceIndexStr[0] == '\0') {
34375 return MA_ERROR; /* No index specified in the ID. */
34376 }
34377
34378 if (pIndexOut) {
34379 *pIndexOut = atoi(deviceIndexStr);
34380 }
34381
34382 return MA_SUCCESS;
34383}
34384
34385
34386#if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
34387static ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision)
34388{
34389 if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) {
34390 return ma_format_u8;
34391 } else {
34392 if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) {
34393 if (precision == 16) {
34394 return ma_format_s16;
34395 } else if (precision == 24) {
34396 return ma_format_s24;
34397 } else if (precision == 32) {
34398 return ma_format_s32;
34399 }
34400 } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) {
34401 if (precision == 16) {
34402 return ma_format_s16;
34403 } else if (precision == 24) {
34404 return ma_format_s24;
34405 } else if (precision == 32) {
34406 return ma_format_s32;
34407 }
34408 }
34409 }
34410
34411 return ma_format_unknown; /* Encoding not supported. */
34412}
34413
34414static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision)
34415{
34416 MA_ASSERT(pEncoding != NULL);
34417 MA_ASSERT(pPrecision != NULL);
34418
34419 switch (format)
34420 {
34421 case ma_format_u8:
34422 {
34423 *pEncoding = AUDIO_ENCODING_ULINEAR;
34424 *pPrecision = 8;
34425 } break;
34426
34427 case ma_format_s24:
34428 {
34429 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
34430 *pPrecision = 24;
34431 } break;
34432
34433 case ma_format_s32:
34434 {
34435 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
34436 *pPrecision = 32;
34437 } break;
34438
34439 case ma_format_s16:
34440 case ma_format_f32:
34441 case ma_format_unknown:
34442 default:
34443 {
34444 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
34445 *pPrecision = 16;
34446 } break;
34447 }
34448}
34449
34450static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo)
34451{
34452 return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision);
34453}
34454
34455static ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat)
34456{
34457 audio_encoding_t encoding;
34458 ma_uint32 iFormat;
34459 int counter = 0;
34460
34461 /* First check to see if the preferred format is supported. */
34462 if (preferredFormat != ma_format_unknown) {
34463 counter = 0;
34464 for (;;) {
34465 MA_ZERO_OBJECT(&encoding);
34466 encoding.index = counter;
34467 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
34468 break;
34469 }
34470
34471 if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) {
34472 return preferredFormat; /* Found the preferred format. */
34473 }
34474
34475 /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */
34476 counter += 1;
34477 }
34478 }
34479
34480 /* Getting here means our preferred format is not supported, so fall back to our standard priorities. */
34481 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
34482 ma_format format = g_maFormatPriorities[iFormat];
34483
34484 counter = 0;
34485 for (;;) {
34486 MA_ZERO_OBJECT(&encoding);
34487 encoding.index = counter;
34488 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
34489 break;
34490 }
34491
34492 if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) {
34493 return format; /* Found a workable format. */
34494 }
34495
34496 /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */
34497 counter += 1;
34498 }
34499 }
34500
34501 /* Getting here means not appropriate format was found. */
34502 return ma_format_unknown;
34503}
34504#else
34505static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par)
34506{
34507 if (par->bits == 8 && par->bps == 1 && par->sig == 0) {
34508 return ma_format_u8;
34509 }
34510 if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) {
34511 return ma_format_s16;
34512 }
34513 if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) {
34514 return ma_format_s24;
34515 }
34516 if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) {
34517 return ma_format_f32;
34518 }
34519
34520 /* Format not supported. */
34521 return ma_format_unknown;
34522}
34523#endif
34524
34525static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo)
34526{
34527 audio_device_t fdDevice;
34528
34529 MA_ASSERT(pContext != NULL);
34530 MA_ASSERT(fd >= 0);
34531 MA_ASSERT(pDeviceInfo != NULL);
34532
34533 (void)pContext;
34534 (void)deviceType;
34535
34536 if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) {
34537 return MA_ERROR; /* Failed to retrieve device info. */
34538 }
34539
34540 /* Name. */
34541 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name);
34542
34543 #if !defined(MA_AUDIO4_USE_NEW_API)
34544 {
34545 audio_info_t fdInfo;
34546 int counter = 0;
34547 ma_uint32 channels;
34548 ma_uint32 sampleRate;
34549
34550 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
34551 return MA_ERROR;
34552 }
34553
34554 if (deviceType == ma_device_type_playback) {
34555 channels = fdInfo.play.channels;
34556 sampleRate = fdInfo.play.sample_rate;
34557 } else {
34558 channels = fdInfo.record.channels;
34559 sampleRate = fdInfo.record.sample_rate;
34560 }
34561
34562 /* Supported formats. We get this by looking at the encodings. */
34563 pDeviceInfo->nativeDataFormatCount = 0;
34564 for (;;) {
34565 audio_encoding_t encoding;
34566 ma_format format;
34567
34568 MA_ZERO_OBJECT(&encoding);
34569 encoding.index = counter;
34570 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
34571 break;
34572 }
34573
34574 format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision);
34575 if (format != ma_format_unknown) {
34576 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0);
34577 }
34578
34579 counter += 1;
34580 }
34581 }
34582 #else
34583 {
34584 struct audio_swpar fdPar;
34585 ma_format format;
34586 ma_uint32 channels;
34587 ma_uint32 sampleRate;
34588
34589 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
34590 return MA_ERROR;
34591 }
34592
34593 format = ma_format_from_swpar__audio4(&fdPar);
34594 if (format == ma_format_unknown) {
34596 }
34597
34598 if (deviceType == ma_device_type_playback) {
34599 channels = fdPar.pchan;
34600 } else {
34601 channels = fdPar.rchan;
34602 }
34603
34604 sampleRate = fdPar.rate;
34605
34606 pDeviceInfo->nativeDataFormatCount = 0;
34607 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0);
34608 }
34609 #endif
34610
34611 return MA_SUCCESS;
34612}
34613
34614static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
34615{
34616 const int maxDevices = 64;
34617 char devpath[256];
34618 int iDevice;
34619
34620 MA_ASSERT(pContext != NULL);
34621 MA_ASSERT(callback != NULL);
34622
34623 /*
34624 Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN"
34625 version here since we can open it even when another process has control of the "/dev/audioN" device.
34626 */
34627 for (iDevice = 0; iDevice < maxDevices; ++iDevice) {
34628 struct stat st;
34629 int fd;
34630 ma_bool32 isTerminating = MA_FALSE;
34631
34632 ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl");
34633 ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10);
34634
34635 if (stat(devpath, &st) < 0) {
34636 break;
34637 }
34638
34639 /* The device exists, but we need to check if it's usable as playback and/or capture. */
34640
34641 /* Playback. */
34642 if (!isTerminating) {
34643 fd = open(devpath, O_RDONLY, 0);
34644 if (fd >= 0) {
34645 /* Supports playback. */
34646 ma_device_info deviceInfo;
34647 MA_ZERO_OBJECT(&deviceInfo);
34648 ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
34649 if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) {
34650 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
34651 }
34652
34653 close(fd);
34654 }
34655 }
34656
34657 /* Capture. */
34658 if (!isTerminating) {
34659 fd = open(devpath, O_WRONLY, 0);
34660 if (fd >= 0) {
34661 /* Supports capture. */
34662 ma_device_info deviceInfo;
34663 MA_ZERO_OBJECT(&deviceInfo);
34664 ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
34665 if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) {
34666 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
34667 }
34668
34669 close(fd);
34670 }
34671 }
34672
34673 if (isTerminating) {
34674 break;
34675 }
34676 }
34677
34678 return MA_SUCCESS;
34679}
34680
34681static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
34682{
34683 int fd = -1;
34684 int deviceIndex = -1;
34685 char ctlid[256];
34686 ma_result result;
34687
34688 MA_ASSERT(pContext != NULL);
34689
34690 /*
34691 We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number
34692 from the device ID which will be in "/dev/audioN" format.
34693 */
34694 if (pDeviceID == NULL) {
34695 /* Default device. */
34696 ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl");
34697 } else {
34698 /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */
34699 result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex);
34700 if (result != MA_SUCCESS) {
34701 return result;
34702 }
34703
34704 ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex);
34705 }
34706
34707 fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0);
34708 if (fd == -1) {
34709 return MA_NO_DEVICE;
34710 }
34711
34712 if (deviceIndex == -1) {
34713 ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio");
34714 } else {
34715 ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex);
34716 }
34717
34718 result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo);
34719
34720 close(fd);
34721 return result;
34722}
34723
34724static ma_result ma_device_uninit__audio4(ma_device* pDevice)
34725{
34726 MA_ASSERT(pDevice != NULL);
34727
34728 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
34729 close(pDevice->audio4.fdCapture);
34730 }
34731
34732 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
34733 close(pDevice->audio4.fdPlayback);
34734 }
34735
34736 return MA_SUCCESS;
34737}
34738
34739static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
34740{
34741 const char* pDefaultDeviceNames[] = {
34742 "/dev/audio",
34743 "/dev/audio0"
34744 };
34745 int fd;
34746 int fdFlags = 0;
34747 ma_format internalFormat;
34748 ma_uint32 internalChannels;
34749 ma_uint32 internalSampleRate;
34750 ma_uint32 internalPeriodSizeInFrames;
34751 ma_uint32 internalPeriods;
34752
34753 MA_ASSERT(pConfig != NULL);
34754 MA_ASSERT(deviceType != ma_device_type_duplex);
34755 MA_ASSERT(pDevice != NULL);
34756
34757 /* The first thing to do is open the file. */
34758 if (deviceType == ma_device_type_capture) {
34759 fdFlags = O_RDONLY;
34760 } else {
34761 fdFlags = O_WRONLY;
34762 }
34763 /*fdFlags |= O_NONBLOCK;*/
34764
34765 if (pDescriptor->pDeviceID == NULL) {
34766 /* Default device. */
34767 size_t iDevice;
34768 for (iDevice = 0; iDevice < ma_countof(pDefaultDeviceNames); ++iDevice) {
34769 fd = open(pDefaultDeviceNames[iDevice], fdFlags, 0);
34770 if (fd != -1) {
34771 break;
34772 }
34773 }
34774 } else {
34775 /* Specific device. */
34776 fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0);
34777 }
34778
34779 if (fd == -1) {
34780 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device.");
34781 return ma_result_from_errno(errno);
34782 }
34783
34784 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
34785 {
34786 audio_info_t fdInfo;
34787
34788 /*
34789 The documentation is a little bit unclear to me as to how it handles formats. It says the
34790 following:
34791
34792 Regardless of formats supported by underlying driver, the audio driver accepts the
34793 following formats.
34794
34795 By then the next sentence says this:
34796
34797 `encoding` and `precision` are one of the values obtained by AUDIO_GETENC.
34798
34799 It sounds like a direct contradiction to me. I'm going to play this safe any only use the
34800 best sample format returned by AUDIO_GETENC. If the requested format is supported we'll
34801 use that, but otherwise we'll just use our standard format priorities to pick an
34802 appropriate one.
34803 */
34804 AUDIO_INITINFO(&fdInfo);
34805
34806 /* We get the driver to do as much of the data conversion as possible. */
34807 if (deviceType == ma_device_type_capture) {
34808 fdInfo.mode = AUMODE_RECORD;
34809 ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision);
34810
34811 if (pDescriptor->channels != 0) {
34812 fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */
34813 }
34814
34815 if (pDescriptor->sampleRate != 0) {
34816 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`.) */
34817 }
34818 } else {
34819 fdInfo.mode = AUMODE_PLAY;
34820 ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision);
34821
34822 if (pDescriptor->channels != 0) {
34823 fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */
34824 }
34825
34826 if (pDescriptor->sampleRate != 0) {
34827 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`.) */
34828 }
34829 }
34830
34831 if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
34832 close(fd);
34833 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed.");
34834 return ma_result_from_errno(errno);
34835 }
34836
34837 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
34838 close(fd);
34839 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.");
34840 return ma_result_from_errno(errno);
34841 }
34842
34843 if (deviceType == ma_device_type_capture) {
34844 internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record);
34845 internalChannels = fdInfo.record.channels;
34846 internalSampleRate = fdInfo.record.sample_rate;
34847 } else {
34848 internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play);
34849 internalChannels = fdInfo.play.channels;
34850 internalSampleRate = fdInfo.play.sample_rate;
34851 }
34852
34853 if (internalFormat == ma_format_unknown) {
34854 close(fd);
34855 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.");
34857 }
34858
34859 /* Buffer. */
34860 {
34861 ma_uint32 internalPeriodSizeInBytes;
34862
34863 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile);
34864
34865 internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
34866 if (internalPeriodSizeInBytes < 16) {
34867 internalPeriodSizeInBytes = 16;
34868 }
34869
34870 internalPeriods = pDescriptor->periodCount;
34871 if (internalPeriods < 2) {
34872 internalPeriods = 2;
34873 }
34874
34875 /* What miniaudio calls a period, audio4 calls a block. */
34876 AUDIO_INITINFO(&fdInfo);
34877 fdInfo.hiwat = internalPeriods;
34878 fdInfo.lowat = internalPeriods-1;
34879 fdInfo.blocksize = internalPeriodSizeInBytes;
34880 if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
34881 close(fd);
34882 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed.");
34883 return ma_result_from_errno(errno);
34884 }
34885
34886 internalPeriods = fdInfo.hiwat;
34887 internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels);
34888 }
34889 }
34890 #else
34891 {
34892 struct audio_swpar fdPar;
34893
34894 /* 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. */
34895 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
34896 close(fd);
34897 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters.");
34898 return ma_result_from_errno(errno);
34899 }
34900
34901 internalFormat = ma_format_from_swpar__audio4(&fdPar);
34902 internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
34903 internalSampleRate = fdPar.rate;
34904
34905 if (internalFormat == ma_format_unknown) {
34906 close(fd);
34907 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.");
34909 }
34910
34911 /* Buffer. */
34912 {
34913 ma_uint32 internalPeriodSizeInBytes;
34914
34915 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile);
34916
34917 /* What miniaudio calls a period, audio4 calls a block. */
34918 internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
34919 if (internalPeriodSizeInBytes < 16) {
34920 internalPeriodSizeInBytes = 16;
34921 }
34922
34923 fdPar.nblks = pDescriptor->periodCount;
34924 fdPar.round = internalPeriodSizeInBytes;
34925
34926 if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) {
34927 close(fd);
34928 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters.");
34929 return ma_result_from_errno(errno);
34930 }
34931
34932 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
34933 close(fd);
34934 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters.");
34935 return ma_result_from_errno(errno);
34936 }
34937 }
34938
34939 internalFormat = ma_format_from_swpar__audio4(&fdPar);
34940 internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
34941 internalSampleRate = fdPar.rate;
34942 internalPeriods = fdPar.nblks;
34943 internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels);
34944 }
34945 #endif
34946
34947 if (internalFormat == ma_format_unknown) {
34948 close(fd);
34949 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.");
34951 }
34952
34953 if (deviceType == ma_device_type_capture) {
34954 pDevice->audio4.fdCapture = fd;
34955 } else {
34956 pDevice->audio4.fdPlayback = fd;
34957 }
34958
34959 pDescriptor->format = internalFormat;
34960 pDescriptor->channels = internalChannels;
34961 pDescriptor->sampleRate = internalSampleRate;
34962 ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels);
34963 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
34964 pDescriptor->periodCount = internalPeriods;
34965
34966 return MA_SUCCESS;
34967}
34968
34969static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
34970{
34971 MA_ASSERT(pDevice != NULL);
34972
34973 MA_ZERO_OBJECT(&pDevice->audio4);
34974
34975 if (pConfig->deviceType == ma_device_type_loopback) {
34977 }
34978
34979 pDevice->audio4.fdCapture = -1;
34980 pDevice->audio4.fdPlayback = -1;
34981
34982 /*
34983 The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD
34984 introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as
34985 I'm aware.
34986 */
34987#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000
34988 /* NetBSD 8.0+ */
34989 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
34990 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
34992 }
34993#else
34994 /* All other flavors. */
34995#endif
34996
34997 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
34998 ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
34999 if (result != MA_SUCCESS) {
35000 return result;
35001 }
35002 }
35003
35004 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
35005 ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
35006 if (result != MA_SUCCESS) {
35007 if (pConfig->deviceType == ma_device_type_duplex) {
35008 close(pDevice->audio4.fdCapture);
35009 }
35010 return result;
35011 }
35012 }
35013
35014 return MA_SUCCESS;
35015}
35016
35017static ma_result ma_device_start__audio4(ma_device* pDevice)
35018{
35019 MA_ASSERT(pDevice != NULL);
35020
35021 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
35022 if (pDevice->audio4.fdCapture == -1) {
35023 return MA_INVALID_ARGS;
35024 }
35025 }
35026
35027 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
35028 if (pDevice->audio4.fdPlayback == -1) {
35029 return MA_INVALID_ARGS;
35030 }
35031 }
35032
35033 return MA_SUCCESS;
35034}
35035
35036static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd)
35037{
35038 if (fd == -1) {
35039 return MA_INVALID_ARGS;
35040 }
35041
35042#if !defined(MA_AUDIO4_USE_NEW_API)
35043 if (ioctl(fd, AUDIO_FLUSH, 0) < 0) {
35044 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed.");
35045 return ma_result_from_errno(errno);
35046 }
35047#else
35048 if (ioctl(fd, AUDIO_STOP, 0) < 0) {
35049 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed.");
35050 return ma_result_from_errno(errno);
35051 }
35052#endif
35053
35054 return MA_SUCCESS;
35055}
35056
35057static ma_result ma_device_stop__audio4(ma_device* pDevice)
35058{
35059 MA_ASSERT(pDevice != NULL);
35060
35061 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
35062 ma_result result;
35063
35064 result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture);
35065 if (result != MA_SUCCESS) {
35066 return result;
35067 }
35068 }
35069
35070 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
35071 ma_result result;
35072
35073 /* 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. */
35074 #if !defined(MA_AUDIO4_USE_NEW_API)
35075 ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0);
35076 #endif
35077
35078 /* Here is where the device is stopped immediately. */
35079 result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback);
35080 if (result != MA_SUCCESS) {
35081 return result;
35082 }
35083 }
35084
35085 return MA_SUCCESS;
35086}
35087
35088static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
35089{
35090 int result;
35091
35092 if (pFramesWritten != NULL) {
35093 *pFramesWritten = 0;
35094 }
35095
35096 result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
35097 if (result < 0) {
35098 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device.");
35099 return ma_result_from_errno(errno);
35100 }
35101
35102 if (pFramesWritten != NULL) {
35103 *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
35104 }
35105
35106 return MA_SUCCESS;
35107}
35108
35109static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
35110{
35111 int result;
35112
35113 if (pFramesRead != NULL) {
35114 *pFramesRead = 0;
35115 }
35116
35117 result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
35118 if (result < 0) {
35119 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device.");
35120 return ma_result_from_errno(errno);
35121 }
35122
35123 if (pFramesRead != NULL) {
35124 *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
35125 }
35126
35127 return MA_SUCCESS;
35128}
35129
35130static ma_result ma_context_uninit__audio4(ma_context* pContext)
35131{
35132 MA_ASSERT(pContext != NULL);
35133 MA_ASSERT(pContext->backend == ma_backend_audio4);
35134
35135 (void)pContext;
35136 return MA_SUCCESS;
35137}
35138
35139static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
35140{
35141 MA_ASSERT(pContext != NULL);
35142
35143 (void)pConfig;
35144
35145 pCallbacks->onContextInit = ma_context_init__audio4;
35146 pCallbacks->onContextUninit = ma_context_uninit__audio4;
35147 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4;
35148 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4;
35149 pCallbacks->onDeviceInit = ma_device_init__audio4;
35150 pCallbacks->onDeviceUninit = ma_device_uninit__audio4;
35151 pCallbacks->onDeviceStart = ma_device_start__audio4;
35152 pCallbacks->onDeviceStop = ma_device_stop__audio4;
35153 pCallbacks->onDeviceRead = ma_device_read__audio4;
35154 pCallbacks->onDeviceWrite = ma_device_write__audio4;
35155 pCallbacks->onDeviceDataLoop = NULL;
35156
35157 return MA_SUCCESS;
35158}
35159#endif /* audio4 */
35160
35161
35162
35167#ifdef MA_HAS_OSS
35168#include <sys/ioctl.h>
35169#include <unistd.h>
35170#include <fcntl.h>
35171#include <sys/soundcard.h>
35172
35173#ifndef SNDCTL_DSP_HALT
35174#define SNDCTL_DSP_HALT SNDCTL_DSP_RESET
35175#endif
35176
35177#define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp"
35178
35179static int ma_open_temp_device__oss()
35180{
35181 /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */
35182 int fd = open("/dev/mixer", O_RDONLY, 0);
35183 if (fd >= 0) {
35184 return fd;
35185 }
35186
35187 return -1;
35188}
35189
35190static 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)
35191{
35192 const char* deviceName;
35193 int flags;
35194
35195 MA_ASSERT(pContext != NULL);
35196 MA_ASSERT(pfd != NULL);
35197 (void)pContext;
35198
35199 *pfd = -1;
35200
35201 /* This function should only be called for playback or capture, not duplex. */
35202 if (deviceType == ma_device_type_duplex) {
35203 return MA_INVALID_ARGS;
35204 }
35205
35206 deviceName = MA_OSS_DEFAULT_DEVICE_NAME;
35207 if (pDeviceID != NULL) {
35208 deviceName = pDeviceID->oss;
35209 }
35210
35211 flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY;
35212 if (shareMode == ma_share_mode_exclusive) {
35213 flags |= O_EXCL;
35214 }
35215
35216 *pfd = open(deviceName, flags, 0);
35217 if (*pfd == -1) {
35218 return ma_result_from_errno(errno);
35219 }
35220
35221 return MA_SUCCESS;
35222}
35223
35224static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
35225{
35226 int fd;
35227 oss_sysinfo si;
35228 int result;
35229
35230 MA_ASSERT(pContext != NULL);
35231 MA_ASSERT(callback != NULL);
35232
35233 fd = ma_open_temp_device__oss();
35234 if (fd == -1) {
35235 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.");
35236 return MA_NO_BACKEND;
35237 }
35238
35239 result = ioctl(fd, SNDCTL_SYSINFO, &si);
35240 if (result != -1) {
35241 int iAudioDevice;
35242 for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
35243 oss_audioinfo ai;
35244 ai.dev = iAudioDevice;
35245 result = ioctl(fd, SNDCTL_AUDIOINFO, &ai);
35246 if (result != -1) {
35247 if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */
35248 ma_device_info deviceInfo;
35249 ma_bool32 isTerminating = MA_FALSE;
35250
35251 MA_ZERO_OBJECT(&deviceInfo);
35252
35253 /* ID */
35254 ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1);
35255
35256 /*
35257 The human readable device name should be in the "ai.handle" variable, but it can
35258 sometimes be empty in which case we just fall back to "ai.name" which is less user
35259 friendly, but usually has a value.
35260 */
35261 if (ai.handle[0] != '\0') {
35262 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1);
35263 } else {
35264 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1);
35265 }
35266
35267 /* The device can be both playback and capture. */
35268 if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) {
35269 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
35270 }
35271 if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) {
35272 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
35273 }
35274
35275 if (isTerminating) {
35276 break;
35277 }
35278 }
35279 }
35280 }
35281 } else {
35282 close(fd);
35283 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.");
35284 return MA_NO_BACKEND;
35285 }
35286
35287 close(fd);
35288 return MA_SUCCESS;
35289}
35290
35291static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo)
35292{
35293 unsigned int minChannels;
35294 unsigned int maxChannels;
35295 unsigned int iRate;
35296
35297 MA_ASSERT(pContext != NULL);
35298 MA_ASSERT(pAudioInfo != NULL);
35299 MA_ASSERT(pDeviceInfo != NULL);
35300
35301 /* If we support all channels we just report 0. */
35302 minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
35303 maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
35304
35305 /*
35306 OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness,
35307 which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which
35308 case we'll need to use min_rate and max_rate and report only standard rates.
35309 */
35310 if (pAudioInfo->nrates > 0) {
35311 for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) {
35312 unsigned int rate = pAudioInfo->rates[iRate];
35313
35314 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
35315 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. */
35316 } else {
35317 unsigned int iChannel;
35318 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
35319 ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0);
35320 }
35321 }
35322 }
35323 } else {
35324 for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) {
35325 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate];
35326
35327 if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) {
35328 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
35329 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. */
35330 } else {
35331 unsigned int iChannel;
35332 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
35333 ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0);
35334 }
35335 }
35336 }
35337 }
35338 }
35339}
35340
35341static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
35342{
35343 ma_bool32 foundDevice;
35344 int fdTemp;
35345 oss_sysinfo si;
35346 int result;
35347
35348 MA_ASSERT(pContext != NULL);
35349
35350 /* Handle the default device a little differently. */
35351 if (pDeviceID == NULL) {
35352 if (deviceType == ma_device_type_playback) {
35353 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
35354 } else {
35355 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
35356 }
35357
35358 return MA_SUCCESS;
35359 }
35360
35361
35362 /* If we get here it means we are _not_ using the default device. */
35363 foundDevice = MA_FALSE;
35364
35365 fdTemp = ma_open_temp_device__oss();
35366 if (fdTemp == -1) {
35367 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.");
35368 return MA_NO_BACKEND;
35369 }
35370
35371 result = ioctl(fdTemp, SNDCTL_SYSINFO, &si);
35372 if (result != -1) {
35373 int iAudioDevice;
35374 for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
35375 oss_audioinfo ai;
35376 ai.dev = iAudioDevice;
35377 result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai);
35378 if (result != -1) {
35379 if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) {
35380 /* It has the same name, so now just confirm the type. */
35381 if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) ||
35382 (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) {
35383 unsigned int formatMask;
35384
35385 /* ID */
35386 ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1);
35387
35388 /*
35389 The human readable device name should be in the "ai.handle" variable, but it can
35390 sometimes be empty in which case we just fall back to "ai.name" which is less user
35391 friendly, but usually has a value.
35392 */
35393 if (ai.handle[0] != '\0') {
35394 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1);
35395 } else {
35396 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1);
35397 }
35398
35399
35400 pDeviceInfo->nativeDataFormatCount = 0;
35401
35402 if (deviceType == ma_device_type_playback) {
35403 formatMask = ai.oformats;
35404 } else {
35405 formatMask = ai.iformats;
35406 }
35407
35408 if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) {
35409 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo);
35410 }
35411 if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) {
35412 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo);
35413 }
35414 if ((formatMask & AFMT_U8) != 0) {
35415 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo);
35416 }
35417
35418 foundDevice = MA_TRUE;
35419 break;
35420 }
35421 }
35422 }
35423 }
35424 } else {
35425 close(fdTemp);
35426 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.");
35427 return MA_NO_BACKEND;
35428 }
35429
35430
35431 close(fdTemp);
35432
35433 if (!foundDevice) {
35434 return MA_NO_DEVICE;
35435 }
35436
35437 return MA_SUCCESS;
35438}
35439
35440static ma_result ma_device_uninit__oss(ma_device* pDevice)
35441{
35442 MA_ASSERT(pDevice != NULL);
35443
35444 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
35445 close(pDevice->oss.fdCapture);
35446 }
35447
35448 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
35449 close(pDevice->oss.fdPlayback);
35450 }
35451
35452 return MA_SUCCESS;
35453}
35454
35455static int ma_format_to_oss(ma_format format)
35456{
35457 int ossFormat = AFMT_U8;
35458 switch (format) {
35459 case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
35460 case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
35461 case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
35462 case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
35463 case ma_format_u8:
35464 default: ossFormat = AFMT_U8; break;
35465 }
35466
35467 return ossFormat;
35468}
35469
35470static ma_format ma_format_from_oss(int ossFormat)
35471{
35472 if (ossFormat == AFMT_U8) {
35473 return ma_format_u8;
35474 } else {
35475 if (ma_is_little_endian()) {
35476 switch (ossFormat) {
35477 case AFMT_S16_LE: return ma_format_s16;
35478 case AFMT_S32_LE: return ma_format_s32;
35479 default: return ma_format_unknown;
35480 }
35481 } else {
35482 switch (ossFormat) {
35483 case AFMT_S16_BE: return ma_format_s16;
35484 case AFMT_S32_BE: return ma_format_s32;
35485 default: return ma_format_unknown;
35486 }
35487 }
35488 }
35489
35490 return ma_format_unknown;
35491}
35492
35493static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
35494{
35495 ma_result result;
35496 int ossResult;
35497 int fd;
35498 const ma_device_id* pDeviceID = NULL;
35499 ma_share_mode shareMode;
35500 int ossFormat;
35501 int ossChannels;
35502 int ossSampleRate;
35503 int ossFragment;
35504
35505 MA_ASSERT(pDevice != NULL);
35506 MA_ASSERT(pConfig != NULL);
35507 MA_ASSERT(deviceType != ma_device_type_duplex);
35508
35509 pDeviceID = pDescriptor->pDeviceID;
35510 shareMode = pDescriptor->shareMode;
35511 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. */
35512 ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS;
35513 ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE;
35514
35515 result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd);
35516 if (result != MA_SUCCESS) {
35517 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.");
35518 return result;
35519 }
35520
35521 /*
35522 The OSS documantation is very clear about the order we should be initializing the device's properties:
35523 1) Format
35524 2) Channels
35525 3) Sample rate.
35526 */
35527
35528 /* Format. */
35529 ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat);
35530 if (ossResult == -1) {
35531 close(fd);
35532 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format.");
35533 return ma_result_from_errno(errno);
35534 }
35535
35536 /* Channels. */
35537 ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels);
35538 if (ossResult == -1) {
35539 close(fd);
35540 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count.");
35541 return ma_result_from_errno(errno);
35542 }
35543
35544 /* Sample Rate. */
35545 ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate);
35546 if (ossResult == -1) {
35547 close(fd);
35548 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate.");
35549 return ma_result_from_errno(errno);
35550 }
35551
35552 /*
35553 Buffer.
35554
35555 The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if
35556 it should be done before or after format/channels/rate.
35557
35558 OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual
35559 value.
35560 */
35561 {
35562 ma_uint32 periodSizeInFrames;
35563 ma_uint32 periodSizeInBytes;
35564 ma_uint32 ossFragmentSizePower;
35565
35566 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile);
35567
35568 periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels));
35569 if (periodSizeInBytes < 16) {
35570 periodSizeInBytes = 16;
35571 }
35572
35573 ossFragmentSizePower = 4;
35574 periodSizeInBytes >>= 4;
35575 while (periodSizeInBytes >>= 1) {
35576 ossFragmentSizePower += 1;
35577 }
35578
35579 ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower);
35580 ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment);
35581 if (ossResult == -1) {
35582 close(fd);
35583 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count.");
35584 return ma_result_from_errno(errno);
35585 }
35586 }
35587
35588 /* Internal settings. */
35589 if (deviceType == ma_device_type_capture) {
35590 pDevice->oss.fdCapture = fd;
35591 } else {
35592 pDevice->oss.fdPlayback = fd;
35593 }
35594
35595 pDescriptor->format = ma_format_from_oss(ossFormat);
35596 pDescriptor->channels = ossChannels;
35597 pDescriptor->sampleRate = ossSampleRate;
35598 ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels);
35599 pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16);
35600 pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels);
35601
35602 if (pDescriptor->format == ma_format_unknown) {
35603 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.");
35605 }
35606
35607 return MA_SUCCESS;
35608}
35609
35610static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
35611{
35612 MA_ASSERT(pDevice != NULL);
35613 MA_ASSERT(pConfig != NULL);
35614
35615 MA_ZERO_OBJECT(&pDevice->oss);
35616
35617 if (pConfig->deviceType == ma_device_type_loopback) {
35619 }
35620
35621 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
35622 ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
35623 if (result != MA_SUCCESS) {
35624 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.");
35625 return result;
35626 }
35627 }
35628
35629 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
35630 ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
35631 if (result != MA_SUCCESS) {
35632 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.");
35633 return result;
35634 }
35635 }
35636
35637 return MA_SUCCESS;
35638}
35639
35640/*
35641Note on Starting and Stopping
35642=============================
35643In the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when
35644trying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will
35645fail. Instead what we need to do is just not write or read to and from the device when the
35646device is not running.
35647
35648As a result, both the start and stop functions for OSS are just empty stubs. The starting and
35649stopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check
35650the device state, and if the device is stopped they will simply not do any kind of processing.
35651
35652The downside to this technique is that I've noticed a fairly lengthy delay in stopping the
35653device, up to a second. This is on a virtual machine, and as such might just be due to the
35654virtual drivers, but I'm not fully sure. I am not sure how to work around this problem so for
35655the moment that's just how it's going to have to be.
35656
35657When starting the device, OSS will automatically start it when write() or read() is called.
35658*/
35659static ma_result ma_device_start__oss(ma_device* pDevice)
35660{
35661 MA_ASSERT(pDevice != NULL);
35662
35663 /* The device is automatically started with reading and writing. */
35664 (void)pDevice;
35665
35666 return MA_SUCCESS;
35667}
35668
35669static ma_result ma_device_stop__oss(ma_device* pDevice)
35670{
35671 MA_ASSERT(pDevice != NULL);
35672
35673 /* See note above on why this is empty. */
35674 (void)pDevice;
35675
35676 return MA_SUCCESS;
35677}
35678
35679static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
35680{
35681 int resultOSS;
35682 ma_uint32 deviceState;
35683
35684 if (pFramesWritten != NULL) {
35685 *pFramesWritten = 0;
35686 }
35687
35688 /* Don't do any processing if the device is stopped. */
35689 deviceState = ma_device_get_state(pDevice);
35690 if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) {
35691 return MA_SUCCESS;
35692 }
35693
35694 resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
35695 if (resultOSS < 0) {
35696 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device.");
35697 return ma_result_from_errno(errno);
35698 }
35699
35700 if (pFramesWritten != NULL) {
35701 *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
35702 }
35703
35704 return MA_SUCCESS;
35705}
35706
35707static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
35708{
35709 int resultOSS;
35710 ma_uint32 deviceState;
35711
35712 if (pFramesRead != NULL) {
35713 *pFramesRead = 0;
35714 }
35715
35716 /* Don't do any processing if the device is stopped. */
35717 deviceState = ma_device_get_state(pDevice);
35718 if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) {
35719 return MA_SUCCESS;
35720 }
35721
35722 resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
35723 if (resultOSS < 0) {
35724 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.");
35725 return ma_result_from_errno(errno);
35726 }
35727
35728 if (pFramesRead != NULL) {
35729 *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
35730 }
35731
35732 return MA_SUCCESS;
35733}
35734
35735static ma_result ma_context_uninit__oss(ma_context* pContext)
35736{
35737 MA_ASSERT(pContext != NULL);
35738 MA_ASSERT(pContext->backend == ma_backend_oss);
35739
35740 (void)pContext;
35741 return MA_SUCCESS;
35742}
35743
35744static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
35745{
35746 int fd;
35747 int ossVersion;
35748 int result;
35749
35750 MA_ASSERT(pContext != NULL);
35751
35752 (void)pConfig;
35753
35754 /* Try opening a temporary device first so we can get version information. This is closed at the end. */
35755 fd = ma_open_temp_device__oss();
35756 if (fd == -1) {
35757 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. */
35758 return MA_NO_BACKEND;
35759 }
35760
35761 /* Grab the OSS version. */
35762 ossVersion = 0;
35763 result = ioctl(fd, OSS_GETVERSION, &ossVersion);
35764 if (result == -1) {
35765 close(fd);
35766 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version.");
35767 return MA_NO_BACKEND;
35768 }
35769
35770 /* The file handle to temp device is no longer needed. Close ASAP. */
35771 close(fd);
35772
35773 pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16);
35774 pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8);
35775
35776 pCallbacks->onContextInit = ma_context_init__oss;
35777 pCallbacks->onContextUninit = ma_context_uninit__oss;
35778 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss;
35779 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss;
35780 pCallbacks->onDeviceInit = ma_device_init__oss;
35781 pCallbacks->onDeviceUninit = ma_device_uninit__oss;
35782 pCallbacks->onDeviceStart = ma_device_start__oss;
35783 pCallbacks->onDeviceStop = ma_device_stop__oss;
35784 pCallbacks->onDeviceRead = ma_device_read__oss;
35785 pCallbacks->onDeviceWrite = ma_device_write__oss;
35786 pCallbacks->onDeviceDataLoop = NULL;
35787
35788 return MA_SUCCESS;
35789}
35790#endif /* OSS */
35791
35792
35793
35798#ifdef MA_HAS_AAUDIO
35799
35800/*#include <AAudio/AAudio.h>*/
35801
35802typedef int32_t ma_aaudio_result_t;
35803typedef int32_t ma_aaudio_direction_t;
35804typedef int32_t ma_aaudio_sharing_mode_t;
35805typedef int32_t ma_aaudio_format_t;
35806typedef int32_t ma_aaudio_stream_state_t;
35807typedef int32_t ma_aaudio_performance_mode_t;
35808typedef int32_t ma_aaudio_usage_t;
35809typedef int32_t ma_aaudio_content_type_t;
35810typedef int32_t ma_aaudio_input_preset_t;
35811typedef int32_t ma_aaudio_data_callback_result_t;
35812typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder;
35813typedef struct ma_AAudioStream_t* ma_AAudioStream;
35814
35815#define MA_AAUDIO_UNSPECIFIED 0
35816
35817/* Result codes. miniaudio only cares about the success code. */
35818#define MA_AAUDIO_OK 0
35819
35820/* Directions. */
35821#define MA_AAUDIO_DIRECTION_OUTPUT 0
35822#define MA_AAUDIO_DIRECTION_INPUT 1
35823
35824/* Sharing modes. */
35825#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0
35826#define MA_AAUDIO_SHARING_MODE_SHARED 1
35827
35828/* Formats. */
35829#define MA_AAUDIO_FORMAT_PCM_I16 1
35830#define MA_AAUDIO_FORMAT_PCM_FLOAT 2
35831
35832/* Stream states. */
35833#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0
35834#define MA_AAUDIO_STREAM_STATE_UNKNOWN 1
35835#define MA_AAUDIO_STREAM_STATE_OPEN 2
35836#define MA_AAUDIO_STREAM_STATE_STARTING 3
35837#define MA_AAUDIO_STREAM_STATE_STARTED 4
35838#define MA_AAUDIO_STREAM_STATE_PAUSING 5
35839#define MA_AAUDIO_STREAM_STATE_PAUSED 6
35840#define MA_AAUDIO_STREAM_STATE_FLUSHING 7
35841#define MA_AAUDIO_STREAM_STATE_FLUSHED 8
35842#define MA_AAUDIO_STREAM_STATE_STOPPING 9
35843#define MA_AAUDIO_STREAM_STATE_STOPPED 10
35844#define MA_AAUDIO_STREAM_STATE_CLOSING 11
35845#define MA_AAUDIO_STREAM_STATE_CLOSED 12
35846#define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13
35847
35848/* Performance modes. */
35849#define MA_AAUDIO_PERFORMANCE_MODE_NONE 10
35850#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11
35851#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12
35852
35853/* Usage types. */
35854#define MA_AAUDIO_USAGE_MEDIA 1
35855#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2
35856#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3
35857#define MA_AAUDIO_USAGE_ALARM 4
35858#define MA_AAUDIO_USAGE_NOTIFICATION 5
35859#define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6
35860#define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10
35861#define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11
35862#define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12
35863#define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13
35864#define MA_AAUDIO_USAGE_GAME 14
35865#define MA_AAUDIO_USAGE_ASSISTANT 16
35866#define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000
35867#define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001
35868#define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002
35869#define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003
35870
35871/* Content types. */
35872#define MA_AAUDIO_CONTENT_TYPE_SPEECH 1
35873#define MA_AAUDIO_CONTENT_TYPE_MUSIC 2
35874#define MA_AAUDIO_CONTENT_TYPE_MOVIE 3
35875#define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4
35876
35877/* Input presets. */
35878#define MA_AAUDIO_INPUT_PRESET_GENERIC 1
35879#define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5
35880#define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6
35881#define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7
35882#define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9
35883#define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10
35884
35885/* Callback results. */
35886#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0
35887#define MA_AAUDIO_CALLBACK_RESULT_STOP 1
35888
35889
35890typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames);
35891typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error);
35892
35893typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder);
35894typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder);
35895typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId);
35896typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction);
35897typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode);
35898typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format);
35899typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount);
35900typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate);
35901typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
35902typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
35903typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData);
35904typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData);
35905typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode);
35906typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType);
35907typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType);
35908typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset);
35909typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream);
35910typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream);
35911typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream);
35912typedef 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);
35913typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream);
35914typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream);
35915typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream);
35916typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream);
35917typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream);
35918typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream);
35919typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream);
35920typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream);
35921
35922static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA)
35923{
35924 switch (resultAA)
35925 {
35926 case MA_AAUDIO_OK: return MA_SUCCESS;
35927 default: break;
35928 }
35929
35930 return MA_ERROR;
35931}
35932
35933static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage)
35934{
35935 switch (usage) {
35936 case ma_aaudio_usage_announcement: return MA_AAUDIO_USAGE_MEDIA;
35937 case ma_aaudio_usage_emergency: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION;
35938 case ma_aaudio_usage_safety: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
35939 case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_USAGE_ALARM;
35940 case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_NOTIFICATION;
35941 case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE;
35942 case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT;
35943 case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
35944 case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
35945 case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION;
35946 case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_GAME;
35947 case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_ASSISTANT;
35948 case ma_aaudio_usage_notification_event: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY;
35949 case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_SYSTEM_USAGE_SAFETY;
35950 case ma_aaudio_usage_voice_communication: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS;
35951 case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT;
35952 default: break;
35953 }
35954
35955 return MA_AAUDIO_USAGE_MEDIA;
35956}
35957
35958static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType)
35959{
35960 switch (contentType) {
35961 case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE;
35962 case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC;
35963 case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION;
35964 case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH;
35965 default: break;
35966 }
35967
35968 return MA_AAUDIO_CONTENT_TYPE_SPEECH;
35969}
35970
35971static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset)
35972{
35973 switch (inputPreset) {
35974 case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC;
35975 case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER;
35976 case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED;
35977 case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION;
35978 case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION;
35979 case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE;
35980 default: break;
35981 }
35982
35983 return MA_AAUDIO_INPUT_PRESET_GENERIC;
35984}
35985
35986static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error)
35987{
35988 ma_device* pDevice = (ma_device*)pUserData;
35989 MA_ASSERT(pDevice != NULL);
35990
35991 (void)error;
35992
35993 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));
35994
35995 /*
35996 From the documentation for AAudio, when a device is disconnected all we can do is stop it. However, we cannot stop it from the callback - we need
35997 to do it from another thread. Therefore we are going to use an event thread for the AAudio backend to do this cleanly and safely.
35998 */
35999 if (((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream) == MA_AAUDIO_STREAM_STATE_DISCONNECTED) {
36000 /* We need to post a job to the job thread for processing. This will reroute the device by reinitializing the stream. */
36001 ma_result result;
36003 job.data.device.aaudio.reroute.pDevice = pDevice;
36004
36005 if (pStream == pDevice->aaudio.pStreamCapture) {
36007 } else {
36009 }
36010
36011 result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job);
36012 if (result != MA_SUCCESS) {
36013 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n");
36014 return;
36015 }
36016 }
36017}
36018
36019static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
36020{
36021 ma_device* pDevice = (ma_device*)pUserData;
36022 MA_ASSERT(pDevice != NULL);
36023
36024 ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, frameCount);
36025
36026 (void)pStream;
36027 return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
36028}
36029
36030static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
36031{
36032 ma_device* pDevice = (ma_device*)pUserData;
36033 MA_ASSERT(pDevice != NULL);
36034
36035 ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, frameCount);
36036
36037 (void)pStream;
36038 return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
36039}
36040
36041static 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)
36042{
36043 ma_AAudioStreamBuilder* pBuilder;
36044 ma_aaudio_result_t resultAA;
36045 ma_uint32 bufferCapacityInFrames;
36046
36047 /* Safety. */
36048 *ppBuilder = NULL;
36049
36050 resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder);
36051 if (resultAA != MA_AAUDIO_OK) {
36052 return ma_result_from_aaudio(resultAA);
36053 }
36054
36055 if (pDeviceID != NULL) {
36056 ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio);
36057 }
36058
36059 ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT);
36060 ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE);
36061
36062
36063 /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */
36064 if (pDescriptor != NULL) {
36065 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. */
36066
36067 if (pDescriptor->sampleRate != 0) {
36068 ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate);
36069 }
36070
36071 if (deviceType == ma_device_type_capture) {
36072 if (pDescriptor->channels != 0) {
36073 ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
36074 }
36075 if (pDescriptor->format != ma_format_unknown) {
36076 ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
36077 }
36078 } else {
36079 if (pDescriptor->channels != 0) {
36080 ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
36081 }
36082 if (pDescriptor->format != ma_format_unknown) {
36083 ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
36084 }
36085 }
36086
36087 /*
36088 AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you
36089 retrieve the actual sample rate until after you've opened the stream. But you need to configure
36090 the buffer capacity before you open the stream... :/
36091
36092 To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on.
36093 */
36094 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount;
36095
36096 ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames);
36097 ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount);
36098
36099 if (deviceType == ma_device_type_capture) {
36100 if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) {
36101 ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset));
36102 }
36103
36104 ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice);
36105 } else {
36106 if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) {
36107 ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage));
36108 }
36109
36110 if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) {
36111 ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType));
36112 }
36113
36114 ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice);
36115 }
36116
36117 /* Not sure how this affects things, but since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let go ahead and set it. */
36118 ((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);
36119
36120 /* We need to set an error callback to detect device changes. */
36121 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. */
36122 ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice);
36123 }
36124 }
36125
36126 *ppBuilder = pBuilder;
36127
36128 return MA_SUCCESS;
36129}
36130
36131static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream)
36132{
36133 ma_result result;
36134
36135 result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream));
36136 ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
36137
36138 return result;
36139}
36140
36141static 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)
36142{
36143 ma_result result;
36144 ma_AAudioStreamBuilder* pBuilder;
36145
36146 *ppStream = NULL;
36147
36148 result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder);
36149 if (result != MA_SUCCESS) {
36150 return result;
36151 }
36152
36153 return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream);
36154}
36155
36156static 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)
36157{
36158 ma_result result;
36159 ma_AAudioStreamBuilder* pBuilder;
36160
36161 MA_ASSERT(pDevice != NULL);
36162 MA_ASSERT(pDescriptor != NULL);
36163 MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */
36164
36165 *ppStream = NULL;
36166
36167 result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder);
36168 if (result != MA_SUCCESS) {
36169 return result;
36170 }
36171
36172 return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream);
36173}
36174
36175static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream)
36176{
36177 return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream));
36178}
36179
36180static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType)
36181{
36182 /* The only way to know this is to try creating a stream. */
36183 ma_AAudioStream* pStream;
36184 ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream);
36185 if (result != MA_SUCCESS) {
36186 return MA_FALSE;
36187 }
36188
36189 ma_close_stream__aaudio(pContext, pStream);
36190 return MA_TRUE;
36191}
36192
36193static 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)
36194{
36195 ma_aaudio_stream_state_t actualNewState;
36196 ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */
36197 if (resultAA != MA_AAUDIO_OK) {
36198 return ma_result_from_aaudio(resultAA);
36199 }
36200
36201 if (newState != actualNewState) {
36202 return MA_ERROR; /* Failed to transition into the expected state. */
36203 }
36204
36205 return MA_SUCCESS;
36206}
36207
36208
36209static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
36210{
36211 ma_bool32 cbResult = MA_TRUE;
36212
36213 MA_ASSERT(pContext != NULL);
36214 MA_ASSERT(callback != NULL);
36215
36216 /* 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. */
36217
36218 /* Playback. */
36219 if (cbResult) {
36220 ma_device_info deviceInfo;
36221 MA_ZERO_OBJECT(&deviceInfo);
36222 deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
36223 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
36224
36225 if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) {
36226 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
36227 }
36228 }
36229
36230 /* Capture. */
36231 if (cbResult) {
36232 ma_device_info deviceInfo;
36233 MA_ZERO_OBJECT(&deviceInfo);
36234 deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
36235 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
36236
36237 if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) {
36238 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
36239 }
36240 }
36241
36242 return MA_SUCCESS;
36243}
36244
36245static 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)
36246{
36247 MA_ASSERT(pContext != NULL);
36248 MA_ASSERT(pStream != NULL);
36249 MA_ASSERT(pDeviceInfo != NULL);
36250
36251 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
36252 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream);
36253 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream);
36254 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
36255 pDeviceInfo->nativeDataFormatCount += 1;
36256}
36257
36258static void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo)
36259{
36260 /* AAudio supports s16 and f32. */
36261 ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo);
36262 ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo);
36263}
36264
36265static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
36266{
36267 ma_AAudioStream* pStream;
36268 ma_result result;
36269
36270 MA_ASSERT(pContext != NULL);
36271
36272 /* ID */
36273 if (pDeviceID != NULL) {
36274 pDeviceInfo->id.aaudio = pDeviceID->aaudio;
36275 } else {
36276 pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED;
36277 }
36278
36279 /* Name */
36280 if (deviceType == ma_device_type_playback) {
36281 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
36282 } else {
36283 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
36284 }
36285
36286
36287 pDeviceInfo->nativeDataFormatCount = 0;
36288
36289 /* We'll need to open the device to get accurate sample rate and channel count information. */
36290 result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream);
36291 if (result != MA_SUCCESS) {
36292 return result;
36293 }
36294
36295 ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo);
36296
36297 ma_close_stream__aaudio(pContext, pStream);
36298 pStream = NULL;
36299
36300 return MA_SUCCESS;
36301}
36302
36303
36304static ma_result ma_device_uninit__aaudio(ma_device* pDevice)
36305{
36306 MA_ASSERT(pDevice != NULL);
36307
36308 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
36309 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
36310 pDevice->aaudio.pStreamCapture = NULL;
36311 }
36312
36313 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
36314 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
36315 pDevice->aaudio.pStreamPlayback = NULL;
36316 }
36317
36318 return MA_SUCCESS;
36319}
36320
36321static 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)
36322{
36323 ma_result result;
36324 int32_t bufferCapacityInFrames;
36325 int32_t framesPerDataCallback;
36326 ma_AAudioStream* pStream;
36327
36328 MA_ASSERT(pDevice != NULL);
36329 MA_ASSERT(pConfig != NULL);
36330 MA_ASSERT(pDescriptor != NULL);
36331
36332 *ppStream = NULL; /* Safety. */
36333
36334 /* First step is to open the stream. From there we'll be able to extract the internal configuration. */
36335 result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream);
36336 if (result != MA_SUCCESS) {
36337 return result; /* Failed to open the AAudio stream. */
36338 }
36339
36340 /* Now extract the internal configuration. */
36341 pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32;
36342 pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream);
36343 pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream);
36344
36345 /* For the channel map we need to be sure we don't overflow any buffers. */
36346 if (pDescriptor->channels <= MA_MAX_CHANNELS) {
36347 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. */
36348 } else {
36349 ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS); /* Too many channels. Use a blank channel map. */
36350 }
36351
36352 bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream);
36353 framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream);
36354
36355 if (framesPerDataCallback > 0) {
36356 pDescriptor->periodSizeInFrames = framesPerDataCallback;
36357 pDescriptor->periodCount = bufferCapacityInFrames / framesPerDataCallback;
36358 } else {
36359 pDescriptor->periodSizeInFrames = bufferCapacityInFrames;
36360 pDescriptor->periodCount = 1;
36361 }
36362
36363 *ppStream = pStream;
36364
36365 return MA_SUCCESS;
36366}
36367
36368static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
36369{
36370 ma_result result;
36371
36372 MA_ASSERT(pDevice != NULL);
36373
36374 if (pConfig->deviceType == ma_device_type_loopback) {
36376 }
36377
36378 pDevice->aaudio.usage = pConfig->aaudio.usage;
36379 pDevice->aaudio.contentType = pConfig->aaudio.contentType;
36380 pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset;
36381 pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute;
36382
36383 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
36384 result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture);
36385 if (result != MA_SUCCESS) {
36386 return result;
36387 }
36388 }
36389
36390 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
36391 result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback);
36392 if (result != MA_SUCCESS) {
36393 return result;
36394 }
36395 }
36396
36397 return MA_SUCCESS;
36398}
36399
36400static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
36401{
36402 ma_aaudio_result_t resultAA;
36403 ma_aaudio_stream_state_t currentState;
36404
36405 MA_ASSERT(pDevice != NULL);
36406
36407 resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream);
36408 if (resultAA != MA_AAUDIO_OK) {
36409 return ma_result_from_aaudio(resultAA);
36410 }
36411
36412 /* Do we actually need to wait for the device to transition into it's started state? */
36413
36414 /* 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. */
36415 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
36416 if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) {
36417 ma_result result;
36418
36419 if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) {
36420 return MA_ERROR; /* Expecting the stream to be a starting or started state. */
36421 }
36422
36423 result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED);
36424 if (result != MA_SUCCESS) {
36425 return result;
36426 }
36427 }
36428
36429 return MA_SUCCESS;
36430}
36431
36432static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
36433{
36434 ma_aaudio_result_t resultAA;
36435 ma_aaudio_stream_state_t currentState;
36436
36437 MA_ASSERT(pDevice != NULL);
36438
36439 /*
36440 From the AAudio documentation:
36441
36442 The stream will stop after all of the data currently buffered has been played.
36443
36444 This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic.
36445 */
36446 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
36447 if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) {
36448 return MA_SUCCESS; /* The device is disconnected. Don't try stopping it. */
36449 }
36450
36451 resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream);
36452 if (resultAA != MA_AAUDIO_OK) {
36453 return ma_result_from_aaudio(resultAA);
36454 }
36455
36456 /* 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. */
36457 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
36458 if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) {
36459 ma_result result;
36460
36461 if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) {
36462 return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */
36463 }
36464
36465 result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED);
36466 if (result != MA_SUCCESS) {
36467 return result;
36468 }
36469 }
36470
36471 return MA_SUCCESS;
36472}
36473
36474static ma_result ma_device_start__aaudio(ma_device* pDevice)
36475{
36476 MA_ASSERT(pDevice != NULL);
36477
36478 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
36479 ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
36480 if (result != MA_SUCCESS) {
36481 return result;
36482 }
36483 }
36484
36485 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
36486 ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
36487 if (result != MA_SUCCESS) {
36488 if (pDevice->type == ma_device_type_duplex) {
36489 ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
36490 }
36491 return result;
36492 }
36493 }
36494
36495 return MA_SUCCESS;
36496}
36497
36498static ma_result ma_device_stop__aaudio(ma_device* pDevice)
36499{
36500 MA_ASSERT(pDevice != NULL);
36501
36502 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
36503 ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
36504 if (result != MA_SUCCESS) {
36505 return result;
36506 }
36507 }
36508
36509 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
36510 ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
36511 if (result != MA_SUCCESS) {
36512 return result;
36513 }
36514 }
36515
36516 ma_device__on_notification_stopped(pDevice);
36517
36518 return MA_SUCCESS;
36519}
36520
36521static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType)
36522{
36523 ma_result result;
36524
36525 MA_ASSERT(pDevice != NULL);
36526
36527 /* The first thing to do is close the streams. */
36528 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
36529 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
36530 pDevice->aaudio.pStreamCapture = NULL;
36531 }
36532
36533 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
36534 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
36535 pDevice->aaudio.pStreamPlayback = NULL;
36536 }
36537
36538 /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */
36539 {
36540 ma_device_config deviceConfig;
36541 ma_device_descriptor descriptorPlayback;
36542 ma_device_descriptor descriptorCapture;
36543
36544 deviceConfig = ma_device_config_init(deviceType);
36545 deviceConfig.playback.pDeviceID = NULL; /* Only doing rerouting with default devices. */
36546 deviceConfig.playback.shareMode = pDevice->playback.shareMode;
36547 deviceConfig.playback.format = pDevice->playback.format;
36548 deviceConfig.playback.channels = pDevice->playback.channels;
36549 deviceConfig.capture.pDeviceID = NULL; /* Only doing rerouting with default devices. */
36550 deviceConfig.capture.shareMode = pDevice->capture.shareMode;
36551 deviceConfig.capture.format = pDevice->capture.format;
36552 deviceConfig.capture.channels = pDevice->capture.channels;
36553 deviceConfig.sampleRate = pDevice->sampleRate;
36554 deviceConfig.aaudio.usage = pDevice->aaudio.usage;
36555 deviceConfig.aaudio.contentType = pDevice->aaudio.contentType;
36556 deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset;
36557 deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute;
36558 deviceConfig.periods = 1;
36559
36560 /* Try to get an accurate period size. */
36561 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
36563 } else {
36565 }
36566
36567 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
36568 descriptorCapture.pDeviceID = deviceConfig.capture.pDeviceID;
36569 descriptorCapture.shareMode = deviceConfig.capture.shareMode;
36570 descriptorCapture.format = deviceConfig.capture.format;
36571 descriptorCapture.channels = deviceConfig.capture.channels;
36572 descriptorCapture.sampleRate = deviceConfig.sampleRate;
36573 descriptorCapture.periodSizeInFrames = deviceConfig.periodSizeInFrames;
36574 descriptorCapture.periodCount = deviceConfig.periods;
36575 }
36576
36577 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
36578 descriptorPlayback.pDeviceID = deviceConfig.playback.pDeviceID;
36579 descriptorPlayback.shareMode = deviceConfig.playback.shareMode;
36580 descriptorPlayback.format = deviceConfig.playback.format;
36581 descriptorPlayback.channels = deviceConfig.playback.channels;
36582 descriptorPlayback.sampleRate = deviceConfig.sampleRate;
36583 descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames;
36584 descriptorPlayback.periodCount = deviceConfig.periods;
36585 }
36586
36587 result = ma_device_init__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture);
36588 if (result != MA_SUCCESS) {
36589 return result;
36590 }
36591
36592 result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture);
36593 if (result != MA_SUCCESS) {
36594 ma_device_uninit__aaudio(pDevice);
36595 return result;
36596 }
36597
36598 /* We'll only ever do this in response to a reroute. */
36599 ma_device__on_notification_rerouted(pDevice);
36600
36601 /* If the device is started, start the streams. Maybe make this configurable? */
36603 if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) {
36604 ma_device_start__aaudio(pDevice);
36605 } else {
36606 ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */
36607 }
36608 }
36609
36610 return MA_SUCCESS;
36611 }
36612}
36613
36614static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo)
36615{
36616 ma_AAudioStream* pStream = NULL;
36617
36618 MA_ASSERT(pDevice != NULL);
36619 MA_ASSERT(type != ma_device_type_duplex);
36620 MA_ASSERT(pDeviceInfo != NULL);
36621
36622 if (type == ma_device_type_playback) {
36623 pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture;
36624 pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio;
36625 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */
36626 }
36627 if (type == ma_device_type_capture) {
36628 pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback;
36629 pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio;
36630 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */
36631 }
36632
36633 /* Safety. Should never happen. */
36634 if (pStream == NULL) {
36635 return MA_INVALID_OPERATION;
36636 }
36637
36638 pDeviceInfo->nativeDataFormatCount = 0;
36639 ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo);
36640
36641 return MA_SUCCESS;
36642}
36643
36644
36645static ma_result ma_context_uninit__aaudio(ma_context* pContext)
36646{
36647 MA_ASSERT(pContext != NULL);
36648 MA_ASSERT(pContext->backend == ma_backend_aaudio);
36649
36650 ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks);
36651
36652 ma_dlclose(pContext, pContext->aaudio.hAAudio);
36653 pContext->aaudio.hAAudio = NULL;
36654
36655 return MA_SUCCESS;
36656}
36657
36658static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
36659{
36660 size_t i;
36661 const char* libNames[] = {
36662 "libaaudio.so"
36663 };
36664
36665 for (i = 0; i < ma_countof(libNames); ++i) {
36666 pContext->aaudio.hAAudio = ma_dlopen(pContext, libNames[i]);
36667 if (pContext->aaudio.hAAudio != NULL) {
36668 break;
36669 }
36670 }
36671
36672 if (pContext->aaudio.hAAudio == NULL) {
36674 }
36675
36676 pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudio_createStreamBuilder");
36677 pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete");
36678 pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId");
36679 pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection");
36680 pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode");
36681 pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat");
36682 pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount");
36683 pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate");
36684 pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames");
36685 pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback");
36686 pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback");
36687 pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback");
36688 pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode");
36689 pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage");
36690 pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType");
36691 pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset");
36692 pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream");
36693 pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close");
36694 pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState");
36695 pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange");
36696 pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFormat");
36697 pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getChannelCount");
36698 pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getSampleRate");
36699 pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames");
36700 pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback");
36701 pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst");
36702 pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStart");
36703 pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStop");
36704
36705
36706 pCallbacks->onContextInit = ma_context_init__aaudio;
36707 pCallbacks->onContextUninit = ma_context_uninit__aaudio;
36708 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio;
36709 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio;
36710 pCallbacks->onDeviceInit = ma_device_init__aaudio;
36711 pCallbacks->onDeviceUninit = ma_device_uninit__aaudio;
36712 pCallbacks->onDeviceStart = ma_device_start__aaudio;
36713 pCallbacks->onDeviceStop = ma_device_stop__aaudio;
36714 pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */
36715 pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */
36716 pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */
36717 pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio;
36718
36719
36720 /* We need a job thread so we can deal with rerouting. */
36721 {
36722 ma_result result;
36723 ma_device_job_thread_config jobThreadConfig;
36724
36725 jobThreadConfig = ma_device_job_thread_config_init();
36726
36727 result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread);
36728 if (result != MA_SUCCESS) {
36729 ma_dlclose(pContext, pContext->aaudio.hAAudio);
36730 pContext->aaudio.hAAudio = NULL;
36731 return result;
36732 }
36733 }
36734
36735
36736 (void)pConfig;
36737 return MA_SUCCESS;
36738}
36739
36740static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)
36741{
36742 ma_device* pDevice;
36743
36744 MA_ASSERT(pJob != NULL);
36745
36746 pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice;
36747 MA_ASSERT(pDevice != NULL);
36748
36749 /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */
36750 return ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType);
36751}
36752#else
36753/* Getting here means there is no AAudio backend so we need a no-op job implementation. */
36754static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)
36755{
36756 return ma_job_process__noop(pJob);
36757}
36758#endif /* AAudio */
36759
36760
36761
36766#ifdef MA_HAS_OPENSL
36767#include <SLES/OpenSLES.h>
36768#ifdef MA_ANDROID
36769#include <SLES/OpenSLES_Android.h>
36770#endif
36771
36772typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired);
36773
36774/* OpenSL|ES has one-per-application objects :( */
36775static SLObjectItf g_maEngineObjectSL = NULL;
36776static SLEngineItf g_maEngineSL = NULL;
36777static ma_uint32 g_maOpenSLInitCounter = 0;
36778static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */
36779
36780#define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p)))
36781#define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p)))
36782#define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p)))
36783#define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p)))
36784
36785#ifdef MA_ANDROID
36786#define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p)))
36787#else
36788#define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p)))
36789#endif
36790
36791static ma_result ma_result_from_OpenSL(SLuint32 result)
36792{
36793 switch (result)
36794 {
36795 case SL_RESULT_SUCCESS: return MA_SUCCESS;
36796 case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR;
36797 case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS;
36798 case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY;
36799 case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA;
36800 case SL_RESULT_RESOURCE_LOST: return MA_ERROR;
36801 case SL_RESULT_IO_ERROR: return MA_IO_ERROR;
36802 case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE;
36803 case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA;
36804 case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED;
36805 case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR;
36806 case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED;
36807 case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED;
36808 case SL_RESULT_INTERNAL_ERROR: return MA_ERROR;
36809 case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR;
36810 case SL_RESULT_OPERATION_ABORTED: return MA_ERROR;
36811 case SL_RESULT_CONTROL_LOST: return MA_ERROR;
36812 default: return MA_ERROR;
36813 }
36814}
36815
36816/* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
36817static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id)
36818{
36819 switch (id)
36820 {
36821 case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
36822 case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
36823 case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
36824 case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
36825 case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
36826 case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
36827 case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
36828 case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
36829 case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
36830 case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
36831 case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
36832 case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
36833 case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
36834 case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
36835 case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
36836 case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
36837 case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
36838 case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
36839 default: return 0;
36840 }
36841}
36842
36843/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */
36844static SLuint32 ma_channel_id_to_opensl(ma_uint8 id)
36845{
36846 switch (id)
36847 {
36848 case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER;
36849 case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT;
36850 case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT;
36851 case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER;
36852 case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY;
36853 case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT;
36854 case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT;
36855 case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER;
36856 case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER;
36857 case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER;
36858 case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT;
36859 case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT;
36860 case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER;
36861 case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT;
36862 case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER;
36863 case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT;
36864 case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT;
36865 case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER;
36866 case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT;
36867 default: return 0;
36868 }
36869}
36870
36871/* Converts a channel mapping to an OpenSL-style channel mask. */
36872static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels)
36873{
36874 SLuint32 channelMask = 0;
36875 ma_uint32 iChannel;
36876 for (iChannel = 0; iChannel < channels; ++iChannel) {
36877 channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]);
36878 }
36879
36880 return channelMask;
36881}
36882
36883/* Converts an OpenSL-style channel mask to a miniaudio channel map. */
36884static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap)
36885{
36886 if (channels == 1 && channelMask == 0) {
36887 pChannelMap[0] = MA_CHANNEL_MONO;
36888 } else if (channels == 2 && channelMask == 0) {
36889 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
36890 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
36891 } else {
36892 if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) {
36893 pChannelMap[0] = MA_CHANNEL_MONO;
36894 } else {
36895 /* Just iterate over each bit. */
36896 ma_uint32 iChannel = 0;
36897 ma_uint32 iBit;
36898 for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {
36899 SLuint32 bitValue = (channelMask & (1UL << iBit));
36900 if (bitValue != 0) {
36901 /* The bit is set. */
36902 pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue);
36903 iChannel += 1;
36904 }
36905 }
36906 }
36907 }
36908}
36909
36910static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec)
36911{
36912 if (samplesPerSec <= SL_SAMPLINGRATE_8) {
36913 return SL_SAMPLINGRATE_8;
36914 }
36915 if (samplesPerSec <= SL_SAMPLINGRATE_11_025) {
36916 return SL_SAMPLINGRATE_11_025;
36917 }
36918 if (samplesPerSec <= SL_SAMPLINGRATE_12) {
36919 return SL_SAMPLINGRATE_12;
36920 }
36921 if (samplesPerSec <= SL_SAMPLINGRATE_16) {
36922 return SL_SAMPLINGRATE_16;
36923 }
36924 if (samplesPerSec <= SL_SAMPLINGRATE_22_05) {
36925 return SL_SAMPLINGRATE_22_05;
36926 }
36927 if (samplesPerSec <= SL_SAMPLINGRATE_24) {
36928 return SL_SAMPLINGRATE_24;
36929 }
36930 if (samplesPerSec <= SL_SAMPLINGRATE_32) {
36931 return SL_SAMPLINGRATE_32;
36932 }
36933 if (samplesPerSec <= SL_SAMPLINGRATE_44_1) {
36934 return SL_SAMPLINGRATE_44_1;
36935 }
36936 if (samplesPerSec <= SL_SAMPLINGRATE_48) {
36937 return SL_SAMPLINGRATE_48;
36938 }
36939
36940 /* Android doesn't support more than 48000. */
36941#ifndef MA_ANDROID
36942 if (samplesPerSec <= SL_SAMPLINGRATE_64) {
36943 return SL_SAMPLINGRATE_64;
36944 }
36945 if (samplesPerSec <= SL_SAMPLINGRATE_88_2) {
36946 return SL_SAMPLINGRATE_88_2;
36947 }
36948 if (samplesPerSec <= SL_SAMPLINGRATE_96) {
36949 return SL_SAMPLINGRATE_96;
36950 }
36951 if (samplesPerSec <= SL_SAMPLINGRATE_192) {
36952 return SL_SAMPLINGRATE_192;
36953 }
36954#endif
36955
36956 return SL_SAMPLINGRATE_16;
36957}
36958
36959
36960static SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType)
36961{
36962 switch (streamType) {
36963 case ma_opensl_stream_type_voice: return SL_ANDROID_STREAM_VOICE;
36964 case ma_opensl_stream_type_system: return SL_ANDROID_STREAM_SYSTEM;
36965 case ma_opensl_stream_type_ring: return SL_ANDROID_STREAM_RING;
36966 case ma_opensl_stream_type_media: return SL_ANDROID_STREAM_MEDIA;
36967 case ma_opensl_stream_type_alarm: return SL_ANDROID_STREAM_ALARM;
36968 case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION;
36969 default: break;
36970 }
36971
36972 return SL_ANDROID_STREAM_VOICE;
36973}
36974
36975static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset)
36976{
36977 switch (recordingPreset) {
36978 case ma_opensl_recording_preset_generic: return SL_ANDROID_RECORDING_PRESET_GENERIC;
36979 case ma_opensl_recording_preset_camcorder: return SL_ANDROID_RECORDING_PRESET_CAMCORDER;
36980 case ma_opensl_recording_preset_voice_recognition: return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
36981 case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION;
36982 case ma_opensl_recording_preset_voice_unprocessed: return SL_ANDROID_RECORDING_PRESET_UNPROCESSED;
36983 default: break;
36984 }
36985
36986 return SL_ANDROID_RECORDING_PRESET_NONE;
36987}
36988
36989
36990static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
36991{
36992 ma_bool32 cbResult;
36993
36994 MA_ASSERT(pContext != NULL);
36995 MA_ASSERT(callback != NULL);
36996
36997 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. */
36998 if (g_maOpenSLInitCounter == 0) {
36999 return MA_INVALID_OPERATION;
37000 }
37001
37002 /*
37003 TODO: Test Me.
37004
37005 This is currently untested, so for now we are just returning default devices.
37006 */
37007#if 0 && !defined(MA_ANDROID)
37008 ma_bool32 isTerminated = MA_FALSE;
37009
37010 SLuint32 pDeviceIDs[128];
37011 SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]);
37012
37013 SLAudioIODeviceCapabilitiesItf deviceCaps;
37014 SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
37015 if (resultSL != SL_RESULT_SUCCESS) {
37016 /* The interface may not be supported so just report a default device. */
37017 goto return_default_device;
37018 }
37019
37020 /* Playback */
37021 if (!isTerminated) {
37022 resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs);
37023 if (resultSL != SL_RESULT_SUCCESS) {
37024 return ma_result_from_OpenSL(resultSL);
37025 }
37026
37027 for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
37028 ma_device_info deviceInfo;
37029 MA_ZERO_OBJECT(&deviceInfo);
37030 deviceInfo.id.opensl = pDeviceIDs[iDevice];
37031
37032 SLAudioOutputDescriptor desc;
37033 resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
37034 if (resultSL == SL_RESULT_SUCCESS) {
37035 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1);
37036
37037 ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
37038 if (cbResult == MA_FALSE) {
37039 isTerminated = MA_TRUE;
37040 break;
37041 }
37042 }
37043 }
37044 }
37045
37046 /* Capture */
37047 if (!isTerminated) {
37048 resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs);
37049 if (resultSL != SL_RESULT_SUCCESS) {
37050 return ma_result_from_OpenSL(resultSL);
37051 }
37052
37053 for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
37054 ma_device_info deviceInfo;
37055 MA_ZERO_OBJECT(&deviceInfo);
37056 deviceInfo.id.opensl = pDeviceIDs[iDevice];
37057
37058 SLAudioInputDescriptor desc;
37059 resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
37060 if (resultSL == SL_RESULT_SUCCESS) {
37061 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1);
37062
37063 ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
37064 if (cbResult == MA_FALSE) {
37065 isTerminated = MA_TRUE;
37066 break;
37067 }
37068 }
37069 }
37070 }
37071
37072 return MA_SUCCESS;
37073#else
37074 goto return_default_device;
37075#endif
37076
37077return_default_device:;
37078 cbResult = MA_TRUE;
37079
37080 /* Playback. */
37081 if (cbResult) {
37082 ma_device_info deviceInfo;
37083 MA_ZERO_OBJECT(&deviceInfo);
37084 deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT;
37085 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
37086 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
37087 }
37088
37089 /* Capture. */
37090 if (cbResult) {
37091 ma_device_info deviceInfo;
37092 MA_ZERO_OBJECT(&deviceInfo);
37093 deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT;
37094 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
37095 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
37096 }
37097
37098 return MA_SUCCESS;
37099}
37100
37101static void ma_context_add_data_format_ex__opensl(ma_context* pContext, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_device_info* pDeviceInfo)
37102{
37103 MA_ASSERT(pContext != NULL);
37104 MA_ASSERT(pDeviceInfo != NULL);
37105
37106 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
37107 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
37108 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
37109 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
37110 pDeviceInfo->nativeDataFormatCount += 1;
37111}
37112
37113static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo)
37114{
37115 ma_uint32 minChannels = 1;
37116 ma_uint32 maxChannels = 2;
37119 ma_uint32 iChannel;
37120 ma_uint32 iSampleRate;
37121
37122 MA_ASSERT(pContext != NULL);
37123 MA_ASSERT(pDeviceInfo != NULL);
37124
37125 /*
37126 Each sample format can support mono and stereo, and we'll support a small subset of standard
37127 rates (up to 48000). A better solution would be to somehow find a native sample rate.
37128 */
37129 for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) {
37130 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {
37131 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];
37132 if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {
37133 ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo);
37134 }
37135 }
37136 }
37137}
37138
37139static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
37140{
37141 MA_ASSERT(pContext != NULL);
37142
37143 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. */
37144 if (g_maOpenSLInitCounter == 0) {
37145 return MA_INVALID_OPERATION;
37146 }
37147
37148 /*
37149 TODO: Test Me.
37150
37151 This is currently untested, so for now we are just returning default devices.
37152 */
37153#if 0 && !defined(MA_ANDROID)
37154 SLAudioIODeviceCapabilitiesItf deviceCaps;
37155 SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
37156 if (resultSL != SL_RESULT_SUCCESS) {
37157 /* The interface may not be supported so just report a default device. */
37158 goto return_default_device;
37159 }
37160
37161 if (deviceType == ma_device_type_playback) {
37162 SLAudioOutputDescriptor desc;
37163 resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
37164 if (resultSL != SL_RESULT_SUCCESS) {
37165 return ma_result_from_OpenSL(resultSL);
37166 }
37167
37168 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1);
37169 } else {
37170 SLAudioInputDescriptor desc;
37171 resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
37172 if (resultSL != SL_RESULT_SUCCESS) {
37173 return ma_result_from_OpenSL(resultSL);
37174 }
37175
37176 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1);
37177 }
37178
37179 goto return_detailed_info;
37180#else
37181 goto return_default_device;
37182#endif
37183
37184return_default_device:
37185 if (pDeviceID != NULL) {
37186 if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) ||
37187 (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) {
37188 return MA_NO_DEVICE; /* Don't know the device. */
37189 }
37190 }
37191
37192 /* ID and Name / Description */
37193 if (deviceType == ma_device_type_playback) {
37194 pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT;
37195 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
37196 } else {
37197 pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT;
37198 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
37199 }
37200
37201 pDeviceInfo->isDefault = MA_TRUE;
37202
37203 goto return_detailed_info;
37204
37205
37206return_detailed_info:
37207
37208 /*
37209 For now we're just outputting a set of values that are supported by the API but not necessarily supported
37210 by the device natively. Later on we should work on this so that it more closely reflects the device's
37211 actual native format.
37212 */
37213 pDeviceInfo->nativeDataFormatCount = 0;
37214#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
37215 ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo);
37216#endif
37217 ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo);
37218 ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo);
37219
37220 return MA_SUCCESS;
37221}
37222
37223
37224#ifdef MA_ANDROID
37225/*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/
37226static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
37227{
37228 ma_device* pDevice = (ma_device*)pUserData;
37229 size_t periodSizeInBytes;
37230 ma_uint8* pBuffer;
37231 SLresult resultSL;
37232
37233 MA_ASSERT(pDevice != NULL);
37234
37235 (void)pBufferQueue;
37236
37237 /*
37238 For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like
37239 OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this,
37240 but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :(
37241 */
37242
37243 /* Don't do anything if the device is not started. */
37245 return;
37246 }
37247
37248 /* Don't do anything if the device is being drained. */
37249 if (pDevice->opensl.isDrainingCapture) {
37250 return;
37251 }
37252
37253 periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
37254 pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes);
37255
37257
37258 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes);
37259 if (resultSL != SL_RESULT_SUCCESS) {
37260 return;
37261 }
37262
37263 pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods;
37264}
37265
37266static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
37267{
37268 ma_device* pDevice = (ma_device*)pUserData;
37269 size_t periodSizeInBytes;
37270 ma_uint8* pBuffer;
37271 SLresult resultSL;
37272
37273 MA_ASSERT(pDevice != NULL);
37274
37275 (void)pBufferQueue;
37276
37277 /* Don't do anything if the device is not started. */
37279 return;
37280 }
37281
37282 /* Don't do anything if the device is being drained. */
37283 if (pDevice->opensl.isDrainingPlayback) {
37284 return;
37285 }
37286
37287 periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
37288 pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes);
37289
37291
37292 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes);
37293 if (resultSL != SL_RESULT_SUCCESS) {
37294 return;
37295 }
37296
37297 pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods;
37298}
37299#endif
37300
37301static ma_result ma_device_uninit__opensl(ma_device* pDevice)
37302{
37303 MA_ASSERT(pDevice != NULL);
37304
37305 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. */
37306 if (g_maOpenSLInitCounter == 0) {
37307 return MA_INVALID_OPERATION;
37308 }
37309
37310 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
37311 if (pDevice->opensl.pAudioRecorderObj) {
37312 MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj);
37313 }
37314
37315 ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks);
37316 }
37317
37318 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
37319 if (pDevice->opensl.pAudioPlayerObj) {
37320 MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj);
37321 }
37322 if (pDevice->opensl.pOutputMixObj) {
37323 MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj);
37324 }
37325
37326 ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks);
37327 }
37328
37329 return MA_SUCCESS;
37330}
37331
37332#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
37333typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM;
37334#else
37335typedef SLDataFormat_PCM ma_SLDataFormat_PCM;
37336#endif
37337
37338static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat)
37339{
37340 /* We need to convert our format/channels/rate so that they aren't set to default. */
37341 if (format == ma_format_unknown) {
37342 format = MA_DEFAULT_FORMAT;
37343 }
37344 if (channels == 0) {
37345 channels = MA_DEFAULT_CHANNELS;
37346 }
37347 if (sampleRate == 0) {
37348 sampleRate = MA_DEFAULT_SAMPLE_RATE;
37349 }
37350
37351#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
37352 if (format == ma_format_f32) {
37353 pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
37354 pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
37355 } else {
37356 pDataFormat->formatType = SL_DATAFORMAT_PCM;
37357 }
37358#else
37359 pDataFormat->formatType = SL_DATAFORMAT_PCM;
37360#endif
37361
37362 pDataFormat->numChannels = channels;
37363 ((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 */
37364 pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8;
37365 pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels);
37366 pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
37367
37368 /*
37369 Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html
37370 - Only mono and stereo is supported.
37371 - Only u8 and s16 formats are supported.
37372 - Maximum sample rate of 48000.
37373 */
37374#ifdef MA_ANDROID
37375 if (pDataFormat->numChannels > 2) {
37376 pDataFormat->numChannels = 2;
37377 }
37378#if __ANDROID_API__ >= 21
37379 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
37380 /* It's floating point. */
37381 MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
37382 if (pDataFormat->bitsPerSample > 32) {
37383 pDataFormat->bitsPerSample = 32;
37384 }
37385 } else {
37386 if (pDataFormat->bitsPerSample > 16) {
37387 pDataFormat->bitsPerSample = 16;
37388 }
37389 }
37390#else
37391 if (pDataFormat->bitsPerSample > 16) {
37392 pDataFormat->bitsPerSample = 16;
37393 }
37394#endif
37395 if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) {
37396 ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48;
37397 }
37398#endif
37399
37400 pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */
37401
37402 return MA_SUCCESS;
37403}
37404
37405static 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)
37406{
37407 ma_bool32 isFloatingPoint = MA_FALSE;
37408#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
37409 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
37410 MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
37411 isFloatingPoint = MA_TRUE;
37412 }
37413#endif
37414 if (isFloatingPoint) {
37415 if (pDataFormat->bitsPerSample == 32) {
37416 *pFormat = ma_format_f32;
37417 }
37418 } else {
37419 if (pDataFormat->bitsPerSample == 8) {
37420 *pFormat = ma_format_u8;
37421 } else if (pDataFormat->bitsPerSample == 16) {
37422 *pFormat = ma_format_s16;
37423 } else if (pDataFormat->bitsPerSample == 24) {
37424 *pFormat = ma_format_s24;
37425 } else if (pDataFormat->bitsPerSample == 32) {
37426 *pFormat = ma_format_s32;
37427 }
37428 }
37429
37430 *pChannels = pDataFormat->numChannels;
37431 *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000;
37432 ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap);
37433
37434 return MA_SUCCESS;
37435}
37436
37437static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
37438{
37439#ifdef MA_ANDROID
37440 SLDataLocator_AndroidSimpleBufferQueue queue;
37441 SLresult resultSL;
37442 size_t bufferSizeInBytes;
37443 SLInterfaceID itfIDs[2];
37444 const SLboolean itfIDsRequired[] = {
37445 SL_BOOLEAN_TRUE, /* SL_IID_ANDROIDSIMPLEBUFFERQUEUE */
37446 SL_BOOLEAN_FALSE /* SL_IID_ANDROIDCONFIGURATION */
37447 };
37448#endif
37449
37450 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. */
37451 if (g_maOpenSLInitCounter == 0) {
37452 return MA_INVALID_OPERATION;
37453 }
37454
37455 if (pConfig->deviceType == ma_device_type_loopback) {
37457 }
37458
37459 /*
37460 For now, only supporting Android implementations of OpenSL|ES since that's the only one I've
37461 been able to test with and I currently depend on Android-specific extensions (simple buffer
37462 queues).
37463 */
37464#ifdef MA_ANDROID
37465 itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
37466 itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION;
37467
37468 /* No exclusive mode with OpenSL|ES. */
37469 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
37470 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
37472 }
37473
37474 /* Now we can start initializing the device properly. */
37475 MA_ASSERT(pDevice != NULL);
37476 MA_ZERO_OBJECT(&pDevice->opensl);
37477
37478 queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
37479
37480 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
37481 ma_SLDataFormat_PCM pcm;
37482 SLDataLocator_IODevice locatorDevice;
37483 SLDataSource source;
37484 SLDataSink sink;
37485 SLAndroidConfigurationItf pRecorderConfig;
37486
37487 ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm);
37488
37489 locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE;
37490 locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT;
37491 locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; /* Must always use the default device with Android. */
37492 locatorDevice.device = NULL;
37493
37494 source.pLocator = &locatorDevice;
37495 source.pFormat = NULL;
37496
37497 queue.numBuffers = pDescriptorCapture->periodCount;
37498
37499 sink.pLocator = &queue;
37500 sink.pFormat = (SLDataFormat_PCM*)&pcm;
37501
37502 resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
37503 if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) {
37504 /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
37505 pcm.formatType = SL_DATAFORMAT_PCM;
37506 pcm.numChannels = 1;
37507 ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */
37508 pcm.bitsPerSample = 16;
37509 pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
37510 pcm.channelMask = 0;
37511 resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
37512 }
37513
37514 if (resultSL != SL_RESULT_SUCCESS) {
37515 ma_device_uninit__opensl(pDevice);
37516 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder.");
37517 return ma_result_from_OpenSL(resultSL);
37518 }
37519
37520
37521 /* Set the recording preset before realizing the player. */
37523 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig);
37524 if (resultSL == SL_RESULT_SUCCESS) {
37525 SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset);
37526 resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32));
37527 if (resultSL != SL_RESULT_SUCCESS) {
37528 /* Failed to set the configuration. Just keep going. */
37529 }
37530 }
37531 }
37532
37533 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE);
37534 if (resultSL != SL_RESULT_SUCCESS) {
37535 ma_device_uninit__opensl(pDevice);
37536 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.");
37537 return ma_result_from_OpenSL(resultSL);
37538 }
37539
37540 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder);
37541 if (resultSL != SL_RESULT_SUCCESS) {
37542 ma_device_uninit__opensl(pDevice);
37543 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.");
37544 return ma_result_from_OpenSL(resultSL);
37545 }
37546
37547 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture);
37548 if (resultSL != SL_RESULT_SUCCESS) {
37549 ma_device_uninit__opensl(pDevice);
37550 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.");
37551 return ma_result_from_OpenSL(resultSL);
37552 }
37553
37554 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice);
37555 if (resultSL != SL_RESULT_SUCCESS) {
37556 ma_device_uninit__opensl(pDevice);
37557 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.");
37558 return ma_result_from_OpenSL(resultSL);
37559 }
37560
37561 /* The internal format is determined by the "pcm" object. */
37562 ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap));
37563
37564 /* Buffer. */
37565 pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
37566 pDevice->opensl.currentBufferIndexCapture = 0;
37567
37568 bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount;
37569 pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);
37570 if (pDevice->opensl.pBufferCapture == NULL) {
37571 ma_device_uninit__opensl(pDevice);
37572 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.");
37573 return MA_OUT_OF_MEMORY;
37574 }
37575 MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes);
37576 }
37577
37578 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
37579 ma_SLDataFormat_PCM pcm;
37580 SLDataSource source;
37581 SLDataLocator_OutputMix outmixLocator;
37582 SLDataSink sink;
37583 SLAndroidConfigurationItf pPlayerConfig;
37584
37585 ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm);
37586
37587 resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL);
37588 if (resultSL != SL_RESULT_SUCCESS) {
37589 ma_device_uninit__opensl(pDevice);
37590 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix.");
37591 return ma_result_from_OpenSL(resultSL);
37592 }
37593
37594 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE);
37595 if (resultSL != SL_RESULT_SUCCESS) {
37596 ma_device_uninit__opensl(pDevice);
37597 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.");
37598 return ma_result_from_OpenSL(resultSL);
37599 }
37600
37601 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix);
37602 if (resultSL != SL_RESULT_SUCCESS) {
37603 ma_device_uninit__opensl(pDevice);
37604 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.");
37605 return ma_result_from_OpenSL(resultSL);
37606 }
37607
37608 /* Set the output device. */
37609 if (pDescriptorPlayback->pDeviceID != NULL) {
37610 SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl;
37611 MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL);
37612 }
37613
37614 queue.numBuffers = pDescriptorPlayback->periodCount;
37615
37616 source.pLocator = &queue;
37617 source.pFormat = (SLDataFormat_PCM*)&pcm;
37618
37619 outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
37620 outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj;
37621
37622 sink.pLocator = &outmixLocator;
37623 sink.pFormat = NULL;
37624
37625 resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
37626 if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) {
37627 /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
37628 pcm.formatType = SL_DATAFORMAT_PCM;
37629 pcm.numChannels = 2;
37630 ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;
37631 pcm.bitsPerSample = 16;
37632 pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
37633 pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
37634 resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
37635 }
37636
37637 if (resultSL != SL_RESULT_SUCCESS) {
37638 ma_device_uninit__opensl(pDevice);
37639 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player.");
37640 return ma_result_from_OpenSL(resultSL);
37641 }
37642
37643
37644 /* Set the stream type before realizing the player. */
37646 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig);
37647 if (resultSL == SL_RESULT_SUCCESS) {
37648 SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType);
37649 resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
37650 if (resultSL != SL_RESULT_SUCCESS) {
37651 /* Failed to set the configuration. Just keep going. */
37652 }
37653 }
37654 }
37655
37656 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE);
37657 if (resultSL != SL_RESULT_SUCCESS) {
37658 ma_device_uninit__opensl(pDevice);
37659 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.");
37660 return ma_result_from_OpenSL(resultSL);
37661 }
37662
37663 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer);
37664 if (resultSL != SL_RESULT_SUCCESS) {
37665 ma_device_uninit__opensl(pDevice);
37666 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.");
37667 return ma_result_from_OpenSL(resultSL);
37668 }
37669
37670 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback);
37671 if (resultSL != SL_RESULT_SUCCESS) {
37672 ma_device_uninit__opensl(pDevice);
37673 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.");
37674 return ma_result_from_OpenSL(resultSL);
37675 }
37676
37677 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice);
37678 if (resultSL != SL_RESULT_SUCCESS) {
37679 ma_device_uninit__opensl(pDevice);
37680 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.");
37681 return ma_result_from_OpenSL(resultSL);
37682 }
37683
37684 /* The internal format is determined by the "pcm" object. */
37685 ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap));
37686
37687 /* Buffer. */
37688 pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
37689 pDevice->opensl.currentBufferIndexPlayback = 0;
37690
37691 bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount;
37692 pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);
37693 if (pDevice->opensl.pBufferPlayback == NULL) {
37694 ma_device_uninit__opensl(pDevice);
37695 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.");
37696 return MA_OUT_OF_MEMORY;
37697 }
37698 MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes);
37699 }
37700
37701 return MA_SUCCESS;
37702#else
37703 return MA_NO_BACKEND; /* Non-Android implementations are not supported. */
37704#endif
37705}
37706
37707static ma_result ma_device_start__opensl(ma_device* pDevice)
37708{
37709 SLresult resultSL;
37710 size_t periodSizeInBytes;
37711 ma_uint32 iPeriod;
37712
37713 MA_ASSERT(pDevice != NULL);
37714
37715 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. */
37716 if (g_maOpenSLInitCounter == 0) {
37717 return MA_INVALID_OPERATION;
37718 }
37719
37720 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
37721 resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING);
37722 if (resultSL != SL_RESULT_SUCCESS) {
37723 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device.");
37724 return ma_result_from_OpenSL(resultSL);
37725 }
37726
37727 periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
37728 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
37729 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes);
37730 if (resultSL != SL_RESULT_SUCCESS) {
37731 MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
37732 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device.");
37733 return ma_result_from_OpenSL(resultSL);
37734 }
37735 }
37736 }
37737
37738 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
37739 resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING);
37740 if (resultSL != SL_RESULT_SUCCESS) {
37741 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device.");
37742 return ma_result_from_OpenSL(resultSL);
37743 }
37744
37745 /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueu silent buffers. */
37746 if (pDevice->type == ma_device_type_duplex) {
37747 MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
37748 } else {
37749 ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback);
37750 }
37751
37752 periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
37753 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
37754 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes);
37755 if (resultSL != SL_RESULT_SUCCESS) {
37756 MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
37757 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device.");
37758 return ma_result_from_OpenSL(resultSL);
37759 }
37760 }
37761 }
37762
37763 return MA_SUCCESS;
37764}
37765
37766static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType)
37767{
37768 SLAndroidSimpleBufferQueueItf pBufferQueue;
37769
37770 MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback);
37771
37772 if (pDevice->type == ma_device_type_capture) {
37773 pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture;
37774 pDevice->opensl.isDrainingCapture = MA_TRUE;
37775 } else {
37776 pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback;
37777 pDevice->opensl.isDrainingPlayback = MA_TRUE;
37778 }
37779
37780 for (;;) {
37781 SLAndroidSimpleBufferQueueState state;
37782
37783 MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state);
37784 if (state.count == 0) {
37785 break;
37786 }
37787
37788 ma_sleep(10);
37789 }
37790
37791 if (pDevice->type == ma_device_type_capture) {
37792 pDevice->opensl.isDrainingCapture = MA_FALSE;
37793 } else {
37794 pDevice->opensl.isDrainingPlayback = MA_FALSE;
37795 }
37796
37797 return MA_SUCCESS;
37798}
37799
37800static ma_result ma_device_stop__opensl(ma_device* pDevice)
37801{
37802 SLresult resultSL;
37803
37804 MA_ASSERT(pDevice != NULL);
37805
37806 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. */
37807 if (g_maOpenSLInitCounter == 0) {
37808 return MA_INVALID_OPERATION;
37809 }
37810
37811 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
37812 ma_device_drain__opensl(pDevice, ma_device_type_capture);
37813
37814 resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
37815 if (resultSL != SL_RESULT_SUCCESS) {
37816 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device.");
37817 return ma_result_from_OpenSL(resultSL);
37818 }
37819
37820 MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture);
37821 }
37822
37823 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
37824 ma_device_drain__opensl(pDevice, ma_device_type_playback);
37825
37826 resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
37827 if (resultSL != SL_RESULT_SUCCESS) {
37828 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device.");
37829 return ma_result_from_OpenSL(resultSL);
37830 }
37831
37832 MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback);
37833 }
37834
37835 /* 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. */
37836 ma_device__on_notification_stopped(pDevice);
37837
37838 return MA_SUCCESS;
37839}
37840
37841
37842static ma_result ma_context_uninit__opensl(ma_context* pContext)
37843{
37844 MA_ASSERT(pContext != NULL);
37845 MA_ASSERT(pContext->backend == ma_backend_opensl);
37846 (void)pContext;
37847
37848 /* Uninit global data. */
37849 ma_spinlock_lock(&g_maOpenSLSpinlock);
37850 {
37851 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(). */
37852
37853 g_maOpenSLInitCounter -= 1;
37854 if (g_maOpenSLInitCounter == 0) {
37855 (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
37856 }
37857 }
37858 ma_spinlock_unlock(&g_maOpenSLSpinlock);
37859
37860 return MA_SUCCESS;
37861}
37862
37863static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle)
37864{
37865 /* 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. */
37866 ma_handle* p = (ma_handle*)ma_dlsym(pContext, pContext->opensl.libOpenSLES, pName);
37867 if (p == NULL) {
37868 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName);
37869 return MA_NO_BACKEND;
37870 }
37871
37872 *pHandle = *p;
37873 return MA_SUCCESS;
37874}
37875
37876static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext)
37877{
37878 g_maOpenSLInitCounter += 1;
37879 if (g_maOpenSLInitCounter == 1) {
37880 SLresult resultSL;
37881
37882 resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL);
37883 if (resultSL != SL_RESULT_SUCCESS) {
37884 g_maOpenSLInitCounter -= 1;
37885 return ma_result_from_OpenSL(resultSL);
37886 }
37887
37888 (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE);
37889
37890 resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL);
37891 if (resultSL != SL_RESULT_SUCCESS) {
37892 (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
37893 g_maOpenSLInitCounter -= 1;
37894 return ma_result_from_OpenSL(resultSL);
37895 }
37896 }
37897
37898 return MA_SUCCESS;
37899}
37900
37901static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
37902{
37903 ma_result result;
37904
37905#if !defined(MA_NO_RUNTIME_LINKING)
37906 size_t i;
37907 const char* libOpenSLESNames[] = {
37908 "libOpenSLES.so"
37909 };
37910#endif
37911
37912 MA_ASSERT(pContext != NULL);
37913
37914 (void)pConfig;
37915
37916#if !defined(MA_NO_RUNTIME_LINKING)
37917 /*
37918 Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One
37919 report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime
37920 and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any
37921 references to the symbols and will hopefully skip the checks.
37922 */
37923 for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) {
37924 pContext->opensl.libOpenSLES = ma_dlopen(pContext, libOpenSLESNames[i]);
37925 if (pContext->opensl.libOpenSLES != NULL) {
37926 break;
37927 }
37928 }
37929
37930 if (pContext->opensl.libOpenSLES == NULL) {
37931 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so");
37932 return MA_NO_BACKEND;
37933 }
37934
37935 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE);
37936 if (result != MA_SUCCESS) {
37937 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
37938 return result;
37939 }
37940
37941 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES);
37942 if (result != MA_SUCCESS) {
37943 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
37944 return result;
37945 }
37946
37947 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE);
37948 if (result != MA_SUCCESS) {
37949 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
37950 return result;
37951 }
37952
37953 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD);
37954 if (result != MA_SUCCESS) {
37955 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
37956 return result;
37957 }
37958
37959 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY);
37960 if (result != MA_SUCCESS) {
37961 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
37962 return result;
37963 }
37964
37965 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX);
37966 if (result != MA_SUCCESS) {
37967 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
37968 return result;
37969 }
37970
37971 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION);
37972 if (result != MA_SUCCESS) {
37973 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
37974 return result;
37975 }
37976
37977 pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(pContext, pContext->opensl.libOpenSLES, "slCreateEngine");
37978 if (pContext->opensl.slCreateEngine == NULL) {
37979 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
37980 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine.");
37981 return MA_NO_BACKEND;
37982 }
37983#else
37984 pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE;
37985 pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES;
37986 pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
37987 pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD;
37988 pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY;
37989 pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX;
37990 pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION;
37991 pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine;
37992#endif
37993
37994
37995 /* Initialize global data first if applicable. */
37996 ma_spinlock_lock(&g_maOpenSLSpinlock);
37997 {
37998 result = ma_context_init_engine_nolock__opensl(pContext);
37999 }
38000 ma_spinlock_unlock(&g_maOpenSLSpinlock);
38001
38002 if (result != MA_SUCCESS) {
38003 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
38004 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine.");
38005 return result;
38006 }
38007
38008 pCallbacks->onContextInit = ma_context_init__opensl;
38009 pCallbacks->onContextUninit = ma_context_uninit__opensl;
38010 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl;
38011 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl;
38012 pCallbacks->onDeviceInit = ma_device_init__opensl;
38013 pCallbacks->onDeviceUninit = ma_device_uninit__opensl;
38014 pCallbacks->onDeviceStart = ma_device_start__opensl;
38015 pCallbacks->onDeviceStop = ma_device_stop__opensl;
38016 pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */
38017 pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */
38018 pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */
38019
38020 return MA_SUCCESS;
38021}
38022#endif /* OpenSL|ES */
38023
38024
38025
38030#ifdef MA_HAS_WEBAUDIO
38031#include <emscripten/emscripten.h>
38032
38033static ma_bool32 ma_is_capture_supported__webaudio()
38034{
38035 return EM_ASM_INT({
38036 return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined);
38037 }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */
38038}
38039
38040#ifdef __cplusplus
38041extern "C" {
38042#endif
38043void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
38044{
38045 ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount);
38046}
38047
38048void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
38049{
38050 ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount);
38051}
38052#ifdef __cplusplus
38053}
38054#endif
38055
38056static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
38057{
38058 ma_bool32 cbResult = MA_TRUE;
38059
38060 MA_ASSERT(pContext != NULL);
38061 MA_ASSERT(callback != NULL);
38062
38063 /* Only supporting default devices for now. */
38064
38065 /* Playback. */
38066 if (cbResult) {
38067 ma_device_info deviceInfo;
38068 MA_ZERO_OBJECT(&deviceInfo);
38069 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
38070 deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */
38071 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
38072 }
38073
38074 /* Capture. */
38075 if (cbResult) {
38076 if (ma_is_capture_supported__webaudio()) {
38077 ma_device_info deviceInfo;
38078 MA_ZERO_OBJECT(&deviceInfo);
38079 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
38080 deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */
38081 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
38082 }
38083 }
38084
38085 return MA_SUCCESS;
38086}
38087
38088static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
38089{
38090 MA_ASSERT(pContext != NULL);
38091
38092 if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
38093 return MA_NO_DEVICE;
38094 }
38095
38096 MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio));
38097
38098 /* Only supporting default devices for now. */
38099 (void)pDeviceID;
38100 if (deviceType == ma_device_type_playback) {
38101 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
38102 } else {
38103 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
38104 }
38105
38106 /* Only supporting default devices. */
38107 pDeviceInfo->isDefault = MA_TRUE;
38108
38109 /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */
38110 pDeviceInfo->nativeDataFormats[0].flags = 0;
38111 pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown;
38112 pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channels are supported. */
38113 pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({
38114 try {
38115 var temp = new (window.AudioContext || window.webkitAudioContext)();
38116 var sampleRate = temp.sampleRate;
38117 temp.close();
38118 return sampleRate;
38119 } catch(e) {
38120 return 0;
38121 }
38122 }, 0); /* Must pass in a dummy argument for C99 compatibility. */
38123
38124 if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) {
38125 return MA_NO_DEVICE;
38126 }
38127
38128 pDeviceInfo->nativeDataFormatCount = 1;
38129
38130 return MA_SUCCESS;
38131}
38132
38133
38134static void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_type deviceType, int deviceIndex)
38135{
38136 MA_ASSERT(pDevice != NULL);
38137
38138 EM_ASM({
38139 var device = miniaudio.get_device_by_index($0);
38140
38141 /* Make sure all nodes are disconnected and marked for collection. */
38142 if (device.scriptNode !== undefined) {
38143 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... */
38144 device.scriptNode.disconnect();
38145 device.scriptNode = undefined;
38146 }
38147 if (device.streamNode !== undefined) {
38148 device.streamNode.disconnect();
38149 device.streamNode = undefined;
38150 }
38151
38152 /*
38153 Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want
38154 to clear the callback before closing.
38155 */
38156 device.webaudio.close();
38157 device.webaudio = undefined;
38158
38159 /* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */
38160 if (device.intermediaryBuffer !== undefined) {
38161 Module._free(device.intermediaryBuffer);
38162 device.intermediaryBuffer = undefined;
38163 device.intermediaryBufferView = undefined;
38164 device.intermediaryBufferSizeInBytes = undefined;
38165 }
38166
38167 /* Make sure the device is untracked so the slot can be reused later. */
38168 miniaudio.untrack_device_by_index($0);
38169 }, deviceIndex, deviceType);
38170}
38171
38172static ma_result ma_device_uninit__webaudio(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 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
38178 }
38179
38180 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
38181 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback);
38182 }
38183
38184 return MA_SUCCESS;
38185}
38186
38187static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
38188{
38189 /*
38190 There have been reports of the default buffer size being too small on some browsers. There have been reports of the default buffer
38191 size being too small on some browsers. If we're using default buffer size, we'll make sure the period size is a big biffer than our
38192 standard defaults.
38193 */
38194 ma_uint32 periodSizeInFrames;
38195
38196 if (pDescriptor->periodSizeInFrames == 0) {
38197 if (pDescriptor->periodSizeInMilliseconds == 0) {
38198 if (performanceProfile == ma_performance_profile_low_latency) {
38199 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */
38200 } else {
38201 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate);
38202 }
38203 } else {
38204 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
38205 }
38206 } else {
38207 periodSizeInFrames = pDescriptor->periodSizeInFrames;
38208 }
38209
38210 /* The size of the buffer must be a power of 2 and between 256 and 16384. */
38211 if (periodSizeInFrames < 256) {
38212 periodSizeInFrames = 256;
38213 } else if (periodSizeInFrames > 16384) {
38214 periodSizeInFrames = 16384;
38215 } else {
38216 periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames);
38217 }
38218
38219 return periodSizeInFrames;
38220}
38221
38222static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
38223{
38224 int deviceIndex;
38225 ma_uint32 channels;
38226 ma_uint32 sampleRate;
38227 ma_uint32 periodSizeInFrames;
38228
38229 MA_ASSERT(pDevice != NULL);
38230 MA_ASSERT(pConfig != NULL);
38231 MA_ASSERT(deviceType != ma_device_type_duplex);
38232
38233 if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
38234 return MA_NO_DEVICE;
38235 }
38236
38237 /* We're going to calculate some stuff in C just to simplify the JS code. */
38238 channels = (pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS;
38239 sampleRate = (pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE;
38240 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptor, sampleRate, pConfig->performanceProfile);
38241
38242 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "periodSizeInFrames = %d\n", (int)periodSizeInFrames);
38243
38244 /* We create the device on the JavaScript side and reference it using an index. We use this to make it possible to reference the device between JavaScript and C. */
38245 deviceIndex = EM_ASM_INT({
38246 var channels = $0;
38247 var sampleRate = $1;
38248 var bufferSize = $2; /* In PCM frames. */
38249 var isCapture = $3;
38250 var pDevice = $4;
38251
38252 if (typeof(window.miniaudio) === 'undefined') {
38253 return -1; /* Context not initialized. */
38254 }
38255
38256 var device = {};
38257
38258 /* The AudioContext must be created in a suspended state. */
38259 device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate});
38260 device.webaudio.suspend();
38261 device.state = 1; /* ma_device_state_stopped */
38262
38263 /*
38264 We need an intermediary buffer which we use for JavaScript and C interop. This buffer stores interleaved f32 PCM data. Because it's passed between
38265 JavaScript and C it needs to be allocated and freed using Module._malloc() and Module._free().
38266 */
38267 device.intermediaryBufferSizeInBytes = channels * bufferSize * 4;
38268 device.intermediaryBuffer = Module._malloc(device.intermediaryBufferSizeInBytes);
38269 device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
38270
38271 /*
38272 Both playback and capture devices use a ScriptProcessorNode for performing per-sample operations.
38273
38274 ScriptProcessorNode is actually deprecated so this is likely to be temporary. The way this works for playback is very simple. You just set a callback
38275 that's periodically fired, just like a normal audio callback function. But apparently this design is "flawed" and is now deprecated in favour of
38276 something called AudioWorklets which _forces_ you to load a _separate_ .js file at run time... nice... Hopefully ScriptProcessorNode will continue to
38277 work for years to come, but this may need to change to use AudioSourceBufferNode instead, which I think is what Emscripten uses for it's built-in SDL
38278 implementation. I'll be avoiding that insane AudioWorklet API like the plague...
38279
38280 For capture it is a bit unintuitive. We use the ScriptProccessorNode _only_ to get the raw PCM data. It is connected to an AudioContext just like the
38281 playback case, however we just output silence to the AudioContext instead of passing any real data. It would make more sense to me to use the
38282 MediaRecorder API, but unfortunately you need to specify a MIME time (Opus, Vorbis, etc.) for the binary blob that's returned to the client, but I've
38283 been unable to figure out how to get this as raw PCM. The closest I can think is to use the MIME type for WAV files and just parse it, but I don't know
38284 how well this would work. Although ScriptProccessorNode is deprecated, in practice it seems to have pretty good browser support so I'm leaving it like
38285 this for now. If anyone knows how I could get raw PCM data using the MediaRecorder API please let me know!
38286 */
38287 device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, (isCapture) ? channels : 0, (isCapture) ? 0 : channels);
38288
38289 if (isCapture) {
38290 device.scriptNode.onaudioprocess = function(e) {
38291 if (device.intermediaryBuffer === undefined) {
38292 return; /* This means the device has been uninitialized. */
38293 }
38294
38295 if (device.intermediaryBufferView.length == 0) {
38296 /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */
38297 device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
38298 }
38299
38300 /* Make sure silence it output to the AudioContext destination. Not doing this will cause sound to come out of the speakers! */
38301 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
38302 e.outputBuffer.getChannelData(iChannel).fill(0.0);
38303 }
38304
38305 /* There are some situations where we may want to send silence to the client. */
38306 var sendSilence = false;
38307 if (device.streamNode === undefined) {
38308 sendSilence = true;
38309 }
38310
38311 /* Sanity check. This will never happen, right? */
38312 if (e.inputBuffer.numberOfChannels != channels) {
38313 console.log("Capture: Channel count mismatch. " + e.inputBufer.numberOfChannels + " != " + channels + ". Sending silence.");
38314 sendSilence = true;
38315 }
38316
38317 /* This looped design guards against the situation where e.inputBuffer is a different size to the original buffer size. Should never happen in practice. */
38318 var totalFramesProcessed = 0;
38319 while (totalFramesProcessed < e.inputBuffer.length) {
38320 var framesRemaining = e.inputBuffer.length - totalFramesProcessed;
38321 var framesToProcess = framesRemaining;
38322 if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
38323 framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
38324 }
38325
38326 /* We need to do the reverse of the playback case. We need to interleave the input data and copy it into the intermediary buffer. Then we send it to the client. */
38327 if (sendSilence) {
38328 device.intermediaryBufferView.fill(0.0);
38329 } else {
38330 for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
38331 for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) {
38332 device.intermediaryBufferView[iFrame*channels + iChannel] = e.inputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame];
38333 }
38334 }
38335 }
38336
38337 /* Send data to the client from our intermediary buffer. */
38338 ccall("ma_device_process_pcm_frames_capture__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]);
38339
38340 totalFramesProcessed += framesToProcess;
38341 }
38342 };
38343
38344 navigator.mediaDevices.getUserMedia({audio:true, video:false})
38345 .then(function(stream) {
38346 device.streamNode = device.webaudio.createMediaStreamSource(stream);
38347 device.streamNode.connect(device.scriptNode);
38348 device.scriptNode.connect(device.webaudio.destination);
38349 })
38350 .catch(function(error) {
38351 /* I think this should output silence... */
38352 device.scriptNode.connect(device.webaudio.destination);
38353 });
38354 } else {
38355 device.scriptNode.onaudioprocess = function(e) {
38356 if (device.intermediaryBuffer === undefined) {
38357 return; /* This means the device has been uninitialized. */
38358 }
38359
38360 if(device.intermediaryBufferView.length == 0) {
38361 /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */
38362 device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
38363 }
38364
38365 var outputSilence = false;
38366
38367 /* Sanity check. This will never happen, right? */
38368 if (e.outputBuffer.numberOfChannels != channels) {
38369 console.log("Playback: Channel count mismatch. " + e.outputBufer.numberOfChannels + " != " + channels + ". Outputting silence.");
38370 outputSilence = true;
38371 return;
38372 }
38373
38374 /* This looped design guards against the situation where e.outputBuffer is a different size to the original buffer size. Should never happen in practice. */
38375 var totalFramesProcessed = 0;
38376 while (totalFramesProcessed < e.outputBuffer.length) {
38377 var framesRemaining = e.outputBuffer.length - totalFramesProcessed;
38378 var framesToProcess = framesRemaining;
38379 if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
38380 framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
38381 }
38382
38383 /* Read data from the client into our intermediary buffer. */
38384 ccall("ma_device_process_pcm_frames_playback__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]);
38385
38386 /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */
38387 if (outputSilence) {
38388 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
38389 e.outputBuffer.getChannelData(iChannel).fill(0.0);
38390 }
38391 } else {
38392 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
38393 var outputBuffer = e.outputBuffer.getChannelData(iChannel);
38394 var intermediaryBuffer = device.intermediaryBufferView;
38395 for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
38396 outputBuffer[totalFramesProcessed + iFrame] = intermediaryBuffer[iFrame*channels + iChannel];
38397 }
38398 }
38399 }
38400
38401 totalFramesProcessed += framesToProcess;
38402 }
38403 };
38404
38405 device.scriptNode.connect(device.webaudio.destination);
38406 }
38407
38408 return miniaudio.track_device(device);
38409 }, channels, sampleRate, periodSizeInFrames, deviceType == ma_device_type_capture, pDevice);
38410
38411 if (deviceIndex < 0) {
38413 }
38414
38415 if (deviceType == ma_device_type_capture) {
38416 pDevice->webaudio.indexCapture = deviceIndex;
38417 } else {
38418 pDevice->webaudio.indexPlayback = deviceIndex;
38419 }
38420
38421 pDescriptor->format = ma_format_f32;
38422 pDescriptor->channels = channels;
38423 ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels);
38424 pDescriptor->sampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
38425 pDescriptor->periodSizeInFrames = periodSizeInFrames;
38426 pDescriptor->periodCount = 1;
38427
38428 return MA_SUCCESS;
38429}
38430
38431static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
38432{
38433 ma_result result;
38434
38435 if (pConfig->deviceType == ma_device_type_loopback) {
38437 }
38438
38439 /* No exclusive mode with Web Audio. */
38440 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
38441 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
38443 }
38444
38445 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
38446 result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
38447 if (result != MA_SUCCESS) {
38448 return result;
38449 }
38450 }
38451
38452 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
38453 result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
38454 if (result != MA_SUCCESS) {
38455 if (pConfig->deviceType == ma_device_type_duplex) {
38456 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
38457 }
38458 return result;
38459 }
38460 }
38461
38462 return MA_SUCCESS;
38463}
38464
38465static ma_result ma_device_start__webaudio(ma_device* pDevice)
38466{
38467 MA_ASSERT(pDevice != NULL);
38468
38469 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
38470 EM_ASM({
38471 var device = miniaudio.get_device_by_index($0);
38472 device.webaudio.resume();
38473 device.state = 2; /* ma_device_state_started */
38474 }, pDevice->webaudio.indexCapture);
38475 }
38476
38477 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
38478 EM_ASM({
38479 var device = miniaudio.get_device_by_index($0);
38480 device.webaudio.resume();
38481 device.state = 2; /* ma_device_state_started */
38482 }, pDevice->webaudio.indexPlayback);
38483 }
38484
38485 return MA_SUCCESS;
38486}
38487
38488static ma_result ma_device_stop__webaudio(ma_device* pDevice)
38489{
38490 MA_ASSERT(pDevice != NULL);
38491
38492 /*
38493 From the WebAudio API documentation for AudioContext.suspend():
38494
38495 Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the
38496 destination, and then allows the system to release its claim on audio hardware.
38497
38498 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
38499 do any kind of explicit draining.
38500 */
38501
38502 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
38503 EM_ASM({
38504 var device = miniaudio.get_device_by_index($0);
38505 device.webaudio.suspend();
38506 device.state = 1; /* ma_device_state_stopped */
38507 }, pDevice->webaudio.indexCapture);
38508 }
38509
38510 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
38511 EM_ASM({
38512 var device = miniaudio.get_device_by_index($0);
38513 device.webaudio.suspend();
38514 device.state = 1; /* ma_device_state_stopped */
38515 }, pDevice->webaudio.indexPlayback);
38516 }
38517
38518 ma_device__on_notification_stopped(pDevice);
38519
38520 return MA_SUCCESS;
38521}
38522
38523static ma_result ma_context_uninit__webaudio(ma_context* pContext)
38524{
38525 MA_ASSERT(pContext != NULL);
38526 MA_ASSERT(pContext->backend == ma_backend_webaudio);
38527
38528 /* Nothing needs to be done here. */
38529 (void)pContext;
38530
38531 return MA_SUCCESS;
38532}
38533
38534static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
38535{
38536 int resultFromJS;
38537
38538 MA_ASSERT(pContext != NULL);
38539
38540 (void)pConfig; /* Unused. */
38541
38542 /* Here is where our global JavaScript object is initialized. */
38543 resultFromJS = EM_ASM_INT({
38544 if ((window.AudioContext || window.webkitAudioContext) === undefined) {
38545 return 0; /* Web Audio not supported. */
38546 }
38547
38548 if (typeof(window.miniaudio) === 'undefined') {
38549 window.miniaudio = {};
38550 miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */
38551
38552 miniaudio.track_device = function(device) {
38553 /* Try inserting into a free slot first. */
38554 for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
38555 if (miniaudio.devices[iDevice] == null) {
38556 miniaudio.devices[iDevice] = device;
38557 return iDevice;
38558 }
38559 }
38560
38561 /* Getting here means there is no empty slots in the array so we just push to the end. */
38562 miniaudio.devices.push(device);
38563 return miniaudio.devices.length - 1;
38564 };
38565
38566 miniaudio.untrack_device_by_index = function(deviceIndex) {
38567 /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */
38568 miniaudio.devices[deviceIndex] = null;
38569
38570 /* Trim the array if possible. */
38571 while (miniaudio.devices.length > 0) {
38572 if (miniaudio.devices[miniaudio.devices.length-1] == null) {
38573 miniaudio.devices.pop();
38574 } else {
38575 break;
38576 }
38577 }
38578 };
38579
38580 miniaudio.untrack_device = function(device) {
38581 for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
38582 if (miniaudio.devices[iDevice] == device) {
38583 return miniaudio.untrack_device_by_index(iDevice);
38584 }
38585 }
38586 };
38587
38588 miniaudio.get_device_by_index = function(deviceIndex) {
38589 return miniaudio.devices[deviceIndex];
38590 };
38591
38592 miniaudio.unlock_event_types = (function(){
38593 return ['touchstart', 'touchend', 'click'];
38594 })();
38595
38596 miniaudio.unlock = function() {
38597 for(var i = 0; i < miniaudio.devices.length; ++i) {
38598 var device = miniaudio.devices[i];
38599 if (device != null && device.webaudio != null && device.state === 2 /* ma_device_state_started */) {
38600 device.webaudio.resume();
38601 }
38602 }
38603 miniaudio.unlock_event_types.map(function(event_type) {
38604 document.removeEventListener(event_type, miniaudio.unlock, true);
38605 });
38606 };
38607
38608 miniaudio.unlock_event_types.map(function(event_type) {
38609 document.addEventListener(event_type, miniaudio.unlock, true);
38610 });
38611 }
38612
38613 return 1;
38614 }, 0); /* Must pass in a dummy argument for C99 compatibility. */
38615
38616 if (resultFromJS != 1) {
38618 }
38619
38620 pCallbacks->onContextInit = ma_context_init__webaudio;
38621 pCallbacks->onContextUninit = ma_context_uninit__webaudio;
38622 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio;
38623 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio;
38624 pCallbacks->onDeviceInit = ma_device_init__webaudio;
38625 pCallbacks->onDeviceUninit = ma_device_uninit__webaudio;
38626 pCallbacks->onDeviceStart = ma_device_start__webaudio;
38627 pCallbacks->onDeviceStop = ma_device_stop__webaudio;
38628 pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */
38629 pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */
38630 pCallbacks->onDeviceDataLoop = NULL; /* Not needed because WebAudio is asynchronous. */
38631
38632 return MA_SUCCESS;
38633}
38634#endif /* Web Audio */
38635
38636
38637
38638static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint32 channels)
38639{
38640 /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */
38641 if (pChannelMap != NULL && pChannelMap[0] != MA_CHANNEL_NONE) {
38642 ma_uint32 iChannel;
38643
38644 if (channels == 0 || channels > MA_MAX_CHANNELS) {
38645 return MA_FALSE; /* Channel count out of range. */
38646 }
38647
38648 /* A channel cannot be present in the channel map more than once. */
38649 for (iChannel = 0; iChannel < channels; ++iChannel) {
38650 ma_uint32 jChannel;
38651 for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) {
38652 if (pChannelMap[iChannel] == pChannelMap[jChannel]) {
38653 return MA_FALSE;
38654 }
38655 }
38656 }
38657 }
38658
38659 return MA_TRUE;
38660}
38661
38662
38663static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType)
38664{
38665 ma_result result;
38666
38667 MA_ASSERT(pDevice != NULL);
38668
38669 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
38670 if (pDevice->capture.format == ma_format_unknown) {
38671 pDevice->capture.format = pDevice->capture.internalFormat;
38672 }
38673 if (pDevice->capture.channels == 0) {
38674 pDevice->capture.channels = pDevice->capture.internalChannels;
38675 }
38676 if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) {
38677 MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS);
38678 if (pDevice->capture.internalChannels == pDevice->capture.channels) {
38680 } else {
38683 } else {
38685 }
38686 }
38687 }
38688 }
38689
38690 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
38691 if (pDevice->playback.format == ma_format_unknown) {
38692 pDevice->playback.format = pDevice->playback.internalFormat;
38693 }
38694 if (pDevice->playback.channels == 0) {
38695 pDevice->playback.channels = pDevice->playback.internalChannels;
38696 }
38697 if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) {
38698 MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS);
38699 if (pDevice->playback.internalChannels == pDevice->playback.channels) {
38701 } else {
38704 } else {
38706 }
38707 }
38708 }
38709 }
38710
38711 if (pDevice->sampleRate == 0) {
38712 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
38713 pDevice->sampleRate = pDevice->capture.internalSampleRate;
38714 } else {
38715 pDevice->sampleRate = pDevice->playback.internalSampleRate;
38716 }
38717 }
38718
38719 /* Data converters. */
38720 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
38721 /* Converting from internal device format to client format. */
38723 converterConfig.formatIn = pDevice->capture.internalFormat;
38724 converterConfig.channelsIn = pDevice->capture.internalChannels;
38725 converterConfig.sampleRateIn = pDevice->capture.internalSampleRate;
38726 converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap;
38727 converterConfig.formatOut = pDevice->capture.format;
38728 converterConfig.channelsOut = pDevice->capture.channels;
38729 converterConfig.sampleRateOut = pDevice->sampleRate;
38730 converterConfig.pChannelMapOut = pDevice->capture.channelMap;
38731 converterConfig.channelMixMode = pDevice->capture.channelMixMode;
38732 converterConfig.allowDynamicSampleRate = MA_FALSE;
38733 converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
38734 converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
38735 converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable;
38736 converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData;
38737
38738 /* Make sure the old converter is uninitialized first. */
38741 }
38742
38743 result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter);
38744 if (result != MA_SUCCESS) {
38745 return result;
38746 }
38747 }
38748
38749 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
38750 /* Converting from client format to device format. */
38752 converterConfig.formatIn = pDevice->playback.format;
38753 converterConfig.channelsIn = pDevice->playback.channels;
38754 converterConfig.sampleRateIn = pDevice->sampleRate;
38755 converterConfig.pChannelMapIn = pDevice->playback.channelMap;
38756 converterConfig.formatOut = pDevice->playback.internalFormat;
38757 converterConfig.channelsOut = pDevice->playback.internalChannels;
38758 converterConfig.sampleRateOut = pDevice->playback.internalSampleRate;
38759 converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap;
38760 converterConfig.channelMixMode = pDevice->playback.channelMixMode;
38761 converterConfig.allowDynamicSampleRate = MA_FALSE;
38762 converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
38763 converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
38764 converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable;
38765 converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData;
38766
38767 /* Make sure the old converter is uninitialized first. */
38770 }
38771
38772 result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter);
38773 if (result != MA_SUCCESS) {
38774 return result;
38775 }
38776 }
38777
38778
38779 /*
38780 In playback mode, if the data converter does not support retrieval of the required number of
38781 input frames given a number of output frames, we need to fall back to a heap-allocated cache.
38782 */
38783 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
38784 ma_uint64 unused;
38785
38786 pDevice->playback.inputCacheConsumed = 0;
38787 pDevice->playback.inputCacheRemaining = 0;
38788
38790 /* We need a heap allocated cache. We want to size this based on the period size. */
38791 void* pNewInputCache;
38792 ma_uint64 newInputCacheCap;
38793 ma_uint64 newInputCacheSizeInBytes;
38794
38795 newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames);
38796
38797 newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
38798 if (newInputCacheSizeInBytes > MA_SIZE_MAX) {
38800 pDevice->playback.pInputCache = NULL;
38801 pDevice->playback.inputCacheCap = 0;
38802 return MA_OUT_OF_MEMORY; /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */
38803 }
38804
38805 pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks);
38806 if (pNewInputCache == NULL) {
38808 pDevice->playback.pInputCache = NULL;
38809 pDevice->playback.inputCacheCap = 0;
38810 return MA_OUT_OF_MEMORY;
38811 }
38812
38813 pDevice->playback.pInputCache = pNewInputCache;
38814 pDevice->playback.inputCacheCap = newInputCacheCap;
38815 } else {
38816 /* 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. */
38818 pDevice->playback.pInputCache = NULL;
38819 pDevice->playback.inputCacheCap = 0;
38820 }
38821 }
38822
38823 return MA_SUCCESS;
38824}
38825
38826MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pDescriptorPlayback, const ma_device_descriptor* pDescriptorCapture)
38827{
38828 ma_result result;
38829
38830 if (pDevice == NULL) {
38831 return MA_INVALID_ARGS;
38832 }
38833
38834 /* Capture. */
38835 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
38836 if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) {
38837 return MA_INVALID_ARGS;
38838 }
38839
38840 pDevice->capture.internalFormat = pDescriptorCapture->format;
38841 pDevice->capture.internalChannels = pDescriptorCapture->channels;
38842 pDevice->capture.internalSampleRate = pDescriptorCapture->sampleRate;
38843 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
38844 pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
38845 pDevice->capture.internalPeriods = pDescriptorCapture->periodCount;
38846
38847 if (pDevice->capture.internalPeriodSizeInFrames == 0) {
38849 }
38850 }
38851
38852 /* Playback. */
38853 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
38854 if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) {
38855 return MA_INVALID_ARGS;
38856 }
38857
38858 pDevice->playback.internalFormat = pDescriptorPlayback->format;
38859 pDevice->playback.internalChannels = pDescriptorPlayback->channels;
38860 pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate;
38861 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
38862 pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
38863 pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount;
38864
38865 if (pDevice->playback.internalPeriodSizeInFrames == 0) {
38867 }
38868 }
38869
38870 /*
38871 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.
38872 For loopback devices, we need to retrieve the name of the playback device.
38873 */
38874 {
38875 ma_device_info deviceInfo;
38876
38877 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
38878 result = ma_device_get_info(pDevice, (deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo);
38879 if (result == MA_SUCCESS) {
38880 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);
38881 } else {
38882 /* We failed to retrieve the device info. Fall back to a default name. */
38883 if (pDescriptorCapture->pDeviceID == NULL) {
38884 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
38885 } else {
38886 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1);
38887 }
38888 }
38889 }
38890
38891 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
38892 result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo);
38893 if (result == MA_SUCCESS) {
38894 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1);
38895 } else {
38896 /* We failed to retrieve the device info. Fall back to a default name. */
38897 if (pDescriptorPlayback->pDeviceID == NULL) {
38898 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
38899 } else {
38900 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1);
38901 }
38902 }
38903 }
38904 }
38905
38906 /* Update data conversion. */
38907 return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */
38908}
38909
38910
38911static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
38912{
38913 ma_device* pDevice = (ma_device*)pData;
38914 MA_ASSERT(pDevice != NULL);
38915
38916#ifdef MA_WIN32
38917 ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE);
38918#endif
38919
38920 /*
38921 When the device is being initialized it's initial state is set to ma_device_state_uninitialized. Before returning from
38922 ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately
38923 after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker
38924 thread to signal an event to know when the worker thread is ready for action.
38925 */
38926 ma_device__set_state(pDevice, ma_device_state_stopped);
38927 ma_event_signal(&pDevice->stopEvent);
38928
38929 for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */
38930 ma_result startResult;
38931 ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */
38932
38933 /* We wait on an event to know when something has requested that the device be started and the main loop entered. */
38934 ma_event_wait(&pDevice->wakeupEvent);
38935
38936 /* Default result code. */
38937 pDevice->workResult = MA_SUCCESS;
38938
38939 /* If the reason for the wake up is that we are terminating, just break from the loop. */
38941 break;
38942 }
38943
38944 /*
38945 Getting to this point means the device is wanting to get started. The function that has requested that the device
38946 be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event
38947 in both the success and error case. It's important that the state of the device is set _before_ signaling the event.
38948 */
38949 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting);
38950
38951 /* If the device has a start callback, start it now. */
38952 if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
38953 startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice);
38954 } else {
38955 startResult = MA_SUCCESS;
38956 }
38957
38958 /*
38959 If starting was not successful we'll need to loop back to the start and wait for something
38960 to happen (pDevice->wakeupEvent).
38961 */
38962 if (startResult != MA_SUCCESS) {
38963 pDevice->workResult = startResult;
38964 ma_event_signal(&pDevice->startEvent); /* <-- Always signal the start event so ma_device_start() can return as it'll be waiting on it. */
38965 continue;
38966 }
38967
38968 /* Make sure the state is set appropriately. */
38969 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. */
38970 ma_event_signal(&pDevice->startEvent);
38971
38972 ma_device__on_notification_started(pDevice);
38973
38974 if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) {
38975 pDevice->pContext->callbacks.onDeviceDataLoop(pDevice);
38976 } else {
38977 /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */
38978 ma_device_audio_thread__default_read_write(pDevice);
38979 }
38980
38981 /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */
38982 if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
38983 stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice);
38984 } else {
38985 stopResult = MA_SUCCESS; /* No stop callback with the backend. Just assume successful. */
38986 }
38987
38988 /*
38989 After the device has stopped, make sure an event is posted. Don't post a stopped event if
38990 stopping failed. This can happen on some backends when the underlying stream has been
38991 stopped due to the device being physically unplugged or disabled via an OS setting.
38992 */
38993 if (stopResult == MA_SUCCESS) {
38994 ma_device__on_notification_stopped(pDevice);
38995 }
38996
38997 /* 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. */
38998 ma_device__set_state(pDevice, ma_device_state_stopped);
38999 ma_event_signal(&pDevice->stopEvent);
39000 }
39001
39002#ifdef MA_WIN32
39003 ma_CoUninitialize(pDevice->pContext);
39004#endif
39005
39006 return (ma_thread_result)0;
39007}
39008
39009
39010/* Helper for determining whether or not the given device is initialized. */
39011static ma_bool32 ma_device__is_initialized(ma_device* pDevice)
39012{
39013 if (pDevice == NULL) {
39014 return MA_FALSE;
39015 }
39016
39018}
39019
39020
39021#ifdef MA_WIN32
39022static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext)
39023{
39024 /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */
39025#ifdef MA_WIN32_DESKTOP
39026 ma_CoUninitialize(pContext);
39027 ma_dlclose(pContext, pContext->win32.hUser32DLL);
39028 ma_dlclose(pContext, pContext->win32.hOle32DLL);
39029 ma_dlclose(pContext, pContext->win32.hAdvapi32DLL);
39030#else
39031 (void)pContext;
39032#endif
39033
39034 return MA_SUCCESS;
39035}
39036
39037static ma_result ma_context_init_backend_apis__win32(ma_context* pContext)
39038{
39039#ifdef MA_WIN32_DESKTOP
39040 /* Ole32.dll */
39041 pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll");
39042 if (pContext->win32.hOle32DLL == NULL) {
39044 }
39045
39046 pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitializeEx");
39047 pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoUninitialize");
39048 pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoCreateInstance");
39049 pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree");
39050 pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear");
39051 pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2");
39052
39053
39054 /* User32.dll */
39055 pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll");
39056 if (pContext->win32.hUser32DLL == NULL) {
39058 }
39059
39060 pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow");
39061 pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow");
39062
39063
39064 /* Advapi32.dll */
39065 pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll");
39066 if (pContext->win32.hAdvapi32DLL == NULL) {
39068 }
39069
39070 pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA");
39071 pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey");
39072 pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA");
39073#endif
39074
39075 ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);
39076 return MA_SUCCESS;
39077}
39078#else
39079static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext)
39080{
39081#if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING)
39082 ma_dlclose(pContext, pContext->posix.pthreadSO);
39083#else
39084 (void)pContext;
39085#endif
39086
39087 return MA_SUCCESS;
39088}
39089
39090static ma_result ma_context_init_backend_apis__nix(ma_context* pContext)
39091{
39092 /* pthread */
39093#if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING)
39094 const char* libpthreadFileNames[] = {
39095 "libpthread.so",
39096 "libpthread.so.0",
39097 "libpthread.dylib"
39098 };
39099 size_t i;
39100
39101 for (i = 0; i < sizeof(libpthreadFileNames) / sizeof(libpthreadFileNames[0]); ++i) {
39102 pContext->posix.pthreadSO = ma_dlopen(pContext, libpthreadFileNames[i]);
39103 if (pContext->posix.pthreadSO != NULL) {
39104 break;
39105 }
39106 }
39107
39108 if (pContext->posix.pthreadSO == NULL) {
39110 }
39111
39112 pContext->posix.pthread_create = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_create");
39113 pContext->posix.pthread_join = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_join");
39114 pContext->posix.pthread_mutex_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_init");
39115 pContext->posix.pthread_mutex_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_destroy");
39116 pContext->posix.pthread_mutex_lock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_lock");
39117 pContext->posix.pthread_mutex_unlock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_unlock");
39118 pContext->posix.pthread_cond_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_init");
39119 pContext->posix.pthread_cond_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_destroy");
39120 pContext->posix.pthread_cond_wait = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_wait");
39121 pContext->posix.pthread_cond_signal = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_signal");
39122 pContext->posix.pthread_attr_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_init");
39123 pContext->posix.pthread_attr_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_destroy");
39124 pContext->posix.pthread_attr_setschedpolicy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedpolicy");
39125 pContext->posix.pthread_attr_getschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_getschedparam");
39126 pContext->posix.pthread_attr_setschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedparam");
39127#else
39128 pContext->posix.pthread_create = (ma_proc)pthread_create;
39129 pContext->posix.pthread_join = (ma_proc)pthread_join;
39130 pContext->posix.pthread_mutex_init = (ma_proc)pthread_mutex_init;
39131 pContext->posix.pthread_mutex_destroy = (ma_proc)pthread_mutex_destroy;
39132 pContext->posix.pthread_mutex_lock = (ma_proc)pthread_mutex_lock;
39133 pContext->posix.pthread_mutex_unlock = (ma_proc)pthread_mutex_unlock;
39134 pContext->posix.pthread_cond_init = (ma_proc)pthread_cond_init;
39135 pContext->posix.pthread_cond_destroy = (ma_proc)pthread_cond_destroy;
39136 pContext->posix.pthread_cond_wait = (ma_proc)pthread_cond_wait;
39137 pContext->posix.pthread_cond_signal = (ma_proc)pthread_cond_signal;
39138 pContext->posix.pthread_attr_init = (ma_proc)pthread_attr_init;
39139 pContext->posix.pthread_attr_destroy = (ma_proc)pthread_attr_destroy;
39140#if !defined(__EMSCRIPTEN__)
39141 pContext->posix.pthread_attr_setschedpolicy = (ma_proc)pthread_attr_setschedpolicy;
39142 pContext->posix.pthread_attr_getschedparam = (ma_proc)pthread_attr_getschedparam;
39143 pContext->posix.pthread_attr_setschedparam = (ma_proc)pthread_attr_setschedparam;
39144#endif
39145#endif
39146
39147 return MA_SUCCESS;
39148}
39149#endif
39150
39151static ma_result ma_context_init_backend_apis(ma_context* pContext)
39152{
39153 ma_result result;
39154#ifdef MA_WIN32
39155 result = ma_context_init_backend_apis__win32(pContext);
39156#else
39157 result = ma_context_init_backend_apis__nix(pContext);
39158#endif
39159
39160 return result;
39161}
39162
39163static ma_result ma_context_uninit_backend_apis(ma_context* pContext)
39164{
39165 ma_result result;
39166#ifdef MA_WIN32
39167 result = ma_context_uninit_backend_apis__win32(pContext);
39168#else
39169 result = ma_context_uninit_backend_apis__nix(pContext);
39170#endif
39171
39172 return result;
39173}
39174
39175
39176static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext)
39177{
39178 MA_ASSERT(pContext != NULL);
39179
39180 if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) {
39181 if (pContext->callbacks.onDeviceDataLoop == NULL) {
39182 return MA_TRUE;
39183 } else {
39184 return MA_FALSE;
39185 }
39186 } else {
39187 return MA_FALSE;
39188 }
39189}
39190
39191
39192/* The default capacity doesn't need to be too big. */
39193#ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY
39194#define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32
39195#endif
39196
39198{
39200
39201 MA_ZERO_OBJECT(&config);
39202 config.noThread = MA_FALSE;
39203 config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY;
39204 config.jobQueueFlags = 0;
39205
39206 return config;
39207}
39208
39209
39210static ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData)
39211{
39212 ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData;
39213 MA_ASSERT(pJobThread != NULL);
39214
39215 for (;;) {
39216 ma_result result;
39217 ma_job job;
39218
39219 result = ma_device_job_thread_next(pJobThread, &job);
39220 if (result != MA_SUCCESS) {
39221 break;
39222 }
39223
39224 if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) {
39225 break;
39226 }
39227
39228 ma_job_process(&job);
39229 }
39230
39231 return (ma_thread_result)0;
39232}
39233
39235{
39236 ma_result result;
39237 ma_job_queue_config jobQueueConfig;
39238
39239 if (pJobThread == NULL) {
39240 return MA_INVALID_ARGS;
39241 }
39242
39243 MA_ZERO_OBJECT(pJobThread);
39244
39245 if (pConfig == NULL) {
39246 return MA_INVALID_ARGS;
39247 }
39248
39249
39250 /* Initialize the job queue before the thread to ensure it's in a valid state. */
39251 jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity);
39252
39253 result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue);
39254 if (result != MA_SUCCESS) {
39255 return result; /* Failed to initialize job queue. */
39256 }
39257
39258
39259 /* The thread needs to be initialized after the job queue to ensure the thread doesn't try to access it prematurely. */
39260 if (pConfig->noThread == MA_FALSE) {
39261 result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks);
39262 if (result != MA_SUCCESS) {
39263 ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks);
39264 return result; /* Failed to create the job thread. */
39265 }
39266
39267 pJobThread->_hasThread = MA_TRUE;
39268 } else {
39269 pJobThread->_hasThread = MA_FALSE;
39270 }
39271
39272
39273 return MA_SUCCESS;
39274}
39275
39276MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks)
39277{
39278 if (pJobThread == NULL) {
39279 return;
39280 }
39281
39282 /* 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. */
39283 {
39285 ma_device_job_thread_post(pJobThread, &job);
39286 }
39287
39288 /* Wait for the thread to terminate naturally. */
39289 if (pJobThread->_hasThread) {
39290 ma_thread_wait(&pJobThread->thread);
39291 }
39292
39293 /* At this point the thread should be terminated so we can safely uninitialize the job queue. */
39294 ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks);
39295}
39296
39298{
39299 if (pJobThread == NULL || pJob == NULL) {
39300 return MA_INVALID_ARGS;
39301 }
39302
39303 return ma_job_queue_post(&pJobThread->jobQueue, pJob);
39304}
39305
39307{
39308 if (pJob == NULL) {
39309 return MA_INVALID_ARGS;
39310 }
39311
39312 MA_ZERO_OBJECT(pJob);
39313
39314 if (pJobThread == NULL) {
39315 return MA_INVALID_ARGS;
39316 }
39317
39318 return ma_job_queue_next(&pJobThread->jobQueue, pJob);
39319}
39320
39321
39322
39324{
39325 ma_context_config config;
39326 MA_ZERO_OBJECT(&config);
39327
39328 return config;
39329}
39330
39331MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext)
39332{
39333 ma_result result;
39334 ma_context_config defaultConfig;
39335 ma_backend defaultBackends[ma_backend_null+1];
39336 ma_uint32 iBackend;
39337 ma_backend* pBackendsToIterate;
39338 ma_uint32 backendsToIterateCount;
39339
39340 if (pContext == NULL) {
39341 return MA_INVALID_ARGS;
39342 }
39343
39344 MA_ZERO_OBJECT(pContext);
39345
39346 /* Always make sure the config is set first to ensure properties are available as soon as possible. */
39347 if (pConfig == NULL) {
39348 defaultConfig = ma_context_config_init();
39349 pConfig = &defaultConfig;
39350 }
39351
39352 /* Allocation callbacks need to come first because they'll be passed around to other areas. */
39353 result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks);
39354 if (result != MA_SUCCESS) {
39355 return result;
39356 }
39357
39358 /* Get a lot set up first so we can start logging ASAP. */
39359 if (pConfig->pLog != NULL) {
39360 pContext->pLog = pConfig->pLog;
39361 } else {
39362 result = ma_log_init(&pContext->allocationCallbacks, &pContext->log);
39363 if (result == MA_SUCCESS) {
39364 pContext->pLog = &pContext->log;
39365 } else {
39366 pContext->pLog = NULL; /* Logging is not available. */
39367 }
39368 }
39369
39370 pContext->threadPriority = pConfig->threadPriority;
39371 pContext->threadStackSize = pConfig->threadStackSize;
39372 pContext->pUserData = pConfig->pUserData;
39373
39374 /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */
39375 result = ma_context_init_backend_apis(pContext);
39376 if (result != MA_SUCCESS) {
39377 return result;
39378 }
39379
39380 for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
39381 defaultBackends[iBackend] = (ma_backend)iBackend;
39382 }
39383
39384 pBackendsToIterate = (ma_backend*)backends;
39385 backendsToIterateCount = backendCount;
39386 if (pBackendsToIterate == NULL) {
39387 pBackendsToIterate = (ma_backend*)defaultBackends;
39388 backendsToIterateCount = ma_countof(defaultBackends);
39389 }
39390
39391 MA_ASSERT(pBackendsToIterate != NULL);
39392
39393 for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) {
39394 ma_backend backend = pBackendsToIterate[iBackend];
39395
39396 /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */
39397 MA_ZERO_OBJECT(&pContext->callbacks);
39398
39399 /* These backends are using the new callback system. */
39400 switch (backend) {
39401 #ifdef MA_HAS_WASAPI
39402 case ma_backend_wasapi:
39403 {
39404 pContext->callbacks.onContextInit = ma_context_init__wasapi;
39405 } break;
39406 #endif
39407 #ifdef MA_HAS_DSOUND
39408 case ma_backend_dsound:
39409 {
39410 pContext->callbacks.onContextInit = ma_context_init__dsound;
39411 } break;
39412 #endif
39413 #ifdef MA_HAS_WINMM
39414 case ma_backend_winmm:
39415 {
39416 pContext->callbacks.onContextInit = ma_context_init__winmm;
39417 } break;
39418 #endif
39419 #ifdef MA_HAS_COREAUDIO
39421 {
39422 pContext->callbacks.onContextInit = ma_context_init__coreaudio;
39423 } break;
39424 #endif
39425 #ifdef MA_HAS_SNDIO
39426 case ma_backend_sndio:
39427 {
39428 pContext->callbacks.onContextInit = ma_context_init__sndio;
39429 } break;
39430 #endif
39431 #ifdef MA_HAS_AUDIO4
39432 case ma_backend_audio4:
39433 {
39434 pContext->callbacks.onContextInit = ma_context_init__audio4;
39435 } break;
39436 #endif
39437 #ifdef MA_HAS_OSS
39438 case ma_backend_oss:
39439 {
39440 pContext->callbacks.onContextInit = ma_context_init__oss;
39441 } break;
39442 #endif
39443 #ifdef MA_HAS_PULSEAUDIO
39445 {
39446 pContext->callbacks.onContextInit = ma_context_init__pulse;
39447 } break;
39448 #endif
39449 #ifdef MA_HAS_ALSA
39450 case ma_backend_alsa:
39451 {
39452 pContext->callbacks.onContextInit = ma_context_init__alsa;
39453 } break;
39454 #endif
39455 #ifdef MA_HAS_JACK
39456 case ma_backend_jack:
39457 {
39458 pContext->callbacks.onContextInit = ma_context_init__jack;
39459 } break;
39460 #endif
39461 #ifdef MA_HAS_AAUDIO
39462 case ma_backend_aaudio:
39463 {
39464 pContext->callbacks.onContextInit = ma_context_init__aaudio;
39465 } break;
39466 #endif
39467 #ifdef MA_HAS_OPENSL
39468 case ma_backend_opensl:
39469 {
39470 pContext->callbacks.onContextInit = ma_context_init__opensl;
39471 } break;
39472 #endif
39473 #ifdef MA_HAS_WEBAUDIO
39475 {
39476 pContext->callbacks.onContextInit = ma_context_init__webaudio;
39477 } break;
39478 #endif
39479 #ifdef MA_HAS_CUSTOM
39480 case ma_backend_custom:
39481 {
39482 /* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */
39483 pContext->callbacks = pConfig->custom;
39484 } break;
39485 #endif
39486 #ifdef MA_HAS_NULL
39487 case ma_backend_null:
39488 {
39489 pContext->callbacks.onContextInit = ma_context_init__null;
39490 } break;
39491 #endif
39492
39493 default: break;
39494 }
39495
39496 if (pContext->callbacks.onContextInit != NULL) {
39497 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend));
39498 result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks);
39499 } else {
39500 result = MA_NO_BACKEND;
39501 }
39502
39503 /* If this iteration was successful, return. */
39504 if (result == MA_SUCCESS) {
39505 result = ma_mutex_init(&pContext->deviceEnumLock);
39506 if (result != MA_SUCCESS) {
39507 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");
39508 }
39509
39510 result = ma_mutex_init(&pContext->deviceInfoLock);
39511 if (result != MA_SUCCESS) {
39512 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");
39513 }
39514
39515 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n");
39516 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE");
39517 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO");
39518 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO");
39519 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO");
39520
39521 pContext->backend = backend;
39522 return result;
39523 } else {
39524 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend));
39525 }
39526 }
39527
39528 /* If we get here it means an error occurred. */
39529 MA_ZERO_OBJECT(pContext); /* Safety. */
39530 return MA_NO_BACKEND;
39531}
39532
39534{
39535 if (pContext == NULL) {
39536 return MA_INVALID_ARGS;
39537 }
39538
39539 if (pContext->callbacks.onContextUninit != NULL) {
39540 pContext->callbacks.onContextUninit(pContext);
39541 }
39542
39543 ma_mutex_uninit(&pContext->deviceEnumLock);
39544 ma_mutex_uninit(&pContext->deviceInfoLock);
39545 ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks);
39546 ma_context_uninit_backend_apis(pContext);
39547
39548 if (pContext->pLog == &pContext->log) {
39549 ma_log_uninit(&pContext->log);
39550 }
39551
39552 return MA_SUCCESS;
39553}
39554
39556{
39557 return sizeof(ma_context);
39558}
39559
39560
39562{
39563 if (pContext == NULL) {
39564 return NULL;
39565 }
39566
39567 return pContext->pLog;
39568}
39569
39570
39572{
39573 ma_result result;
39574
39575 if (pContext == NULL || callback == NULL) {
39576 return MA_INVALID_ARGS;
39577 }
39578
39579 if (pContext->callbacks.onContextEnumerateDevices == NULL) {
39580 return MA_INVALID_OPERATION;
39581 }
39582
39583 ma_mutex_lock(&pContext->deviceEnumLock);
39584 {
39585 result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData);
39586 }
39587 ma_mutex_unlock(&pContext->deviceEnumLock);
39588
39589 return result;
39590}
39591
39592
39593static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
39594{
39595 /*
39596 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
39597 it's just appended to the end. If it's a playback device it's inserted just before the first capture device.
39598 */
39599
39600 /*
39601 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
39602 simple fixed size increment for buffer expansion.
39603 */
39604 const ma_uint32 bufferExpansionCount = 2;
39605 const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount;
39606
39607 if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) {
39608 ma_uint32 newCapacity = pContext->deviceInfoCapacity + bufferExpansionCount;
39609 ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, &pContext->allocationCallbacks);
39610 if (pNewInfos == NULL) {
39611 return MA_FALSE; /* Out of memory. */
39612 }
39613
39614 pContext->pDeviceInfos = pNewInfos;
39615 pContext->deviceInfoCapacity = newCapacity;
39616 }
39617
39618 if (deviceType == ma_device_type_playback) {
39619 /* Playback. Insert just before the first capture device. */
39620
39621 /* The first thing to do is move all of the capture devices down a slot. */
39622 ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount;
39623 size_t iCaptureDevice;
39624 for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) {
39625 pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1];
39626 }
39627
39628 /* Now just insert where the first capture device was before moving it down a slot. */
39629 pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo;
39630 pContext->playbackDeviceInfoCount += 1;
39631 } else {
39632 /* Capture. Insert at the end. */
39633 pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo;
39634 pContext->captureDeviceInfoCount += 1;
39635 }
39636
39637 (void)pUserData;
39638 return MA_TRUE;
39639}
39640
39641MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount)
39642{
39643 ma_result result;
39644
39645 /* Safety. */
39646 if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL;
39647 if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0;
39648 if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL;
39649 if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0;
39650
39651 if (pContext == NULL) {
39652 return MA_INVALID_ARGS;
39653 }
39654
39655 if (pContext->callbacks.onContextEnumerateDevices == NULL) {
39656 return MA_INVALID_OPERATION;
39657 }
39658
39659 /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */
39660 ma_mutex_lock(&pContext->deviceEnumLock);
39661 {
39662 /* Reset everything first. */
39663 pContext->playbackDeviceInfoCount = 0;
39664 pContext->captureDeviceInfoCount = 0;
39665
39666 /* Now enumerate over available devices. */
39667 result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL);
39668 if (result == MA_SUCCESS) {
39669 /* Playback devices. */
39670 if (ppPlaybackDeviceInfos != NULL) {
39671 *ppPlaybackDeviceInfos = pContext->pDeviceInfos;
39672 }
39673 if (pPlaybackDeviceCount != NULL) {
39674 *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount;
39675 }
39676
39677 /* Capture devices. */
39678 if (ppCaptureDeviceInfos != NULL) {
39679 *ppCaptureDeviceInfos = pContext->pDeviceInfos + pContext->playbackDeviceInfoCount; /* Capture devices come after playback devices. */
39680 }
39681 if (pCaptureDeviceCount != NULL) {
39682 *pCaptureDeviceCount = pContext->captureDeviceInfoCount;
39683 }
39684 }
39685 }
39686 ma_mutex_unlock(&pContext->deviceEnumLock);
39687
39688 return result;
39689}
39690
39691MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
39692{
39693 ma_result result;
39694 ma_device_info deviceInfo;
39695
39696 /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */
39697 if (pContext == NULL || pDeviceInfo == NULL) {
39698 return MA_INVALID_ARGS;
39699 }
39700
39701 MA_ZERO_OBJECT(&deviceInfo);
39702
39703 /* Help the backend out by copying over the device ID if we have one. */
39704 if (pDeviceID != NULL) {
39705 MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID));
39706 }
39707
39708 if (pContext->callbacks.onContextGetDeviceInfo == NULL) {
39709 return MA_INVALID_OPERATION;
39710 }
39711
39712 ma_mutex_lock(&pContext->deviceInfoLock);
39713 {
39714 result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo);
39715 }
39716 ma_mutex_unlock(&pContext->deviceInfoLock);
39717
39718 *pDeviceInfo = deviceInfo;
39719 return result;
39720}
39721
39723{
39724 if (pContext == NULL) {
39725 return MA_FALSE;
39726 }
39727
39728 return ma_is_loopback_supported(pContext->backend);
39729}
39730
39731
39733{
39734 ma_device_config config;
39735 MA_ZERO_OBJECT(&config);
39736 config.deviceType = deviceType;
39737 config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate don't matter here. */
39738
39739 return config;
39740}
39741
39742MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
39743{
39744 ma_result result;
39745 ma_device_descriptor descriptorPlayback;
39746 ma_device_descriptor descriptorCapture;
39747
39748 /* The context can be null, in which case we self-manage it. */
39749 if (pContext == NULL) {
39750 return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice);
39751 }
39752
39753 if (pDevice == NULL) {
39754 return MA_INVALID_ARGS;
39755 }
39756
39757 MA_ZERO_OBJECT(pDevice);
39758
39759 if (pConfig == NULL) {
39760 return MA_INVALID_ARGS;
39761 }
39762
39763 /* Check that we have our callbacks defined. */
39764 if (pContext->callbacks.onDeviceInit == NULL) {
39765 return MA_INVALID_OPERATION;
39766 }
39767
39768 /* Basic config validation. */
39769 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
39770 if (pConfig->capture.channels > MA_MAX_CHANNELS) {
39771 return MA_INVALID_ARGS;
39772 }
39773
39774 if (!ma__is_channel_map_valid(pConfig->capture.pChannelMap, pConfig->capture.channels)) {
39775 return MA_INVALID_ARGS;
39776 }
39777 }
39778
39780 if (pConfig->playback.channels > MA_MAX_CHANNELS) {
39781 return MA_INVALID_ARGS;
39782 }
39783
39784 if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) {
39785 return MA_INVALID_ARGS;
39786 }
39787 }
39788
39789 pDevice->pContext = pContext;
39790
39791 /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */
39792 pDevice->pUserData = pConfig->pUserData;
39793 pDevice->onData = pConfig->dataCallback;
39794 pDevice->onNotification = pConfig->notificationCallback;
39795 pDevice->onStop = pConfig->stopCallback;
39796
39797 if (pConfig->playback.pDeviceID != NULL) {
39798 MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id));
39799 pDevice->playback.pID = &pDevice->playback.id;
39800 } else {
39801 pDevice->playback.pID = NULL;
39802 }
39803
39804 if (pConfig->capture.pDeviceID != NULL) {
39805 MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id));
39806 pDevice->capture.pID = &pDevice->capture.id;
39807 } else {
39808 pDevice->capture.pID = NULL;
39809 }
39810
39812 pDevice->noClip = pConfig->noClip;
39813 pDevice->noDisableDenormals = pConfig->noDisableDenormals;
39814 pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback;
39815 pDevice->masterVolumeFactor = 1;
39816
39817 pDevice->type = pConfig->deviceType;
39818 pDevice->sampleRate = pConfig->sampleRate;
39819 pDevice->resampling.algorithm = pConfig->resampling.algorithm;
39820 pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder;
39823
39824 pDevice->capture.shareMode = pConfig->capture.shareMode;
39825 pDevice->capture.format = pConfig->capture.format;
39826 pDevice->capture.channels = pConfig->capture.channels;
39827 ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels);
39828 pDevice->capture.channelMixMode = pConfig->capture.channelMixMode;
39829
39830 pDevice->playback.shareMode = pConfig->playback.shareMode;
39831 pDevice->playback.format = pConfig->playback.format;
39832 pDevice->playback.channels = pConfig->playback.channels;
39834 pDevice->playback.channelMixMode = pConfig->playback.channelMixMode;
39835
39836
39837 result = ma_mutex_init(&pDevice->startStopLock);
39838 if (result != MA_SUCCESS) {
39839 return result;
39840 }
39841
39842 /*
39843 When the device is started, the worker thread is the one that does the actual startup of the backend device. We
39844 use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device.
39845
39846 Each of these semaphores is released internally by the worker thread when the work is completed. The start
39847 semaphore is also used to wake up the worker thread.
39848 */
39849 result = ma_event_init(&pDevice->wakeupEvent);
39850 if (result != MA_SUCCESS) {
39851 ma_mutex_uninit(&pDevice->startStopLock);
39852 return result;
39853 }
39854
39855 result = ma_event_init(&pDevice->startEvent);
39856 if (result != MA_SUCCESS) {
39857 ma_event_uninit(&pDevice->wakeupEvent);
39858 ma_mutex_uninit(&pDevice->startStopLock);
39859 return result;
39860 }
39861
39862 result = ma_event_init(&pDevice->stopEvent);
39863 if (result != MA_SUCCESS) {
39864 ma_event_uninit(&pDevice->startEvent);
39865 ma_event_uninit(&pDevice->wakeupEvent);
39866 ma_mutex_uninit(&pDevice->startStopLock);
39867 return result;
39868 }
39869
39870
39871 MA_ZERO_OBJECT(&descriptorPlayback);
39872 descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID;
39873 descriptorPlayback.shareMode = pConfig->playback.shareMode;
39874 descriptorPlayback.format = pConfig->playback.format;
39875 descriptorPlayback.channels = pConfig->playback.channels;
39876 descriptorPlayback.sampleRate = pConfig->sampleRate;
39877 ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels);
39878 descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames;
39879 descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
39880 descriptorPlayback.periodCount = pConfig->periods;
39881
39882 if (descriptorPlayback.periodCount == 0) {
39883 descriptorPlayback.periodCount = MA_DEFAULT_PERIODS;
39884 }
39885
39886
39887 MA_ZERO_OBJECT(&descriptorCapture);
39888 descriptorCapture.pDeviceID = pConfig->capture.pDeviceID;
39889 descriptorCapture.shareMode = pConfig->capture.shareMode;
39890 descriptorCapture.format = pConfig->capture.format;
39891 descriptorCapture.channels = pConfig->capture.channels;
39892 descriptorCapture.sampleRate = pConfig->sampleRate;
39893 ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels);
39894 descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames;
39895 descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
39896 descriptorCapture.periodCount = pConfig->periods;
39897
39898 if (descriptorCapture.periodCount == 0) {
39899 descriptorCapture.periodCount = MA_DEFAULT_PERIODS;
39900 }
39901
39902
39903 result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture);
39904 if (result != MA_SUCCESS) {
39905 ma_event_uninit(&pDevice->startEvent);
39906 ma_event_uninit(&pDevice->wakeupEvent);
39907 ma_mutex_uninit(&pDevice->startStopLock);
39908 return result;
39909 }
39910
39911#if 0
39912 /*
39913 On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between
39914 the requested format and the internal format.
39915 */
39917 if (!ma_device_descriptor_is_valid(&descriptorCapture)) {
39918 ma_device_uninit(pDevice);
39919 return MA_INVALID_ARGS;
39920 }
39921
39922 pDevice->capture.internalFormat = descriptorCapture.format;
39923 pDevice->capture.internalChannels = descriptorCapture.channels;
39924 pDevice->capture.internalSampleRate = descriptorCapture.sampleRate;
39925 ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels);
39926 pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames;
39927 pDevice->capture.internalPeriods = descriptorCapture.periodCount;
39928
39929 if (pDevice->capture.internalPeriodSizeInFrames == 0) {
39931 }
39932 }
39933
39934 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
39935 if (!ma_device_descriptor_is_valid(&descriptorPlayback)) {
39936 ma_device_uninit(pDevice);
39937 return MA_INVALID_ARGS;
39938 }
39939
39940 pDevice->playback.internalFormat = descriptorPlayback.format;
39941 pDevice->playback.internalChannels = descriptorPlayback.channels;
39942 pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate;
39943 ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels);
39944 pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames;
39945 pDevice->playback.internalPeriods = descriptorPlayback.periodCount;
39946
39947 if (pDevice->playback.internalPeriodSizeInFrames == 0) {
39949 }
39950 }
39951
39952
39953 /*
39954 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.
39955 For loopback devices, we need to retrieve the name of the playback device.
39956 */
39957 {
39958 ma_device_info deviceInfo;
39959
39962 if (result == MA_SUCCESS) {
39963 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);
39964 } else {
39965 /* We failed to retrieve the device info. Fall back to a default name. */
39966 if (descriptorCapture.pDeviceID == NULL) {
39967 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
39968 } else {
39969 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1);
39970 }
39971 }
39972 }
39973
39974 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
39975 result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo);
39976 if (result == MA_SUCCESS) {
39977 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1);
39978 } else {
39979 /* We failed to retrieve the device info. Fall back to a default name. */
39980 if (descriptorPlayback.pDeviceID == NULL) {
39981 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
39982 } else {
39983 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1);
39984 }
39985 }
39986 }
39987 }
39988
39989
39990 ma_device__post_init_setup(pDevice, pConfig->deviceType);
39991#endif
39992
39993 result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture);
39994 if (result != MA_SUCCESS) {
39995 ma_device_uninit(pDevice);
39996 return result;
39997 }
39998
39999
40000
40001 /*
40002 If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to
40003 be done after post_init_setup() because we'll need access to the sample rate.
40004 */
40005 if (pConfig->noFixedSizedCallback == MA_FALSE) {
40006 /* We're using a fixed sized data callback so we'll need an intermediary buffer. */
40007 ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames;
40008 if (intermediaryBufferCap == 0) {
40010 }
40011
40013 ma_uint32 intermediaryBufferSizeInBytes;
40014
40015 pDevice->capture.intermediaryBufferLen = 0;
40016 pDevice->capture.intermediaryBufferCap = intermediaryBufferCap;
40017 if (pDevice->capture.intermediaryBufferCap == 0) {
40019 }
40020
40021 intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
40022
40023 pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks);
40024 if (pDevice->capture.pIntermediaryBuffer == NULL) {
40025 ma_device_uninit(pDevice);
40026 return MA_OUT_OF_MEMORY;
40027 }
40028
40029 /* Silence the buffer for safety. */
40032 }
40033
40034 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
40035 ma_uint64 intermediaryBufferSizeInBytes;
40036
40037 pDevice->playback.intermediaryBufferLen = 0;
40038 if (pConfig->deviceType == ma_device_type_duplex) {
40039 pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap; /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */
40040 } else {
40041 pDevice->playback.intermediaryBufferCap = intermediaryBufferCap;
40042 if (pDevice->playback.intermediaryBufferCap == 0) {
40044 }
40045 }
40046
40047 intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
40048
40049 pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks);
40050 if (pDevice->playback.pIntermediaryBuffer == NULL) {
40051 ma_device_uninit(pDevice);
40052 return MA_OUT_OF_MEMORY;
40053 }
40054
40055 /* Silence the buffer for safety. */
40057 pDevice->playback.intermediaryBufferLen = 0;
40058 }
40059 } else {
40060 /* Not using a fixed sized data callback so no need for an intermediary buffer. */
40061 }
40062
40063
40064 /* Some backends don't require the worker thread. */
40065 if (!ma_context_is_backend_asynchronous(pContext)) {
40066 /* The worker thread. */
40067 result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks);
40068 if (result != MA_SUCCESS) {
40069 ma_device_uninit(pDevice);
40070 return result;
40071 }
40072
40073 /* Wait for the worker thread to put the device into it's stopped state for real. */
40074 ma_event_wait(&pDevice->stopEvent);
40075 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
40076 } else {
40077 /*
40078 If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done
40079 after ma_device__post_init_setup().
40080 */
40081 if (ma_context_is_backend_asynchronous(pContext)) {
40082 if (pConfig->deviceType == ma_device_type_duplex) {
40083 result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);
40084 if (result != MA_SUCCESS) {
40085 ma_device_uninit(pDevice);
40086 return result;
40087 }
40088 }
40089 }
40090
40091 ma_device__set_state(pDevice, ma_device_state_stopped);
40092 }
40093
40094 /* Log device information. */
40095 {
40097 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
40098 char name[MA_MAX_DEVICE_NAME_LENGTH + 1];
40099 ma_device_get_name(pDevice, ma_device_type_capture, name, sizeof(name), NULL);
40100
40101 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture");
40103 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels);
40104 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate);
40106 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n");
40107 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO");
40108 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO");
40109 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO");
40110 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO");
40111 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO");
40112 }
40113 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
40114 char name[MA_MAX_DEVICE_NAME_LENGTH + 1];
40115 ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL);
40116
40117 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Playback");
40119 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels);
40120 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate);
40122 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n");
40123 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO");
40124 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO");
40125 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO");
40126 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO");
40127 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO");
40128 }
40129 }
40130
40131 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
40132 return MA_SUCCESS;
40133}
40134
40135MA_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)
40136{
40137 ma_result result;
40138 ma_context* pContext;
40139 ma_backend defaultBackends[ma_backend_null+1];
40140 ma_uint32 iBackend;
40141 ma_backend* pBackendsToIterate;
40142 ma_uint32 backendsToIterateCount;
40143 ma_allocation_callbacks allocationCallbacks;
40144
40145 if (pConfig == NULL) {
40146 return MA_INVALID_ARGS;
40147 }
40148
40149 if (pContextConfig != NULL) {
40150 result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks);
40151 if (result != MA_SUCCESS) {
40152 return result;
40153 }
40154 } else {
40155 allocationCallbacks = ma_allocation_callbacks_init_default();
40156 }
40157
40158
40159 pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks);
40160 if (pContext == NULL) {
40161 return MA_OUT_OF_MEMORY;
40162 }
40163
40164 for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
40165 defaultBackends[iBackend] = (ma_backend)iBackend;
40166 }
40167
40168 pBackendsToIterate = (ma_backend*)backends;
40169 backendsToIterateCount = backendCount;
40170 if (pBackendsToIterate == NULL) {
40171 pBackendsToIterate = (ma_backend*)defaultBackends;
40172 backendsToIterateCount = ma_countof(defaultBackends);
40173 }
40174
40175 result = MA_NO_BACKEND;
40176
40177 for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
40178 result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext);
40179 if (result == MA_SUCCESS) {
40180 result = ma_device_init(pContext, pConfig, pDevice);
40181 if (result == MA_SUCCESS) {
40182 break; /* Success. */
40183 } else {
40184 ma_context_uninit(pContext); /* Failure. */
40185 }
40186 }
40187 }
40188
40189 if (result != MA_SUCCESS) {
40190 ma_free(pContext, &allocationCallbacks);
40191 return result;
40192 }
40193
40194 pDevice->isOwnerOfContext = MA_TRUE;
40195 return result;
40196}
40197
40198MA_API void ma_device_uninit(ma_device* pDevice)
40199{
40200 if (!ma_device__is_initialized(pDevice)) {
40201 return;
40202 }
40203
40204 /* 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. */
40205 if (ma_device_is_started(pDevice)) {
40206 ma_device_stop(pDevice);
40207 }
40208
40209 /* Putting the device into an uninitialized state will make the worker thread return. */
40210 ma_device__set_state(pDevice, ma_device_state_uninitialized);
40211
40212 /* Wake up the worker thread and wait for it to properly terminate. */
40213 if (!ma_context_is_backend_asynchronous(pDevice->pContext)) {
40214 ma_event_signal(&pDevice->wakeupEvent);
40215 ma_thread_wait(&pDevice->thread);
40216 }
40217
40218 if (pDevice->pContext->callbacks.onDeviceUninit != NULL) {
40219 pDevice->pContext->callbacks.onDeviceUninit(pDevice);
40220 }
40221
40222
40223 ma_event_uninit(&pDevice->stopEvent);
40224 ma_event_uninit(&pDevice->startEvent);
40225 ma_event_uninit(&pDevice->wakeupEvent);
40226 ma_mutex_uninit(&pDevice->startStopLock);
40227
40228 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
40229 if (pDevice->type == ma_device_type_duplex) {
40230 ma_duplex_rb_uninit(&pDevice->duplexRB);
40231 }
40232 }
40233
40234 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
40236 }
40237 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
40239 }
40240
40241 if (pDevice->playback.pInputCache != NULL) {
40243 }
40244
40245 if (pDevice->capture.pIntermediaryBuffer != NULL) {
40247 }
40248 if (pDevice->playback.pIntermediaryBuffer != NULL) {
40250 }
40251
40252 if (pDevice->isOwnerOfContext) {
40253 ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks;
40254
40255 ma_context_uninit(pDevice->pContext);
40256 ma_free(pDevice->pContext, &allocationCallbacks);
40257 }
40258
40259 MA_ZERO_OBJECT(pDevice);
40260}
40261
40263{
40264 if (pDevice == NULL) {
40265 return NULL;
40266 }
40267
40268 return pDevice->pContext;
40269}
40270
40272{
40274}
40275
40277{
40278 if (pDeviceInfo == NULL) {
40279 return MA_INVALID_ARGS;
40280 }
40281
40282 MA_ZERO_OBJECT(pDeviceInfo);
40283
40284 if (pDevice == NULL) {
40285 return MA_INVALID_ARGS;
40286 }
40287
40288 /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */
40289 if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) {
40290 return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo);
40291 }
40292
40293 /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */
40294 if (type == ma_device_type_playback) {
40295 return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo);
40296 } else {
40297 return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo);
40298 }
40299}
40300
40301MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator)
40302{
40303 ma_result result;
40304 ma_device_info deviceInfo;
40305
40306 if (pLengthNotIncludingNullTerminator != NULL) {
40307 *pLengthNotIncludingNullTerminator = 0;
40308 }
40309
40310 if (pName != NULL && nameCap > 0) {
40311 pName[0] = '\0';
40312 }
40313
40314 result = ma_device_get_info(pDevice, type, &deviceInfo);
40315 if (result != MA_SUCCESS) {
40316 return result;
40317 }
40318
40319 if (pName != NULL) {
40320 ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1);
40321
40322 /*
40323 For safety, make sure the length is based on the truncated output string rather than the
40324 source. Otherwise the caller might assume the output buffer contains more content than it
40325 actually does.
40326 */
40327 if (pLengthNotIncludingNullTerminator != NULL) {
40328 *pLengthNotIncludingNullTerminator = strlen(pName);
40329 }
40330 } else {
40331 /* Name not specified. Just report the length of the source string. */
40332 if (pLengthNotIncludingNullTerminator != NULL) {
40333 *pLengthNotIncludingNullTerminator = strlen(deviceInfo.name);
40334 }
40335 }
40336
40337 return MA_SUCCESS;
40338}
40339
40341{
40342 ma_result result;
40343
40344 if (pDevice == NULL) {
40345 return MA_INVALID_ARGS;
40346 }
40347
40349 return MA_INVALID_OPERATION; /* Not initialized. */
40350 }
40351
40353 return MA_SUCCESS; /* Already started. */
40354 }
40355
40356 ma_mutex_lock(&pDevice->startStopLock);
40357 {
40358 /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */
40359 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
40360
40361 ma_device__set_state(pDevice, ma_device_state_starting);
40362
40363 /* Asynchronous backends need to be handled differently. */
40364 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
40365 if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
40366 result = pDevice->pContext->callbacks.onDeviceStart(pDevice);
40367 } else {
40368 result = MA_INVALID_OPERATION;
40369 }
40370
40371 if (result == MA_SUCCESS) {
40372 ma_device__set_state(pDevice, ma_device_state_started);
40373 ma_device__on_notification_started(pDevice);
40374 }
40375 } else {
40376 /*
40377 Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the
40378 thread and then wait for the start event.
40379 */
40380 ma_event_signal(&pDevice->wakeupEvent);
40381
40382 /*
40383 Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device
40384 into the started state. Don't call ma_device__set_state() here.
40385 */
40386 ma_event_wait(&pDevice->startEvent);
40387 result = pDevice->workResult;
40388 }
40389
40390 /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */
40391 if (result != MA_SUCCESS) {
40392 ma_device__set_state(pDevice, ma_device_state_stopped);
40393 }
40394 }
40395 ma_mutex_unlock(&pDevice->startStopLock);
40396
40397 return result;
40398}
40399
40401{
40402 ma_result result;
40403
40404 if (pDevice == NULL) {
40405 return MA_INVALID_ARGS;
40406 }
40407
40409 return MA_INVALID_OPERATION; /* Not initialized. */
40410 }
40411
40413 return MA_SUCCESS; /* Already stopped. */
40414 }
40415
40416 ma_mutex_lock(&pDevice->startStopLock);
40417 {
40418 /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */
40419 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started);
40420
40421 ma_device__set_state(pDevice, ma_device_state_stopping);
40422
40423 /* Asynchronous backends need to be handled differently. */
40424 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
40425 /* Asynchronous backends must have a stop operation. */
40426 if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
40427 result = pDevice->pContext->callbacks.onDeviceStop(pDevice);
40428 } else {
40429 result = MA_INVALID_OPERATION;
40430 }
40431
40432 ma_device__set_state(pDevice, ma_device_state_stopped);
40433 } else {
40434 /*
40435 Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If
40436 the backend is implementing it's own audio thread loop we'll need to wake it up if required. Note that we need to make
40437 sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super
40438 important though, so I'm asserting it here as well for extra safety in case we accidentally change something later.
40439 */
40440 MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started);
40441
40442 if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) {
40443 pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice);
40444 }
40445
40446 /*
40447 We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be
40448 the one who puts the device into the stopped state. Don't call ma_device__set_state() here.
40449 */
40450 ma_event_wait(&pDevice->stopEvent);
40451 result = MA_SUCCESS;
40452 }
40453 }
40454 ma_mutex_unlock(&pDevice->startStopLock);
40455
40456 return result;
40457}
40458
40460{
40462}
40463
40465{
40466 if (pDevice == NULL) {
40468 }
40469
40470 return (ma_device_state)c89atomic_load_i32((ma_int32*)&pDevice->state); /* Naughty cast to get rid of a const warning. */
40471}
40472
40474{
40475 if (pDevice == NULL) {
40476 return MA_INVALID_ARGS;
40477 }
40478
40479 if (volume < 0.0f) {
40480 return MA_INVALID_ARGS;
40481 }
40482
40483 c89atomic_exchange_f32(&pDevice->masterVolumeFactor, volume);
40484
40485 return MA_SUCCESS;
40486}
40487
40488MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume)
40489{
40490 if (pVolume == NULL) {
40491 return MA_INVALID_ARGS;
40492 }
40493
40494 if (pDevice == NULL) {
40495 *pVolume = 0;
40496 return MA_INVALID_ARGS;
40497 }
40498
40499 *pVolume = c89atomic_load_f32(&pDevice->masterVolumeFactor);
40500
40501 return MA_SUCCESS;
40502}
40503
40505{
40506 if (gainDB > 0) {
40507 return MA_INVALID_ARGS;
40508 }
40509
40510 return ma_device_set_master_volume(pDevice, ma_volume_db_to_linear(gainDB));
40511}
40512
40514{
40515 float factor;
40516 ma_result result;
40517
40518 if (pGainDB == NULL) {
40519 return MA_INVALID_ARGS;
40520 }
40521
40522 result = ma_device_get_master_volume(pDevice, &factor);
40523 if (result != MA_SUCCESS) {
40524 *pGainDB = 0;
40525 return result;
40526 }
40527
40528 *pGainDB = ma_volume_linear_to_db(factor);
40529
40530 return MA_SUCCESS;
40531}
40532
40533
40534MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
40535{
40536 if (pDevice == NULL) {
40537 return MA_INVALID_ARGS;
40538 }
40539
40540 if (pOutput == NULL && pInput == NULL) {
40541 return MA_INVALID_ARGS;
40542 }
40543
40544 if (pDevice->type == ma_device_type_duplex) {
40545 if (pInput != NULL) {
40546 ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb);
40547 }
40548
40549 if (pOutput != NULL) {
40550 ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb);
40551 }
40552 } else {
40553 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) {
40554 if (pInput == NULL) {
40555 return MA_INVALID_ARGS;
40556 }
40557
40558 ma_device__send_frames_to_client(pDevice, frameCount, pInput);
40559 }
40560
40561 if (pDevice->type == ma_device_type_playback) {
40562 if (pOutput == NULL) {
40563 return MA_INVALID_ARGS;
40564 }
40565
40566 ma_device__read_frames_from_client(pDevice, frameCount, pOutput);
40567 }
40568 }
40569
40570 return MA_SUCCESS;
40571}
40572
40574{
40575 if (pDescriptor == NULL) {
40576 return 0;
40577 }
40578
40579 /*
40580 We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the
40581 time when the size of the buffer needs to be determined. In this case we need to just take a best
40582 guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll
40583 just fall back to MA_DEFAULT_SAMPLE_RATE.
40584 */
40585 if (nativeSampleRate == 0) {
40586 nativeSampleRate = pDescriptor->sampleRate;
40587 }
40588 if (nativeSampleRate == 0) {
40589 nativeSampleRate = MA_DEFAULT_SAMPLE_RATE;
40590 }
40591
40592 MA_ASSERT(nativeSampleRate != 0);
40593
40594 if (pDescriptor->periodSizeInFrames == 0) {
40595 if (pDescriptor->periodSizeInMilliseconds == 0) {
40596 if (performanceProfile == ma_performance_profile_low_latency) {
40597 return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate);
40598 } else {
40599 return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate);
40600 }
40601 } else {
40603 }
40604 } else {
40605 return pDescriptor->periodSizeInFrames;
40606 }
40607}
40608#endif /* MA_NO_DEVICE_IO */
40609
40610
40612{
40613 /* Prevent a division by zero. */
40614 if (sampleRate == 0) {
40615 return 0;
40616 }
40617
40618 return bufferSizeInFrames*1000 / sampleRate;
40619}
40620
40622{
40623 /* Prevent a division by zero. */
40624 if (sampleRate == 0) {
40625 return 0;
40626 }
40627
40628 return bufferSizeInMilliseconds*sampleRate / 1000;
40629}
40630
40631MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
40632{
40633 if (dst == src) {
40634 return; /* No-op. */
40635 }
40636
40637 ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels));
40638}
40639
40640MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
40641{
40642 if (format == ma_format_u8) {
40643 ma_uint64 sampleCount = frameCount * channels;
40644 ma_uint64 iSample;
40645 for (iSample = 0; iSample < sampleCount; iSample += 1) {
40646 ((ma_uint8*)p)[iSample] = 128;
40647 }
40648 } else {
40649 ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels));
40650 }
40651}
40652
40653MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
40654{
40655 return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels));
40656}
40657
40658MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
40659{
40660 return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels));
40661}
40662
40663
40664MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count)
40665{
40666 ma_uint64 iSample;
40667
40668 MA_ASSERT(pDst != NULL);
40669 MA_ASSERT(pSrc != NULL);
40670
40671 for (iSample = 0; iSample < count; iSample += 1) {
40672 pDst[iSample] = ma_clip_u8(pSrc[iSample]);
40673 }
40674}
40675
40676MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count)
40677{
40678 ma_uint64 iSample;
40679
40680 MA_ASSERT(pDst != NULL);
40681 MA_ASSERT(pSrc != NULL);
40682
40683 for (iSample = 0; iSample < count; iSample += 1) {
40684 pDst[iSample] = ma_clip_s16(pSrc[iSample]);
40685 }
40686}
40687
40688MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count)
40689{
40690 ma_uint64 iSample;
40691
40692 MA_ASSERT(pDst != NULL);
40693 MA_ASSERT(pSrc != NULL);
40694
40695 for (iSample = 0; iSample < count; iSample += 1) {
40696 ma_int64 s = ma_clip_s24(pSrc[iSample]);
40697 pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0);
40698 pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8);
40699 pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);
40700 }
40701}
40702
40703MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count)
40704{
40705 ma_uint64 iSample;
40706
40707 MA_ASSERT(pDst != NULL);
40708 MA_ASSERT(pSrc != NULL);
40709
40710 for (iSample = 0; iSample < count; iSample += 1) {
40711 pDst[iSample] = ma_clip_s32(pSrc[iSample]);
40712 }
40713}
40714
40715MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count)
40716{
40717 ma_uint64 iSample;
40718
40719 MA_ASSERT(pDst != NULL);
40720 MA_ASSERT(pSrc != NULL);
40721
40722 for (iSample = 0; iSample < count; iSample += 1) {
40723 pDst[iSample] = ma_clip_f32(pSrc[iSample]);
40724 }
40725}
40726
40727MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
40728{
40729 ma_uint64 sampleCount;
40730
40731 MA_ASSERT(pDst != NULL);
40732 MA_ASSERT(pSrc != NULL);
40733
40734 sampleCount = frameCount * channels;
40735
40736 switch (format) {
40737 case ma_format_u8: ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break;
40738 case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break;
40739 case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break;
40740 case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break;
40741 case ma_format_f32: ma_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount); break;
40742
40743 /* 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. */
40744 case ma_format_unknown:
40745 case ma_format_count:
40746 break;
40747 }
40748}
40749
40750
40751MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor)
40752{
40753 ma_uint64 iSample;
40754
40755 if (pSamplesOut == NULL || pSamplesIn == NULL) {
40756 return;
40757 }
40758
40759 for (iSample = 0; iSample < sampleCount; iSample += 1) {
40760 pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor);
40761 }
40762}
40763
40764MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor)
40765{
40766 ma_uint64 iSample;
40767
40768 if (pSamplesOut == NULL || pSamplesIn == NULL) {
40769 return;
40770 }
40771
40772 for (iSample = 0; iSample < sampleCount; iSample += 1) {
40773 pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor);
40774 }
40775}
40776
40777MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor)
40778{
40779 ma_uint64 iSample;
40780 ma_uint8* pSamplesOut8;
40781 ma_uint8* pSamplesIn8;
40782
40783 if (pSamplesOut == NULL || pSamplesIn == NULL) {
40784 return;
40785 }
40786
40787 pSamplesOut8 = (ma_uint8*)pSamplesOut;
40788 pSamplesIn8 = (ma_uint8*)pSamplesIn;
40789
40790 for (iSample = 0; iSample < sampleCount; iSample += 1) {
40791 ma_int32 sampleS32;
40792
40793 sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24);
40794 sampleS32 = (ma_int32)(sampleS32 * factor);
40795
40796 pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8);
40797 pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16);
40798 pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24);
40799 }
40800}
40801
40802MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor)
40803{
40804 ma_uint64 iSample;
40805
40806 if (pSamplesOut == NULL || pSamplesIn == NULL) {
40807 return;
40808 }
40809
40810 for (iSample = 0; iSample < sampleCount; iSample += 1) {
40811 pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor);
40812 }
40813}
40814
40815MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor)
40816{
40817 ma_uint64 iSample;
40818
40819 if (pSamplesOut == NULL || pSamplesIn == NULL) {
40820 return;
40821 }
40822
40823 if (factor == 1) {
40824 if (pSamplesOut == pSamplesIn) {
40825 /* In place. No-op. */
40826 } else {
40827 /* Just a copy. */
40828 for (iSample = 0; iSample < sampleCount; iSample += 1) {
40829 pSamplesOut[iSample] = pSamplesIn[iSample];
40830 }
40831 }
40832 } else {
40833 for (iSample = 0; iSample < sampleCount; iSample += 1) {
40834 pSamplesOut[iSample] = pSamplesIn[iSample] * factor;
40835 }
40836 }
40837}
40838
40839MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor)
40840{
40841 ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor);
40842}
40843
40844MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor)
40845{
40846 ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor);
40847}
40848
40849MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor)
40850{
40851 ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor);
40852}
40853
40854MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor)
40855{
40856 ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor);
40857}
40858
40859MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor)
40860{
40861 ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor);
40862}
40863
40864MA_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)
40865{
40866 ma_copy_and_apply_volume_factor_u8(pFramesOut, pFramesIn, frameCount*channels, factor);
40867}
40868
40869MA_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)
40870{
40871 ma_copy_and_apply_volume_factor_s16(pFramesOut, pFramesIn, frameCount*channels, factor);
40872}
40873
40874MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
40875{
40876 ma_copy_and_apply_volume_factor_s24(pFramesOut, pFramesIn, frameCount*channels, factor);
40877}
40878
40879MA_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)
40880{
40881 ma_copy_and_apply_volume_factor_s32(pFramesOut, pFramesIn, frameCount*channels, factor);
40882}
40883
40884MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
40885{
40886 ma_copy_and_apply_volume_factor_f32(pFramesOut, pFramesIn, frameCount*channels, factor);
40887}
40888
40889MA_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)
40890{
40891 switch (format)
40892 {
40893 case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pFramesOut, (const ma_uint8*)pFramesIn, frameCount, channels, factor); return;
40894 case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pFramesOut, (const ma_int16*)pFramesIn, frameCount, channels, factor); return;
40895 case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pFramesOut, pFramesIn, frameCount, channels, factor); return;
40896 case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pFramesOut, (const ma_int32*)pFramesIn, frameCount, channels, factor); return;
40897 case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pFramesOut, (const float*)pFramesIn, frameCount, channels, factor); return;
40898 default: return; /* Do nothing. */
40899 }
40900}
40901
40902MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
40903{
40904 ma_copy_and_apply_volume_factor_pcm_frames_u8(pFrames, pFrames, frameCount, channels, factor);
40905}
40906
40907MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
40908{
40909 ma_copy_and_apply_volume_factor_pcm_frames_s16(pFrames, pFrames, frameCount, channels, factor);
40910}
40911
40912MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
40913{
40914 ma_copy_and_apply_volume_factor_pcm_frames_s24(pFrames, pFrames, frameCount, channels, factor);
40915}
40916
40917MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
40918{
40919 ma_copy_and_apply_volume_factor_pcm_frames_s32(pFrames, pFrames, frameCount, channels, factor);
40920}
40921
40922MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
40923{
40924 ma_copy_and_apply_volume_factor_pcm_frames_f32(pFrames, pFrames, frameCount, channels, factor);
40925}
40926
40927MA_API void ma_apply_volume_factor_pcm_frames(void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor)
40928{
40929 ma_copy_and_apply_volume_factor_pcm_frames(pFramesOut, pFramesOut, frameCount, format, channels, factor);
40930}
40931
40932
40933MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains)
40934{
40935 ma_uint64 iFrame;
40936
40937 if (channels == 2) {
40938 /* TODO: Do an optimized implementation for stereo and mono. Can do a SIMD optimized implementation as well. */
40939 }
40940
40941 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
40942 ma_uint32 iChannel;
40943 for (iChannel = 0; iChannel < channels; iChannel += 1) {
40944 pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel];
40945 }
40946 }
40947}
40948
40949
40950
40951static MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume)
40952{
40953 return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8);
40954}
40955
40956static MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume)
40957{
40958 return (ma_int32)((x * volume) >> 8);
40959}
40960
40961static MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume)
40962{
40963 return (ma_int64)((x * volume) >> 8);
40964}
40965
40966static MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume)
40967{
40968 return (ma_int64)((x * volume) >> 8);
40969}
40970
40971static MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume)
40972{
40973 return x * volume;
40974}
40975
40976
40977MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume)
40978{
40979 ma_uint64 iSample;
40980 ma_int16 volumeFixed;
40981
40982 MA_ASSERT(pDst != NULL);
40983 MA_ASSERT(pSrc != NULL);
40984
40985 volumeFixed = ma_float_to_fixed_16(volume);
40986
40987 for (iSample = 0; iSample < count; iSample += 1) {
40988 pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed));
40989 }
40990}
40991
40992MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume)
40993{
40994 ma_uint64 iSample;
40995 ma_int16 volumeFixed;
40996
40997 MA_ASSERT(pDst != NULL);
40998 MA_ASSERT(pSrc != NULL);
40999
41000 volumeFixed = ma_float_to_fixed_16(volume);
41001
41002 for (iSample = 0; iSample < count; iSample += 1) {
41003 pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed));
41004 }
41005}
41006
41007MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume)
41008{
41009 ma_uint64 iSample;
41010 ma_int16 volumeFixed;
41011
41012 MA_ASSERT(pDst != NULL);
41013 MA_ASSERT(pSrc != NULL);
41014
41015 volumeFixed = ma_float_to_fixed_16(volume);
41016
41017 for (iSample = 0; iSample < count; iSample += 1) {
41018 ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed));
41019 pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0);
41020 pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8);
41021 pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);
41022 }
41023}
41024
41025MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume)
41026{
41027 ma_uint64 iSample;
41028 ma_int16 volumeFixed;
41029
41030 MA_ASSERT(pDst != NULL);
41031 MA_ASSERT(pSrc != NULL);
41032
41033 volumeFixed = ma_float_to_fixed_16(volume);
41034
41035 for (iSample = 0; iSample < count; iSample += 1) {
41036 pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed));
41037 }
41038}
41039
41040MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume)
41041{
41042 ma_uint64 iSample;
41043
41044 MA_ASSERT(pDst != NULL);
41045 MA_ASSERT(pSrc != NULL);
41046
41047 /* For the f32 case we need to make sure this supports in-place processing where the input and output buffers are the same. */
41048
41049 for (iSample = 0; iSample < count; iSample += 1) {
41050 pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume));
41051 }
41052}
41053
41054MA_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)
41055{
41056 MA_ASSERT(pDst != NULL);
41057 MA_ASSERT(pSrc != NULL);
41058
41059 if (volume == 1) {
41060 ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels); /* Optimized case for volume = 1. */
41061 } else if (volume == 0) {
41062 ma_silence_pcm_frames(pDst, frameCount, format, channels); /* Optimized case for volume = 0. */
41063 } else {
41064 ma_uint64 sampleCount = frameCount * channels;
41065
41066 switch (format) {
41067 case ma_format_u8: ma_copy_and_apply_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break;
41068 case ma_format_s16: ma_copy_and_apply_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break;
41069 case ma_format_s24: ma_copy_and_apply_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break;
41070 case ma_format_s32: ma_copy_and_apply_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break;
41071 case ma_format_f32: ma_copy_and_apply_volume_and_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break;
41072
41073 /* 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. */
41074 case ma_format_unknown:
41075 case ma_format_count:
41076 break;
41077 }
41078 }
41079}
41080
41081
41082
41083MA_API float ma_volume_linear_to_db(float factor)
41084{
41085 return 20*ma_log10f(factor);
41086}
41087
41088MA_API float ma_volume_db_to_linear(float gain)
41089{
41090 return ma_powf(10, gain/20.0f);
41091}
41092
41093
41094
41095
41101static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x)
41102{
41103 return (ma_int16)(x * 32767.0f);
41104}
41105
41106static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x)
41107{
41108 return (ma_int16)((ma_int16)x - 128);
41109}
41110
41111static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x)
41112{
41113 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. */
41114}
41115
41116static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24)
41117{
41118 s24[0] = (ma_uint8)((x & 0x000000FF) >> 0);
41119 s24[1] = (ma_uint8)((x & 0x0000FF00) >> 8);
41120 s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16);
41121}
41122
41123
41124/* u8 */
41125MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41126{
41127 (void)ditherMode;
41128 ma_copy_memory_64(dst, src, count * sizeof(ma_uint8));
41129}
41130
41131
41132static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41133{
41134 ma_int16* dst_s16 = (ma_int16*)dst;
41135 const ma_uint8* src_u8 = (const ma_uint8*)src;
41136
41137 ma_uint64 i;
41138 for (i = 0; i < count; i += 1) {
41139 ma_int16 x = src_u8[i];
41140 x = (ma_int16)(x - 128);
41141 x = (ma_int16)(x << 8);
41142 dst_s16[i] = x;
41143 }
41144
41145 (void)ditherMode;
41146}
41147
41148static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41149{
41150 ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
41151}
41152
41153#if defined(MA_SUPPORT_SSE2)
41154static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41155{
41156 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
41157}
41158#endif
41159#if defined(MA_SUPPORT_AVX2)
41160static MA_INLINE void ma_pcm_u8_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41161{
41162 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
41163}
41164#endif
41165#if defined(MA_SUPPORT_NEON)
41166static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41167{
41168 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
41169}
41170#endif
41171
41172MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41173{
41174#ifdef MA_USE_REFERENCE_CONVERSION_APIS
41175 ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
41176#else
41177 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
41178 if (ma_has_avx2()) {
41179 ma_pcm_u8_to_s16__avx2(dst, src, count, ditherMode);
41180 } else
41181 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
41182 if (ma_has_sse2()) {
41183 ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode);
41184 } else
41185 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
41186 if (ma_has_neon()) {
41187 ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode);
41188 } else
41189 #endif
41190 {
41191 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
41192 }
41193#endif
41194}
41195
41196
41197static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41198{
41199 ma_uint8* dst_s24 = (ma_uint8*)dst;
41200 const ma_uint8* src_u8 = (const ma_uint8*)src;
41201
41202 ma_uint64 i;
41203 for (i = 0; i < count; i += 1) {
41204 ma_int16 x = src_u8[i];
41205 x = (ma_int16)(x - 128);
41206
41207 dst_s24[i*3+0] = 0;
41208 dst_s24[i*3+1] = 0;
41209 dst_s24[i*3+2] = (ma_uint8)((ma_int8)x);
41210 }
41211
41212 (void)ditherMode;
41213}
41214
41215static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41216{
41217 ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
41218}
41219
41220#if defined(MA_SUPPORT_SSE2)
41221static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41222{
41223 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
41224}
41225#endif
41226#if defined(MA_SUPPORT_AVX2)
41227static MA_INLINE void ma_pcm_u8_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41228{
41229 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
41230}
41231#endif
41232#if defined(MA_SUPPORT_NEON)
41233static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41234{
41235 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
41236}
41237#endif
41238
41239MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41240{
41241#ifdef MA_USE_REFERENCE_CONVERSION_APIS
41242 ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
41243#else
41244 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
41245 if (ma_has_avx2()) {
41246 ma_pcm_u8_to_s24__avx2(dst, src, count, ditherMode);
41247 } else
41248 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
41249 if (ma_has_sse2()) {
41250 ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode);
41251 } else
41252 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
41253 if (ma_has_neon()) {
41254 ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode);
41255 } else
41256 #endif
41257 {
41258 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
41259 }
41260#endif
41261}
41262
41263
41264static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41265{
41266 ma_int32* dst_s32 = (ma_int32*)dst;
41267 const ma_uint8* src_u8 = (const ma_uint8*)src;
41268
41269 ma_uint64 i;
41270 for (i = 0; i < count; i += 1) {
41271 ma_int32 x = src_u8[i];
41272 x = x - 128;
41273 x = x << 24;
41274 dst_s32[i] = x;
41275 }
41276
41277 (void)ditherMode;
41278}
41279
41280static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41281{
41282 ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
41283}
41284
41285#if defined(MA_SUPPORT_SSE2)
41286static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41287{
41288 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
41289}
41290#endif
41291#if defined(MA_SUPPORT_AVX2)
41292static MA_INLINE void ma_pcm_u8_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41293{
41294 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
41295}
41296#endif
41297#if defined(MA_SUPPORT_NEON)
41298static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41299{
41300 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
41301}
41302#endif
41303
41304MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41305{
41306#ifdef MA_USE_REFERENCE_CONVERSION_APIS
41307 ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
41308#else
41309 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
41310 if (ma_has_avx2()) {
41311 ma_pcm_u8_to_s32__avx2(dst, src, count, ditherMode);
41312 } else
41313 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
41314 if (ma_has_sse2()) {
41315 ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode);
41316 } else
41317 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
41318 if (ma_has_neon()) {
41319 ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode);
41320 } else
41321 #endif
41322 {
41323 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
41324 }
41325#endif
41326}
41327
41328
41329static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41330{
41331 float* dst_f32 = (float*)dst;
41332 const ma_uint8* src_u8 = (const ma_uint8*)src;
41333
41334 ma_uint64 i;
41335 for (i = 0; i < count; i += 1) {
41336 float x = (float)src_u8[i];
41337 x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */
41338 x = x - 1; /* 0..2 to -1..1 */
41339
41340 dst_f32[i] = x;
41341 }
41342
41343 (void)ditherMode;
41344}
41345
41346static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41347{
41348 ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
41349}
41350
41351#if defined(MA_SUPPORT_SSE2)
41352static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41353{
41354 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
41355}
41356#endif
41357#if defined(MA_SUPPORT_AVX2)
41358static MA_INLINE void ma_pcm_u8_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41359{
41360 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
41361}
41362#endif
41363#if defined(MA_SUPPORT_NEON)
41364static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41365{
41366 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
41367}
41368#endif
41369
41370MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41371{
41372#ifdef MA_USE_REFERENCE_CONVERSION_APIS
41373 ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
41374#else
41375 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
41376 if (ma_has_avx2()) {
41377 ma_pcm_u8_to_f32__avx2(dst, src, count, ditherMode);
41378 } else
41379 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
41380 if (ma_has_sse2()) {
41381 ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode);
41382 } else
41383 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
41384 if (ma_has_neon()) {
41385 ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode);
41386 } else
41387 #endif
41388 {
41389 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
41390 }
41391#endif
41392}
41393
41394
41395#ifdef MA_USE_REFERENCE_CONVERSION_APIS
41396static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
41397{
41398 ma_uint8* dst_u8 = (ma_uint8*)dst;
41399 const ma_uint8** src_u8 = (const ma_uint8**)src;
41400
41401 ma_uint64 iFrame;
41402 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41403 ma_uint32 iChannel;
41404 for (iChannel = 0; iChannel < channels; iChannel += 1) {
41405 dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
41406 }
41407 }
41408}
41409#else
41410static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
41411{
41412 ma_uint8* dst_u8 = (ma_uint8*)dst;
41413 const ma_uint8** src_u8 = (const ma_uint8**)src;
41414
41415 if (channels == 1) {
41416 ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8));
41417 } else if (channels == 2) {
41418 ma_uint64 iFrame;
41419 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41420 dst_u8[iFrame*2 + 0] = src_u8[0][iFrame];
41421 dst_u8[iFrame*2 + 1] = src_u8[1][iFrame];
41422 }
41423 } else {
41424 ma_uint64 iFrame;
41425 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41426 ma_uint32 iChannel;
41427 for (iChannel = 0; iChannel < channels; iChannel += 1) {
41428 dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
41429 }
41430 }
41431 }
41432}
41433#endif
41434
41435MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
41436{
41437#ifdef MA_USE_REFERENCE_CONVERSION_APIS
41438 ma_pcm_interleave_u8__reference(dst, src, frameCount, channels);
41439#else
41440 ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels);
41441#endif
41442}
41443
41444
41445static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
41446{
41447 ma_uint8** dst_u8 = (ma_uint8**)dst;
41448 const ma_uint8* src_u8 = (const ma_uint8*)src;
41449
41450 ma_uint64 iFrame;
41451 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41452 ma_uint32 iChannel;
41453 for (iChannel = 0; iChannel < channels; iChannel += 1) {
41454 dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel];
41455 }
41456 }
41457}
41458
41459static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
41460{
41461 ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
41462}
41463
41464MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
41465{
41466#ifdef MA_USE_REFERENCE_CONVERSION_APIS
41467 ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
41468#else
41469 ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels);
41470#endif
41471}
41472
41473
41474/* s16 */
41475static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41476{
41477 ma_uint8* dst_u8 = (ma_uint8*)dst;
41478 const ma_int16* src_s16 = (const ma_int16*)src;
41479
41480 if (ditherMode == ma_dither_mode_none) {
41481 ma_uint64 i;
41482 for (i = 0; i < count; i += 1) {
41483 ma_int16 x = src_s16[i];
41484 x = (ma_int16)(x >> 8);
41485 x = (ma_int16)(x + 128);
41486 dst_u8[i] = (ma_uint8)x;
41487 }
41488 } else {
41489 ma_uint64 i;
41490 for (i = 0; i < count; i += 1) {
41491 ma_int16 x = src_s16[i];
41492
41493 /* Dither. Don't overflow. */
41494 ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F);
41495 if ((x + dither) <= 0x7FFF) {
41496 x = (ma_int16)(x + dither);
41497 } else {
41498 x = 0x7FFF;
41499 }
41500
41501 x = (ma_int16)(x >> 8);
41502 x = (ma_int16)(x + 128);
41503 dst_u8[i] = (ma_uint8)x;
41504 }
41505 }
41506}
41507
41508static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41509{
41510 ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
41511}
41512
41513#if defined(MA_SUPPORT_SSE2)
41514static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41515{
41516 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
41517}
41518#endif
41519#if defined(MA_SUPPORT_AVX2)
41520static MA_INLINE void ma_pcm_s16_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41521{
41522 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
41523}
41524#endif
41525#if defined(MA_SUPPORT_NEON)
41526static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41527{
41528 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
41529}
41530#endif
41531
41532MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41533{
41534#ifdef MA_USE_REFERENCE_CONVERSION_APIS
41535 ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
41536#else
41537 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
41538 if (ma_has_avx2()) {
41539 ma_pcm_s16_to_u8__avx2(dst, src, count, ditherMode);
41540 } else
41541 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
41542 if (ma_has_sse2()) {
41543 ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode);
41544 } else
41545 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
41546 if (ma_has_neon()) {
41547 ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode);
41548 } else
41549 #endif
41550 {
41551 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
41552 }
41553#endif
41554}
41555
41556
41557MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41558{
41559 (void)ditherMode;
41560 ma_copy_memory_64(dst, src, count * sizeof(ma_int16));
41561}
41562
41563
41564static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41565{
41566 ma_uint8* dst_s24 = (ma_uint8*)dst;
41567 const ma_int16* src_s16 = (const ma_int16*)src;
41568
41569 ma_uint64 i;
41570 for (i = 0; i < count; i += 1) {
41571 dst_s24[i*3+0] = 0;
41572 dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF);
41573 dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8);
41574 }
41575
41576 (void)ditherMode;
41577}
41578
41579static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41580{
41581 ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
41582}
41583
41584#if defined(MA_SUPPORT_SSE2)
41585static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41586{
41587 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
41588}
41589#endif
41590#if defined(MA_SUPPORT_AVX2)
41591static MA_INLINE void ma_pcm_s16_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41592{
41593 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
41594}
41595#endif
41596#if defined(MA_SUPPORT_NEON)
41597static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41598{
41599 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
41600}
41601#endif
41602
41603MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41604{
41605#ifdef MA_USE_REFERENCE_CONVERSION_APIS
41606 ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
41607#else
41608 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
41609 if (ma_has_avx2()) {
41610 ma_pcm_s16_to_s24__avx2(dst, src, count, ditherMode);
41611 } else
41612 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
41613 if (ma_has_sse2()) {
41614 ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode);
41615 } else
41616 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
41617 if (ma_has_neon()) {
41618 ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode);
41619 } else
41620 #endif
41621 {
41622 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
41623 }
41624#endif
41625}
41626
41627
41628static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41629{
41630 ma_int32* dst_s32 = (ma_int32*)dst;
41631 const ma_int16* src_s16 = (const ma_int16*)src;
41632
41633 ma_uint64 i;
41634 for (i = 0; i < count; i += 1) {
41635 dst_s32[i] = src_s16[i] << 16;
41636 }
41637
41638 (void)ditherMode;
41639}
41640
41641static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41642{
41643 ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
41644}
41645
41646#if defined(MA_SUPPORT_SSE2)
41647static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41648{
41649 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
41650}
41651#endif
41652#if defined(MA_SUPPORT_AVX2)
41653static MA_INLINE void ma_pcm_s16_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41654{
41655 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
41656}
41657#endif
41658#if defined(MA_SUPPORT_NEON)
41659static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41660{
41661 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
41662}
41663#endif
41664
41665MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41666{
41667#ifdef MA_USE_REFERENCE_CONVERSION_APIS
41668 ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
41669#else
41670 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
41671 if (ma_has_avx2()) {
41672 ma_pcm_s16_to_s32__avx2(dst, src, count, ditherMode);
41673 } else
41674 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
41675 if (ma_has_sse2()) {
41676 ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode);
41677 } else
41678 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
41679 if (ma_has_neon()) {
41680 ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode);
41681 } else
41682 #endif
41683 {
41684 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
41685 }
41686#endif
41687}
41688
41689
41690static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41691{
41692 float* dst_f32 = (float*)dst;
41693 const ma_int16* src_s16 = (const ma_int16*)src;
41694
41695 ma_uint64 i;
41696 for (i = 0; i < count; i += 1) {
41697 float x = (float)src_s16[i];
41698
41699#if 0
41700 /* The accurate way. */
41701 x = x + 32768.0f; /* -32768..32767 to 0..65535 */
41702 x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */
41703 x = x - 1; /* 0..2 to -1..1 */
41704#else
41705 /* The fast way. */
41706 x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */
41707#endif
41708
41709 dst_f32[i] = x;
41710 }
41711
41712 (void)ditherMode;
41713}
41714
41715static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41716{
41717 ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
41718}
41719
41720#if defined(MA_SUPPORT_SSE2)
41721static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41722{
41723 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
41724}
41725#endif
41726#if defined(MA_SUPPORT_AVX2)
41727static MA_INLINE void ma_pcm_s16_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41728{
41729 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
41730}
41731#endif
41732#if defined(MA_SUPPORT_NEON)
41733static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41734{
41735 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
41736}
41737#endif
41738
41739MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41740{
41741#ifdef MA_USE_REFERENCE_CONVERSION_APIS
41742 ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
41743#else
41744 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
41745 if (ma_has_avx2()) {
41746 ma_pcm_s16_to_f32__avx2(dst, src, count, ditherMode);
41747 } else
41748 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
41749 if (ma_has_sse2()) {
41750 ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode);
41751 } else
41752 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
41753 if (ma_has_neon()) {
41754 ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode);
41755 } else
41756 #endif
41757 {
41758 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
41759 }
41760#endif
41761}
41762
41763
41764static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
41765{
41766 ma_int16* dst_s16 = (ma_int16*)dst;
41767 const ma_int16** src_s16 = (const ma_int16**)src;
41768
41769 ma_uint64 iFrame;
41770 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41771 ma_uint32 iChannel;
41772 for (iChannel = 0; iChannel < channels; iChannel += 1) {
41773 dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame];
41774 }
41775 }
41776}
41777
41778static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
41779{
41780 ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
41781}
41782
41783MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
41784{
41785#ifdef MA_USE_REFERENCE_CONVERSION_APIS
41786 ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
41787#else
41788 ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels);
41789#endif
41790}
41791
41792
41793static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
41794{
41795 ma_int16** dst_s16 = (ma_int16**)dst;
41796 const ma_int16* src_s16 = (const ma_int16*)src;
41797
41798 ma_uint64 iFrame;
41799 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
41800 ma_uint32 iChannel;
41801 for (iChannel = 0; iChannel < channels; iChannel += 1) {
41802 dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel];
41803 }
41804 }
41805}
41806
41807static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
41808{
41809 ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
41810}
41811
41812MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
41813{
41814#ifdef MA_USE_REFERENCE_CONVERSION_APIS
41815 ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
41816#else
41817 ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels);
41818#endif
41819}
41820
41821
41822/* s24 */
41823static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41824{
41825 ma_uint8* dst_u8 = (ma_uint8*)dst;
41826 const ma_uint8* src_s24 = (const ma_uint8*)src;
41827
41828 if (ditherMode == ma_dither_mode_none) {
41829 ma_uint64 i;
41830 for (i = 0; i < count; i += 1) {
41831 dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128);
41832 }
41833 } else {
41834 ma_uint64 i;
41835 for (i = 0; i < count; i += 1) {
41836 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);
41837
41838 /* Dither. Don't overflow. */
41839 ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
41840 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
41841 x = x + dither;
41842 } else {
41843 x = 0x7FFFFFFF;
41844 }
41845
41846 x = x >> 24;
41847 x = x + 128;
41848 dst_u8[i] = (ma_uint8)x;
41849 }
41850 }
41851}
41852
41853static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41854{
41855 ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
41856}
41857
41858#if defined(MA_SUPPORT_SSE2)
41859static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41860{
41861 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
41862}
41863#endif
41864#if defined(MA_SUPPORT_AVX2)
41865static MA_INLINE void ma_pcm_s24_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41866{
41867 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
41868}
41869#endif
41870#if defined(MA_SUPPORT_NEON)
41871static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41872{
41873 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
41874}
41875#endif
41876
41877MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41878{
41879#ifdef MA_USE_REFERENCE_CONVERSION_APIS
41880 ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
41881#else
41882 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
41883 if (ma_has_avx2()) {
41884 ma_pcm_s24_to_u8__avx2(dst, src, count, ditherMode);
41885 } else
41886 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
41887 if (ma_has_sse2()) {
41888 ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode);
41889 } else
41890 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
41891 if (ma_has_neon()) {
41892 ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode);
41893 } else
41894 #endif
41895 {
41896 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
41897 }
41898#endif
41899}
41900
41901
41902static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41903{
41904 ma_int16* dst_s16 = (ma_int16*)dst;
41905 const ma_uint8* src_s24 = (const ma_uint8*)src;
41906
41907 if (ditherMode == ma_dither_mode_none) {
41908 ma_uint64 i;
41909 for (i = 0; i < count; i += 1) {
41910 ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]);
41911 ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8);
41912 dst_s16[i] = (ma_int16)(dst_lo | dst_hi);
41913 }
41914 } else {
41915 ma_uint64 i;
41916 for (i = 0; i < count; i += 1) {
41917 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);
41918
41919 /* Dither. Don't overflow. */
41920 ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
41921 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
41922 x = x + dither;
41923 } else {
41924 x = 0x7FFFFFFF;
41925 }
41926
41927 x = x >> 16;
41928 dst_s16[i] = (ma_int16)x;
41929 }
41930 }
41931}
41932
41933static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41934{
41935 ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
41936}
41937
41938#if defined(MA_SUPPORT_SSE2)
41939static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41940{
41941 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
41942}
41943#endif
41944#if defined(MA_SUPPORT_AVX2)
41945static MA_INLINE void ma_pcm_s24_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41946{
41947 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
41948}
41949#endif
41950#if defined(MA_SUPPORT_NEON)
41951static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41952{
41953 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
41954}
41955#endif
41956
41957MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41958{
41959#ifdef MA_USE_REFERENCE_CONVERSION_APIS
41960 ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
41961#else
41962 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
41963 if (ma_has_avx2()) {
41964 ma_pcm_s24_to_s16__avx2(dst, src, count, ditherMode);
41965 } else
41966 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
41967 if (ma_has_sse2()) {
41968 ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode);
41969 } else
41970 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
41971 if (ma_has_neon()) {
41972 ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode);
41973 } else
41974 #endif
41975 {
41976 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
41977 }
41978#endif
41979}
41980
41981
41982MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41983{
41984 (void)ditherMode;
41985
41986 ma_copy_memory_64(dst, src, count * 3);
41987}
41988
41989
41990static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
41991{
41992 ma_int32* dst_s32 = (ma_int32*)dst;
41993 const ma_uint8* src_s24 = (const ma_uint8*)src;
41994
41995 ma_uint64 i;
41996 for (i = 0; i < count; i += 1) {
41997 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);
41998 }
41999
42000 (void)ditherMode;
42001}
42002
42003static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42004{
42005 ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
42006}
42007
42008#if defined(MA_SUPPORT_SSE2)
42009static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42010{
42011 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
42012}
42013#endif
42014#if defined(MA_SUPPORT_AVX2)
42015static MA_INLINE void ma_pcm_s24_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42016{
42017 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
42018}
42019#endif
42020#if defined(MA_SUPPORT_NEON)
42021static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42022{
42023 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
42024}
42025#endif
42026
42027MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42028{
42029#ifdef MA_USE_REFERENCE_CONVERSION_APIS
42030 ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
42031#else
42032 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
42033 if (ma_has_avx2()) {
42034 ma_pcm_s24_to_s32__avx2(dst, src, count, ditherMode);
42035 } else
42036 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
42037 if (ma_has_sse2()) {
42038 ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode);
42039 } else
42040 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
42041 if (ma_has_neon()) {
42042 ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode);
42043 } else
42044 #endif
42045 {
42046 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
42047 }
42048#endif
42049}
42050
42051
42052static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42053{
42054 float* dst_f32 = (float*)dst;
42055 const ma_uint8* src_s24 = (const ma_uint8*)src;
42056
42057 ma_uint64 i;
42058 for (i = 0; i < count; i += 1) {
42059 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);
42060
42061#if 0
42062 /* The accurate way. */
42063 x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */
42064 x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */
42065 x = x - 1; /* 0..2 to -1..1 */
42066#else
42067 /* The fast way. */
42068 x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */
42069#endif
42070
42071 dst_f32[i] = x;
42072 }
42073
42074 (void)ditherMode;
42075}
42076
42077static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42078{
42079 ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
42080}
42081
42082#if defined(MA_SUPPORT_SSE2)
42083static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42084{
42085 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
42086}
42087#endif
42088#if defined(MA_SUPPORT_AVX2)
42089static MA_INLINE void ma_pcm_s24_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42090{
42091 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
42092}
42093#endif
42094#if defined(MA_SUPPORT_NEON)
42095static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42096{
42097 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
42098}
42099#endif
42100
42101MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42102{
42103#ifdef MA_USE_REFERENCE_CONVERSION_APIS
42104 ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
42105#else
42106 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
42107 if (ma_has_avx2()) {
42108 ma_pcm_s24_to_f32__avx2(dst, src, count, ditherMode);
42109 } else
42110 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
42111 if (ma_has_sse2()) {
42112 ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode);
42113 } else
42114 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
42115 if (ma_has_neon()) {
42116 ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode);
42117 } else
42118 #endif
42119 {
42120 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
42121 }
42122#endif
42123}
42124
42125
42126static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
42127{
42128 ma_uint8* dst8 = (ma_uint8*)dst;
42129 const ma_uint8** src8 = (const ma_uint8**)src;
42130
42131 ma_uint64 iFrame;
42132 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42133 ma_uint32 iChannel;
42134 for (iChannel = 0; iChannel < channels; iChannel += 1) {
42135 dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0];
42136 dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1];
42137 dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2];
42138 }
42139 }
42140}
42141
42142static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
42143{
42144 ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
42145}
42146
42147MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
42148{
42149#ifdef MA_USE_REFERENCE_CONVERSION_APIS
42150 ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
42151#else
42152 ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels);
42153#endif
42154}
42155
42156
42157static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
42158{
42159 ma_uint8** dst8 = (ma_uint8**)dst;
42160 const ma_uint8* src8 = (const ma_uint8*)src;
42161
42162 ma_uint32 iFrame;
42163 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42164 ma_uint32 iChannel;
42165 for (iChannel = 0; iChannel < channels; iChannel += 1) {
42166 dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0];
42167 dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1];
42168 dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2];
42169 }
42170 }
42171}
42172
42173static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
42174{
42175 ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
42176}
42177
42178MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
42179{
42180#ifdef MA_USE_REFERENCE_CONVERSION_APIS
42181 ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
42182#else
42183 ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels);
42184#endif
42185}
42186
42187
42188
42189/* s32 */
42190static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42191{
42192 ma_uint8* dst_u8 = (ma_uint8*)dst;
42193 const ma_int32* src_s32 = (const ma_int32*)src;
42194
42195 if (ditherMode == ma_dither_mode_none) {
42196 ma_uint64 i;
42197 for (i = 0; i < count; i += 1) {
42198 ma_int32 x = src_s32[i];
42199 x = x >> 24;
42200 x = x + 128;
42201 dst_u8[i] = (ma_uint8)x;
42202 }
42203 } else {
42204 ma_uint64 i;
42205 for (i = 0; i < count; i += 1) {
42206 ma_int32 x = src_s32[i];
42207
42208 /* Dither. Don't overflow. */
42209 ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
42210 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
42211 x = x + dither;
42212 } else {
42213 x = 0x7FFFFFFF;
42214 }
42215
42216 x = x >> 24;
42217 x = x + 128;
42218 dst_u8[i] = (ma_uint8)x;
42219 }
42220 }
42221}
42222
42223static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42224{
42225 ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
42226}
42227
42228#if defined(MA_SUPPORT_SSE2)
42229static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42230{
42231 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
42232}
42233#endif
42234#if defined(MA_SUPPORT_AVX2)
42235static MA_INLINE void ma_pcm_s32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42236{
42237 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
42238}
42239#endif
42240#if defined(MA_SUPPORT_NEON)
42241static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42242{
42243 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
42244}
42245#endif
42246
42247MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42248{
42249#ifdef MA_USE_REFERENCE_CONVERSION_APIS
42250 ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
42251#else
42252 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
42253 if (ma_has_avx2()) {
42254 ma_pcm_s32_to_u8__avx2(dst, src, count, ditherMode);
42255 } else
42256 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
42257 if (ma_has_sse2()) {
42258 ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode);
42259 } else
42260 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
42261 if (ma_has_neon()) {
42262 ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode);
42263 } else
42264 #endif
42265 {
42266 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
42267 }
42268#endif
42269}
42270
42271
42272static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42273{
42274 ma_int16* dst_s16 = (ma_int16*)dst;
42275 const ma_int32* src_s32 = (const ma_int32*)src;
42276
42277 if (ditherMode == ma_dither_mode_none) {
42278 ma_uint64 i;
42279 for (i = 0; i < count; i += 1) {
42280 ma_int32 x = src_s32[i];
42281 x = x >> 16;
42282 dst_s16[i] = (ma_int16)x;
42283 }
42284 } else {
42285 ma_uint64 i;
42286 for (i = 0; i < count; i += 1) {
42287 ma_int32 x = src_s32[i];
42288
42289 /* Dither. Don't overflow. */
42290 ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
42291 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
42292 x = x + dither;
42293 } else {
42294 x = 0x7FFFFFFF;
42295 }
42296
42297 x = x >> 16;
42298 dst_s16[i] = (ma_int16)x;
42299 }
42300 }
42301}
42302
42303static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42304{
42305 ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
42306}
42307
42308#if defined(MA_SUPPORT_SSE2)
42309static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42310{
42311 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
42312}
42313#endif
42314#if defined(MA_SUPPORT_AVX2)
42315static MA_INLINE void ma_pcm_s32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42316{
42317 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
42318}
42319#endif
42320#if defined(MA_SUPPORT_NEON)
42321static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42322{
42323 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
42324}
42325#endif
42326
42327MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42328{
42329#ifdef MA_USE_REFERENCE_CONVERSION_APIS
42330 ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
42331#else
42332 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
42333 if (ma_has_avx2()) {
42334 ma_pcm_s32_to_s16__avx2(dst, src, count, ditherMode);
42335 } else
42336 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
42337 if (ma_has_sse2()) {
42338 ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode);
42339 } else
42340 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
42341 if (ma_has_neon()) {
42342 ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode);
42343 } else
42344 #endif
42345 {
42346 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
42347 }
42348#endif
42349}
42350
42351
42352static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42353{
42354 ma_uint8* dst_s24 = (ma_uint8*)dst;
42355 const ma_int32* src_s32 = (const ma_int32*)src;
42356
42357 ma_uint64 i;
42358 for (i = 0; i < count; i += 1) {
42359 ma_uint32 x = (ma_uint32)src_s32[i];
42360 dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8);
42361 dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16);
42362 dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24);
42363 }
42364
42365 (void)ditherMode; /* No dithering for s32 -> s24. */
42366}
42367
42368static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42369{
42370 ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
42371}
42372
42373#if defined(MA_SUPPORT_SSE2)
42374static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42375{
42376 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
42377}
42378#endif
42379#if defined(MA_SUPPORT_AVX2)
42380static MA_INLINE void ma_pcm_s32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42381{
42382 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
42383}
42384#endif
42385#if defined(MA_SUPPORT_NEON)
42386static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42387{
42388 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
42389}
42390#endif
42391
42392MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42393{
42394#ifdef MA_USE_REFERENCE_CONVERSION_APIS
42395 ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
42396#else
42397 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
42398 if (ma_has_avx2()) {
42399 ma_pcm_s32_to_s24__avx2(dst, src, count, ditherMode);
42400 } else
42401 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
42402 if (ma_has_sse2()) {
42403 ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode);
42404 } else
42405 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
42406 if (ma_has_neon()) {
42407 ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode);
42408 } else
42409 #endif
42410 {
42411 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
42412 }
42413#endif
42414}
42415
42416
42417MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42418{
42419 (void)ditherMode;
42420
42421 ma_copy_memory_64(dst, src, count * sizeof(ma_int32));
42422}
42423
42424
42425static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42426{
42427 float* dst_f32 = (float*)dst;
42428 const ma_int32* src_s32 = (const ma_int32*)src;
42429
42430 ma_uint64 i;
42431 for (i = 0; i < count; i += 1) {
42432 double x = src_s32[i];
42433
42434#if 0
42435 x = x + 2147483648.0;
42436 x = x * 0.0000000004656612873077392578125;
42437 x = x - 1;
42438#else
42439 x = x / 2147483648.0;
42440#endif
42441
42442 dst_f32[i] = (float)x;
42443 }
42444
42445 (void)ditherMode; /* No dithering for s32 -> f32. */
42446}
42447
42448static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42449{
42450 ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
42451}
42452
42453#if defined(MA_SUPPORT_SSE2)
42454static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42455{
42456 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
42457}
42458#endif
42459#if defined(MA_SUPPORT_AVX2)
42460static MA_INLINE void ma_pcm_s32_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42461{
42462 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
42463}
42464#endif
42465#if defined(MA_SUPPORT_NEON)
42466static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42467{
42468 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
42469}
42470#endif
42471
42472MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42473{
42474#ifdef MA_USE_REFERENCE_CONVERSION_APIS
42475 ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
42476#else
42477 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
42478 if (ma_has_avx2()) {
42479 ma_pcm_s32_to_f32__avx2(dst, src, count, ditherMode);
42480 } else
42481 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
42482 if (ma_has_sse2()) {
42483 ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode);
42484 } else
42485 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
42486 if (ma_has_neon()) {
42487 ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode);
42488 } else
42489 #endif
42490 {
42491 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
42492 }
42493#endif
42494}
42495
42496
42497static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
42498{
42499 ma_int32* dst_s32 = (ma_int32*)dst;
42500 const ma_int32** src_s32 = (const ma_int32**)src;
42501
42502 ma_uint64 iFrame;
42503 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42504 ma_uint32 iChannel;
42505 for (iChannel = 0; iChannel < channels; iChannel += 1) {
42506 dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame];
42507 }
42508 }
42509}
42510
42511static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
42512{
42513 ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
42514}
42515
42516MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
42517{
42518#ifdef MA_USE_REFERENCE_CONVERSION_APIS
42519 ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
42520#else
42521 ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels);
42522#endif
42523}
42524
42525
42526static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
42527{
42528 ma_int32** dst_s32 = (ma_int32**)dst;
42529 const ma_int32* src_s32 = (const ma_int32*)src;
42530
42531 ma_uint64 iFrame;
42532 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42533 ma_uint32 iChannel;
42534 for (iChannel = 0; iChannel < channels; iChannel += 1) {
42535 dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel];
42536 }
42537 }
42538}
42539
42540static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
42541{
42542 ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
42543}
42544
42545MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
42546{
42547#ifdef MA_USE_REFERENCE_CONVERSION_APIS
42548 ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
42549#else
42550 ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels);
42551#endif
42552}
42553
42554
42555/* f32 */
42556static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42557{
42558 ma_uint64 i;
42559
42560 ma_uint8* dst_u8 = (ma_uint8*)dst;
42561 const float* src_f32 = (const float*)src;
42562
42563 float ditherMin = 0;
42564 float ditherMax = 0;
42565 if (ditherMode != ma_dither_mode_none) {
42566 ditherMin = 1.0f / -128;
42567 ditherMax = 1.0f / 127;
42568 }
42569
42570 for (i = 0; i < count; i += 1) {
42571 float x = src_f32[i];
42572 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
42573 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
42574 x = x + 1; /* -1..1 to 0..2 */
42575 x = x * 127.5f; /* 0..2 to 0..255 */
42576
42577 dst_u8[i] = (ma_uint8)x;
42578 }
42579}
42580
42581static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42582{
42583 ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
42584}
42585
42586#if defined(MA_SUPPORT_SSE2)
42587static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42588{
42589 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
42590}
42591#endif
42592#if defined(MA_SUPPORT_AVX2)
42593static MA_INLINE void ma_pcm_f32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42594{
42595 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
42596}
42597#endif
42598#if defined(MA_SUPPORT_NEON)
42599static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42600{
42601 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
42602}
42603#endif
42604
42605MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42606{
42607#ifdef MA_USE_REFERENCE_CONVERSION_APIS
42608 ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
42609#else
42610 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
42611 if (ma_has_avx2()) {
42612 ma_pcm_f32_to_u8__avx2(dst, src, count, ditherMode);
42613 } else
42614 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
42615 if (ma_has_sse2()) {
42616 ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode);
42617 } else
42618 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
42619 if (ma_has_neon()) {
42620 ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode);
42621 } else
42622 #endif
42623 {
42624 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
42625 }
42626#endif
42627}
42628
42629#ifdef MA_USE_REFERENCE_CONVERSION_APIS
42630static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42631{
42632 ma_uint64 i;
42633
42634 ma_int16* dst_s16 = (ma_int16*)dst;
42635 const float* src_f32 = (const float*)src;
42636
42637 float ditherMin = 0;
42638 float ditherMax = 0;
42639 if (ditherMode != ma_dither_mode_none) {
42640 ditherMin = 1.0f / -32768;
42641 ditherMax = 1.0f / 32767;
42642 }
42643
42644 for (i = 0; i < count; i += 1) {
42645 float x = src_f32[i];
42646 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
42647 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
42648
42649#if 0
42650 /* The accurate way. */
42651 x = x + 1; /* -1..1 to 0..2 */
42652 x = x * 32767.5f; /* 0..2 to 0..65535 */
42653 x = x - 32768.0f; /* 0...65535 to -32768..32767 */
42654#else
42655 /* The fast way. */
42656 x = x * 32767.0f; /* -1..1 to -32767..32767 */
42657#endif
42658
42659 dst_s16[i] = (ma_int16)x;
42660 }
42661}
42662#else
42663static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42664{
42665 ma_uint64 i;
42666 ma_uint64 i4;
42667 ma_uint64 count4;
42668
42669 ma_int16* dst_s16 = (ma_int16*)dst;
42670 const float* src_f32 = (const float*)src;
42671
42672 float ditherMin = 0;
42673 float ditherMax = 0;
42674 if (ditherMode != ma_dither_mode_none) {
42675 ditherMin = 1.0f / -32768;
42676 ditherMax = 1.0f / 32767;
42677 }
42678
42679 /* Unrolled. */
42680 i = 0;
42681 count4 = count >> 2;
42682 for (i4 = 0; i4 < count4; i4 += 1) {
42683 float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
42684 float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
42685 float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
42686 float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
42687
42688 float x0 = src_f32[i+0];
42689 float x1 = src_f32[i+1];
42690 float x2 = src_f32[i+2];
42691 float x3 = src_f32[i+3];
42692
42693 x0 = x0 + d0;
42694 x1 = x1 + d1;
42695 x2 = x2 + d2;
42696 x3 = x3 + d3;
42697
42698 x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
42699 x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
42700 x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
42701 x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
42702
42703 x0 = x0 * 32767.0f;
42704 x1 = x1 * 32767.0f;
42705 x2 = x2 * 32767.0f;
42706 x3 = x3 * 32767.0f;
42707
42708 dst_s16[i+0] = (ma_int16)x0;
42709 dst_s16[i+1] = (ma_int16)x1;
42710 dst_s16[i+2] = (ma_int16)x2;
42711 dst_s16[i+3] = (ma_int16)x3;
42712
42713 i += 4;
42714 }
42715
42716 /* Leftover. */
42717 for (; i < count; i += 1) {
42718 float x = src_f32[i];
42719 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
42720 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
42721 x = x * 32767.0f; /* -1..1 to -32767..32767 */
42722
42723 dst_s16[i] = (ma_int16)x;
42724 }
42725}
42726
42727#if defined(MA_SUPPORT_SSE2)
42728static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42729{
42730 ma_uint64 i;
42731 ma_uint64 i8;
42732 ma_uint64 count8;
42733 ma_int16* dst_s16;
42734 const float* src_f32;
42735 float ditherMin;
42736 float ditherMax;
42737
42738 /* Both the input and output buffers need to be aligned to 16 bytes. */
42739 if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
42740 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
42741 return;
42742 }
42743
42744 dst_s16 = (ma_int16*)dst;
42745 src_f32 = (const float*)src;
42746
42747 ditherMin = 0;
42748 ditherMax = 0;
42749 if (ditherMode != ma_dither_mode_none) {
42750 ditherMin = 1.0f / -32768;
42751 ditherMax = 1.0f / 32767;
42752 }
42753
42754 i = 0;
42755
42756 /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
42757 count8 = count >> 3;
42758 for (i8 = 0; i8 < count8; i8 += 1) {
42759 __m128 d0;
42760 __m128 d1;
42761 __m128 x0;
42762 __m128 x1;
42763
42764 if (ditherMode == ma_dither_mode_none) {
42765 d0 = _mm_set1_ps(0);
42766 d1 = _mm_set1_ps(0);
42767 } else if (ditherMode == ma_dither_mode_rectangle) {
42768 d0 = _mm_set_ps(
42769 ma_dither_f32_rectangle(ditherMin, ditherMax),
42770 ma_dither_f32_rectangle(ditherMin, ditherMax),
42771 ma_dither_f32_rectangle(ditherMin, ditherMax),
42772 ma_dither_f32_rectangle(ditherMin, ditherMax)
42773 );
42774 d1 = _mm_set_ps(
42775 ma_dither_f32_rectangle(ditherMin, ditherMax),
42776 ma_dither_f32_rectangle(ditherMin, ditherMax),
42777 ma_dither_f32_rectangle(ditherMin, ditherMax),
42778 ma_dither_f32_rectangle(ditherMin, ditherMax)
42779 );
42780 } else {
42781 d0 = _mm_set_ps(
42782 ma_dither_f32_triangle(ditherMin, ditherMax),
42783 ma_dither_f32_triangle(ditherMin, ditherMax),
42784 ma_dither_f32_triangle(ditherMin, ditherMax),
42785 ma_dither_f32_triangle(ditherMin, ditherMax)
42786 );
42787 d1 = _mm_set_ps(
42788 ma_dither_f32_triangle(ditherMin, ditherMax),
42789 ma_dither_f32_triangle(ditherMin, ditherMax),
42790 ma_dither_f32_triangle(ditherMin, ditherMax),
42791 ma_dither_f32_triangle(ditherMin, ditherMax)
42792 );
42793 }
42794
42795 x0 = *((__m128*)(src_f32 + i) + 0);
42796 x1 = *((__m128*)(src_f32 + i) + 1);
42797
42798 x0 = _mm_add_ps(x0, d0);
42799 x1 = _mm_add_ps(x1, d1);
42800
42801 x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f));
42802 x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f));
42803
42804 _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1)));
42805
42806 i += 8;
42807 }
42808
42809
42810 /* Leftover. */
42811 for (; i < count; i += 1) {
42812 float x = src_f32[i];
42813 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
42814 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
42815 x = x * 32767.0f; /* -1..1 to -32767..32767 */
42816
42817 dst_s16[i] = (ma_int16)x;
42818 }
42819}
42820#endif /* SSE2 */
42821
42822#if defined(MA_SUPPORT_AVX2)
42823static MA_INLINE void ma_pcm_f32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42824{
42825 ma_uint64 i;
42826 ma_uint64 i16;
42827 ma_uint64 count16;
42828 ma_int16* dst_s16;
42829 const float* src_f32;
42830 float ditherMin;
42831 float ditherMax;
42832
42833 /* Both the input and output buffers need to be aligned to 32 bytes. */
42834 if ((((ma_uintptr)dst & 31) != 0) || (((ma_uintptr)src & 31) != 0)) {
42835 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
42836 return;
42837 }
42838
42839 dst_s16 = (ma_int16*)dst;
42840 src_f32 = (const float*)src;
42841
42842 ditherMin = 0;
42843 ditherMax = 0;
42844 if (ditherMode != ma_dither_mode_none) {
42845 ditherMin = 1.0f / -32768;
42846 ditherMax = 1.0f / 32767;
42847 }
42848
42849 i = 0;
42850
42851 /* AVX2. AVX2 allows us to output 16 s16's at a time which means our loop is unrolled 16 times. */
42852 count16 = count >> 4;
42853 for (i16 = 0; i16 < count16; i16 += 1) {
42854 __m256 d0;
42855 __m256 d1;
42856 __m256 x0;
42857 __m256 x1;
42858 __m256i i0;
42859 __m256i i1;
42860 __m256i p0;
42861 __m256i p1;
42862 __m256i r;
42863
42864 if (ditherMode == ma_dither_mode_none) {
42865 d0 = _mm256_set1_ps(0);
42866 d1 = _mm256_set1_ps(0);
42867 } else if (ditherMode == ma_dither_mode_rectangle) {
42868 d0 = _mm256_set_ps(
42869 ma_dither_f32_rectangle(ditherMin, ditherMax),
42870 ma_dither_f32_rectangle(ditherMin, ditherMax),
42871 ma_dither_f32_rectangle(ditherMin, ditherMax),
42872 ma_dither_f32_rectangle(ditherMin, ditherMax),
42873 ma_dither_f32_rectangle(ditherMin, ditherMax),
42874 ma_dither_f32_rectangle(ditherMin, ditherMax),
42875 ma_dither_f32_rectangle(ditherMin, ditherMax),
42876 ma_dither_f32_rectangle(ditherMin, ditherMax)
42877 );
42878 d1 = _mm256_set_ps(
42879 ma_dither_f32_rectangle(ditherMin, ditherMax),
42880 ma_dither_f32_rectangle(ditherMin, ditherMax),
42881 ma_dither_f32_rectangle(ditherMin, ditherMax),
42882 ma_dither_f32_rectangle(ditherMin, ditherMax),
42883 ma_dither_f32_rectangle(ditherMin, ditherMax),
42884 ma_dither_f32_rectangle(ditherMin, ditherMax),
42885 ma_dither_f32_rectangle(ditherMin, ditherMax),
42886 ma_dither_f32_rectangle(ditherMin, ditherMax)
42887 );
42888 } else {
42889 d0 = _mm256_set_ps(
42890 ma_dither_f32_triangle(ditherMin, ditherMax),
42891 ma_dither_f32_triangle(ditherMin, ditherMax),
42892 ma_dither_f32_triangle(ditherMin, ditherMax),
42893 ma_dither_f32_triangle(ditherMin, ditherMax),
42894 ma_dither_f32_triangle(ditherMin, ditherMax),
42895 ma_dither_f32_triangle(ditherMin, ditherMax),
42896 ma_dither_f32_triangle(ditherMin, ditherMax),
42897 ma_dither_f32_triangle(ditherMin, ditherMax)
42898 );
42899 d1 = _mm256_set_ps(
42900 ma_dither_f32_triangle(ditherMin, ditherMax),
42901 ma_dither_f32_triangle(ditherMin, ditherMax),
42902 ma_dither_f32_triangle(ditherMin, ditherMax),
42903 ma_dither_f32_triangle(ditherMin, ditherMax),
42904 ma_dither_f32_triangle(ditherMin, ditherMax),
42905 ma_dither_f32_triangle(ditherMin, ditherMax),
42906 ma_dither_f32_triangle(ditherMin, ditherMax),
42907 ma_dither_f32_triangle(ditherMin, ditherMax)
42908 );
42909 }
42910
42911 x0 = *((__m256*)(src_f32 + i) + 0);
42912 x1 = *((__m256*)(src_f32 + i) + 1);
42913
42914 x0 = _mm256_add_ps(x0, d0);
42915 x1 = _mm256_add_ps(x1, d1);
42916
42917 x0 = _mm256_mul_ps(x0, _mm256_set1_ps(32767.0f));
42918 x1 = _mm256_mul_ps(x1, _mm256_set1_ps(32767.0f));
42919
42920 /* Computing the final result is a little more complicated for AVX2 than SSE2. */
42921 i0 = _mm256_cvttps_epi32(x0);
42922 i1 = _mm256_cvttps_epi32(x1);
42923 p0 = _mm256_permute2x128_si256(i0, i1, 0 | 32);
42924 p1 = _mm256_permute2x128_si256(i0, i1, 1 | 48);
42925 r = _mm256_packs_epi32(p0, p1);
42926
42927 _mm256_stream_si256(((__m256i*)(dst_s16 + i)), r);
42928
42929 i += 16;
42930 }
42931
42932
42933 /* Leftover. */
42934 for (; i < count; i += 1) {
42935 float x = src_f32[i];
42936 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
42937 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
42938 x = x * 32767.0f; /* -1..1 to -32767..32767 */
42939
42940 dst_s16[i] = (ma_int16)x;
42941 }
42942}
42943#endif /* AVX2 */
42944
42945#if defined(MA_SUPPORT_NEON)
42946static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
42947{
42948 ma_uint64 i;
42949 ma_uint64 i8;
42950 ma_uint64 count8;
42951 ma_int16* dst_s16;
42952 const float* src_f32;
42953 float ditherMin;
42954 float ditherMax;
42955
42956 if (!ma_has_neon()) {
42957 return ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
42958 }
42959
42960 /* Both the input and output buffers need to be aligned to 16 bytes. */
42961 if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
42962 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
42963 return;
42964 }
42965
42966 dst_s16 = (ma_int16*)dst;
42967 src_f32 = (const float*)src;
42968
42969 ditherMin = 0;
42970 ditherMax = 0;
42971 if (ditherMode != ma_dither_mode_none) {
42972 ditherMin = 1.0f / -32768;
42973 ditherMax = 1.0f / 32767;
42974 }
42975
42976 i = 0;
42977
42978 /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
42979 count8 = count >> 3;
42980 for (i8 = 0; i8 < count8; i8 += 1) {
42981 float32x4_t d0;
42982 float32x4_t d1;
42983 float32x4_t x0;
42984 float32x4_t x1;
42985 int32x4_t i0;
42986 int32x4_t i1;
42987
42988 if (ditherMode == ma_dither_mode_none) {
42989 d0 = vmovq_n_f32(0);
42990 d1 = vmovq_n_f32(0);
42991 } else if (ditherMode == ma_dither_mode_rectangle) {
42992 float d0v[4];
42993 d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
42994 d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
42995 d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
42996 d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
42997 d0 = vld1q_f32(d0v);
42998
42999 float d1v[4];
43000 d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
43001 d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
43002 d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
43003 d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
43004 d1 = vld1q_f32(d1v);
43005 } else {
43006 float d0v[4];
43007 d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
43008 d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
43009 d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
43010 d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
43011 d0 = vld1q_f32(d0v);
43012
43013 float d1v[4];
43014 d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
43015 d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
43016 d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
43017 d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
43018 d1 = vld1q_f32(d1v);
43019 }
43020
43021 x0 = *((float32x4_t*)(src_f32 + i) + 0);
43022 x1 = *((float32x4_t*)(src_f32 + i) + 1);
43023
43024 x0 = vaddq_f32(x0, d0);
43025 x1 = vaddq_f32(x1, d1);
43026
43027 x0 = vmulq_n_f32(x0, 32767.0f);
43028 x1 = vmulq_n_f32(x1, 32767.0f);
43029
43030 i0 = vcvtq_s32_f32(x0);
43031 i1 = vcvtq_s32_f32(x1);
43032 *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1));
43033
43034 i += 8;
43035 }
43036
43037
43038 /* Leftover. */
43039 for (; i < count; i += 1) {
43040 float x = src_f32[i];
43041 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
43042 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
43043 x = x * 32767.0f; /* -1..1 to -32767..32767 */
43044
43045 dst_s16[i] = (ma_int16)x;
43046 }
43047}
43048#endif /* Neon */
43049#endif /* MA_USE_REFERENCE_CONVERSION_APIS */
43050
43051MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43052{
43053#ifdef MA_USE_REFERENCE_CONVERSION_APIS
43054 ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode);
43055#else
43056 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
43057 if (ma_has_avx2()) {
43058 ma_pcm_f32_to_s16__avx2(dst, src, count, ditherMode);
43059 } else
43060 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
43061 if (ma_has_sse2()) {
43062 ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode);
43063 } else
43064 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
43065 if (ma_has_neon()) {
43066 ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode);
43067 } else
43068 #endif
43069 {
43070 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
43071 }
43072#endif
43073}
43074
43075
43076static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43077{
43078 ma_uint8* dst_s24 = (ma_uint8*)dst;
43079 const float* src_f32 = (const float*)src;
43080
43081 ma_uint64 i;
43082 for (i = 0; i < count; i += 1) {
43083 ma_int32 r;
43084 float x = src_f32[i];
43085 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
43086
43087#if 0
43088 /* The accurate way. */
43089 x = x + 1; /* -1..1 to 0..2 */
43090 x = x * 8388607.5f; /* 0..2 to 0..16777215 */
43091 x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */
43092#else
43093 /* The fast way. */
43094 x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */
43095#endif
43096
43097 r = (ma_int32)x;
43098 dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0);
43099 dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8);
43100 dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16);
43101 }
43102
43103 (void)ditherMode; /* No dithering for f32 -> s24. */
43104}
43105
43106static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43107{
43108 ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
43109}
43110
43111#if defined(MA_SUPPORT_SSE2)
43112static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43113{
43114 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
43115}
43116#endif
43117#if defined(MA_SUPPORT_AVX2)
43118static MA_INLINE void ma_pcm_f32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43119{
43120 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
43121}
43122#endif
43123#if defined(MA_SUPPORT_NEON)
43124static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43125{
43126 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
43127}
43128#endif
43129
43130MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43131{
43132#ifdef MA_USE_REFERENCE_CONVERSION_APIS
43133 ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
43134#else
43135 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
43136 if (ma_has_avx2()) {
43137 ma_pcm_f32_to_s24__avx2(dst, src, count, ditherMode);
43138 } else
43139 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
43140 if (ma_has_sse2()) {
43141 ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode);
43142 } else
43143 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
43144 if (ma_has_neon()) {
43145 ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode);
43146 } else
43147 #endif
43148 {
43149 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
43150 }
43151#endif
43152}
43153
43154
43155static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43156{
43157 ma_int32* dst_s32 = (ma_int32*)dst;
43158 const float* src_f32 = (const float*)src;
43159
43160 ma_uint32 i;
43161 for (i = 0; i < count; i += 1) {
43162 double x = src_f32[i];
43163 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
43164
43165#if 0
43166 /* The accurate way. */
43167 x = x + 1; /* -1..1 to 0..2 */
43168 x = x * 2147483647.5; /* 0..2 to 0..4294967295 */
43169 x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */
43170#else
43171 /* The fast way. */
43172 x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */
43173#endif
43174
43175 dst_s32[i] = (ma_int32)x;
43176 }
43177
43178 (void)ditherMode; /* No dithering for f32 -> s32. */
43179}
43180
43181static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43182{
43183 ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
43184}
43185
43186#if defined(MA_SUPPORT_SSE2)
43187static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43188{
43189 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
43190}
43191#endif
43192#if defined(MA_SUPPORT_AVX2)
43193static MA_INLINE void ma_pcm_f32_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43194{
43195 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
43196}
43197#endif
43198#if defined(MA_SUPPORT_NEON)
43199static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43200{
43201 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
43202}
43203#endif
43204
43205MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43206{
43207#ifdef MA_USE_REFERENCE_CONVERSION_APIS
43208 ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
43209#else
43210 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
43211 if (ma_has_avx2()) {
43212 ma_pcm_f32_to_s32__avx2(dst, src, count, ditherMode);
43213 } else
43214 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
43215 if (ma_has_sse2()) {
43216 ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode);
43217 } else
43218 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
43219 if (ma_has_neon()) {
43220 ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode);
43221 } else
43222 #endif
43223 {
43224 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
43225 }
43226#endif
43227}
43228
43229
43230MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43231{
43232 (void)ditherMode;
43233
43234 ma_copy_memory_64(dst, src, count * sizeof(float));
43235}
43236
43237
43238static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
43239{
43240 float* dst_f32 = (float*)dst;
43241 const float** src_f32 = (const float**)src;
43242
43243 ma_uint64 iFrame;
43244 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
43245 ma_uint32 iChannel;
43246 for (iChannel = 0; iChannel < channels; iChannel += 1) {
43247 dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame];
43248 }
43249 }
43250}
43251
43252static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
43253{
43254 ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
43255}
43256
43257MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
43258{
43259#ifdef MA_USE_REFERENCE_CONVERSION_APIS
43260 ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
43261#else
43262 ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels);
43263#endif
43264}
43265
43266
43267static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
43268{
43269 float** dst_f32 = (float**)dst;
43270 const float* src_f32 = (const float*)src;
43271
43272 ma_uint64 iFrame;
43273 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
43274 ma_uint32 iChannel;
43275 for (iChannel = 0; iChannel < channels; iChannel += 1) {
43276 dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel];
43277 }
43278 }
43279}
43280
43281static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
43282{
43283 ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
43284}
43285
43286MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
43287{
43288#ifdef MA_USE_REFERENCE_CONVERSION_APIS
43289 ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
43290#else
43291 ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels);
43292#endif
43293}
43294
43295
43296MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
43297{
43298 if (formatOut == formatIn) {
43299 ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut));
43300 return;
43301 }
43302
43303 switch (formatIn)
43304 {
43305 case ma_format_u8:
43306 {
43307 switch (formatOut)
43308 {
43309 case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return;
43310 case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return;
43311 case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return;
43312 case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return;
43313 default: break;
43314 }
43315 } break;
43316
43317 case ma_format_s16:
43318 {
43319 switch (formatOut)
43320 {
43321 case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return;
43322 case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return;
43323 case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return;
43324 case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return;
43325 default: break;
43326 }
43327 } break;
43328
43329 case ma_format_s24:
43330 {
43331 switch (formatOut)
43332 {
43333 case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return;
43334 case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return;
43335 case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return;
43336 case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return;
43337 default: break;
43338 }
43339 } break;
43340
43341 case ma_format_s32:
43342 {
43343 switch (formatOut)
43344 {
43345 case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
43346 case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
43347 case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
43348 case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return;
43349 default: break;
43350 }
43351 } break;
43352
43353 case ma_format_f32:
43354 {
43355 switch (formatOut)
43356 {
43357 case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
43358 case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
43359 case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
43360 case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return;
43361 default: break;
43362 }
43363 } break;
43364
43365 default: break;
43366 }
43367}
43368
43369MA_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)
43370{
43371 ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode);
43372}
43373
43374MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
43375{
43376 if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {
43377 return; /* Invalid args. */
43378 }
43379
43380 /* For efficiency we do this per format. */
43381 switch (format) {
43382 case ma_format_s16:
43383 {
43384 const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames;
43385 ma_uint64 iPCMFrame;
43386 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
43387 ma_uint32 iChannel;
43388 for (iChannel = 0; iChannel < channels; ++iChannel) {
43389 ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel];
43390 pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];
43391 }
43392 }
43393 } break;
43394
43395 case ma_format_f32:
43396 {
43397 const float* pSrcF32 = (const float*)pInterleavedPCMFrames;
43398 ma_uint64 iPCMFrame;
43399 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
43400 ma_uint32 iChannel;
43401 for (iChannel = 0; iChannel < channels; ++iChannel) {
43402 float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];
43403 pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];
43404 }
43405 }
43406 } break;
43407
43408 default:
43409 {
43410 ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
43411 ma_uint64 iPCMFrame;
43412 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
43413 ma_uint32 iChannel;
43414 for (iChannel = 0; iChannel < channels; ++iChannel) {
43415 void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
43416 const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
43417 memcpy(pDst, pSrc, sampleSizeInBytes);
43418 }
43419 }
43420 } break;
43421 }
43422}
43423
43424MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
43425{
43426 switch (format)
43427 {
43428 case ma_format_s16:
43429 {
43430 ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames;
43431 ma_uint64 iPCMFrame;
43432 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
43433 ma_uint32 iChannel;
43434 for (iChannel = 0; iChannel < channels; ++iChannel) {
43435 const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel];
43436 pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];
43437 }
43438 }
43439 } break;
43440
43441 case ma_format_f32:
43442 {
43443 float* pDstF32 = (float*)pInterleavedPCMFrames;
43444 ma_uint64 iPCMFrame;
43445 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
43446 ma_uint32 iChannel;
43447 for (iChannel = 0; iChannel < channels; ++iChannel) {
43448 const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];
43449 pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];
43450 }
43451 }
43452 } break;
43453
43454 default:
43455 {
43456 ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
43457 ma_uint64 iPCMFrame;
43458 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
43459 ma_uint32 iChannel;
43460 for (iChannel = 0; iChannel < channels; ++iChannel) {
43461 void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
43462 const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
43463 memcpy(pDst, pSrc, sampleSizeInBytes);
43464 }
43465 }
43466 } break;
43467 }
43468}
43469
43470
43471
43476#ifndef MA_BIQUAD_FIXED_POINT_SHIFT
43477#define MA_BIQUAD_FIXED_POINT_SHIFT 14
43478#endif
43479
43480static ma_int32 ma_biquad_float_to_fp(double x)
43481{
43482 return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT));
43483}
43484
43485MA_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)
43486{
43487 ma_biquad_config config;
43488
43489 MA_ZERO_OBJECT(&config);
43490 config.format = format;
43491 config.channels = channels;
43492 config.b0 = b0;
43493 config.b1 = b1;
43494 config.b2 = b2;
43495 config.a0 = a0;
43496 config.a1 = a1;
43497 config.a2 = a2;
43498
43499 return config;
43500}
43501
43502
43503typedef struct
43504{
43505 size_t sizeInBytes;
43506 size_t r1Offset;
43507 size_t r2Offset;
43508} ma_biquad_heap_layout;
43509
43510static ma_result ma_biquad_get_heap_layout(const ma_biquad_config* pConfig, ma_biquad_heap_layout* pHeapLayout)
43511{
43512 MA_ASSERT(pHeapLayout != NULL);
43513
43514 MA_ZERO_OBJECT(pHeapLayout);
43515
43516 if (pConfig == NULL) {
43517 return MA_INVALID_ARGS;
43518 }
43519
43520 if (pConfig->channels == 0) {
43521 return MA_INVALID_ARGS;
43522 }
43523
43524 pHeapLayout->sizeInBytes = 0;
43525
43526 /* R0 */
43527 pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;
43528 pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
43529
43530 /* R1 */
43531 pHeapLayout->r2Offset = pHeapLayout->sizeInBytes;
43532 pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
43533
43534 /* Make sure allocation size is aligned. */
43535 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
43536
43537 return MA_SUCCESS;
43538}
43539
43540MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes)
43541{
43542 ma_result result;
43543 ma_biquad_heap_layout heapLayout;
43544
43545 if (pHeapSizeInBytes == NULL) {
43546 return MA_INVALID_ARGS;
43547 }
43548
43549 *pHeapSizeInBytes = 0;
43550
43551 result = ma_biquad_get_heap_layout(pConfig, &heapLayout);
43552 if (result != MA_SUCCESS) {
43553 return result;
43554 }
43555
43556 *pHeapSizeInBytes = heapLayout.sizeInBytes;
43557
43558 return MA_SUCCESS;
43559}
43560
43562{
43563 ma_result result;
43564 ma_biquad_heap_layout heapLayout;
43565
43566 if (pBQ == NULL) {
43567 return MA_INVALID_ARGS;
43568 }
43569
43570 MA_ZERO_OBJECT(pBQ);
43571
43572 result = ma_biquad_get_heap_layout(pConfig, &heapLayout);
43573 if (result != MA_SUCCESS) {
43574 return result;
43575 }
43576
43577 pBQ->_pHeap = pHeap;
43578 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
43579
43580 pBQ->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);
43581 pBQ->pR2 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r2Offset);
43582
43583 return ma_biquad_reinit(pConfig, pBQ);
43584}
43585
43586MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ)
43587{
43588 ma_result result;
43589 size_t heapSizeInBytes;
43590 void* pHeap;
43591
43592 result = ma_biquad_get_heap_size(pConfig, &heapSizeInBytes);
43593 if (result != MA_SUCCESS) {
43594 return result;
43595 }
43596
43597 if (heapSizeInBytes > 0) {
43598 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
43599 if (pHeap == NULL) {
43600 return MA_OUT_OF_MEMORY;
43601 }
43602 } else {
43603 pHeap = NULL;
43604 }
43605
43606 result = ma_biquad_init_preallocated(pConfig, pHeap, pBQ);
43607 if (result != MA_SUCCESS) {
43608 ma_free(pHeap, pAllocationCallbacks);
43609 return result;
43610 }
43611
43612 pBQ->_ownsHeap = MA_TRUE;
43613 return MA_SUCCESS;
43614}
43615
43616MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks)
43617{
43618 if (pBQ == NULL) {
43619 return;
43620 }
43621
43622 if (pBQ->_ownsHeap) {
43623 ma_free(pBQ->_pHeap, pAllocationCallbacks);
43624 }
43625}
43626
43628{
43629 if (pBQ == NULL || pConfig == NULL) {
43630 return MA_INVALID_ARGS;
43631 }
43632
43633 if (pConfig->a0 == 0) {
43634 return MA_INVALID_ARGS; /* Division by zero. */
43635 }
43636
43637 /* Only supporting f32 and s16. */
43638 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
43639 return MA_INVALID_ARGS;
43640 }
43641
43642 /* The format cannot be changed after initialization. */
43643 if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) {
43644 return MA_INVALID_OPERATION;
43645 }
43646
43647 /* The channel count cannot be changed after initialization. */
43648 if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) {
43649 return MA_INVALID_OPERATION;
43650 }
43651
43652
43653 pBQ->format = pConfig->format;
43654 pBQ->channels = pConfig->channels;
43655
43656 /* Normalize. */
43657 if (pConfig->format == ma_format_f32) {
43658 pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0);
43659 pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0);
43660 pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0);
43661 pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0);
43662 pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0);
43663 } else {
43664 pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0);
43665 pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0);
43666 pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0);
43667 pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0);
43668 pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0);
43669 }
43670
43671 return MA_SUCCESS;
43672}
43673
43674static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX)
43675{
43676 ma_uint32 c;
43677 const ma_uint32 channels = pBQ->channels;
43678 const float b0 = pBQ->b0.f32;
43679 const float b1 = pBQ->b1.f32;
43680 const float b2 = pBQ->b2.f32;
43681 const float a1 = pBQ->a1.f32;
43682 const float a2 = pBQ->a2.f32;
43683
43684 MA_ASSUME(channels > 0);
43685 for (c = 0; c < channels; c += 1) {
43686 float r1 = pBQ->pR1[c].f32;
43687 float r2 = pBQ->pR2[c].f32;
43688 float x = pX[c];
43689 float y;
43690
43691 y = b0*x + r1;
43692 r1 = b1*x - a1*y + r2;
43693 r2 = b2*x - a2*y;
43694
43695 pY[c] = y;
43696 pBQ->pR1[c].f32 = r1;
43697 pBQ->pR2[c].f32 = r2;
43698 }
43699}
43700
43701static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX)
43702{
43703 ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
43704}
43705
43706static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
43707{
43708 ma_uint32 c;
43709 const ma_uint32 channels = pBQ->channels;
43710 const ma_int32 b0 = pBQ->b0.s32;
43711 const ma_int32 b1 = pBQ->b1.s32;
43712 const ma_int32 b2 = pBQ->b2.s32;
43713 const ma_int32 a1 = pBQ->a1.s32;
43714 const ma_int32 a2 = pBQ->a2.s32;
43715
43716 MA_ASSUME(channels > 0);
43717 for (c = 0; c < channels; c += 1) {
43718 ma_int32 r1 = pBQ->pR1[c].s32;
43719 ma_int32 r2 = pBQ->pR2[c].s32;
43720 ma_int32 x = pX[c];
43721 ma_int32 y;
43722
43723 y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
43724 r1 = (b1*x - a1*y + r2);
43725 r2 = (b2*x - a2*y);
43726
43727 pY[c] = (ma_int16)ma_clamp(y, -32768, 32767);
43728 pBQ->pR1[c].s32 = r1;
43729 pBQ->pR2[c].s32 = r2;
43730 }
43731}
43732
43733static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
43734{
43735 ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
43736}
43737
43738MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
43739{
43740 ma_uint32 n;
43741
43742 if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) {
43743 return MA_INVALID_ARGS;
43744 }
43745
43746 /* 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. */
43747
43748 if (pBQ->format == ma_format_f32) {
43749 /* */ float* pY = ( float*)pFramesOut;
43750 const float* pX = (const float*)pFramesIn;
43751
43752 for (n = 0; n < frameCount; n += 1) {
43753 ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
43754 pY += pBQ->channels;
43755 pX += pBQ->channels;
43756 }
43757 } else if (pBQ->format == ma_format_s16) {
43758 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
43759 const ma_int16* pX = (const ma_int16*)pFramesIn;
43760
43761 for (n = 0; n < frameCount; n += 1) {
43762 ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
43763 pY += pBQ->channels;
43764 pX += pBQ->channels;
43765 }
43766 } else {
43767 MA_ASSERT(MA_FALSE);
43768 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
43769 }
43770
43771 return MA_SUCCESS;
43772}
43773
43775{
43776 if (pBQ == NULL) {
43777 return 0;
43778 }
43779
43780 return 2;
43781}
43782
43783
43784
43789MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
43790{
43791 ma_lpf1_config config;
43792
43793 MA_ZERO_OBJECT(&config);
43794 config.format = format;
43795 config.channels = channels;
43796 config.sampleRate = sampleRate;
43797 config.cutoffFrequency = cutoffFrequency;
43798 config.q = 0.5;
43799
43800 return config;
43801}
43802
43803MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
43804{
43805 ma_lpf2_config config;
43806
43807 MA_ZERO_OBJECT(&config);
43808 config.format = format;
43809 config.channels = channels;
43810 config.sampleRate = sampleRate;
43811 config.cutoffFrequency = cutoffFrequency;
43812 config.q = q;
43813
43814 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
43815 if (config.q == 0) {
43816 config.q = 0.707107;
43817 }
43818
43819 return config;
43820}
43821
43822
43823typedef struct
43824{
43825 size_t sizeInBytes;
43826 size_t r1Offset;
43827} ma_lpf1_heap_layout;
43828
43829static ma_result ma_lpf1_get_heap_layout(const ma_lpf1_config* pConfig, ma_lpf1_heap_layout* pHeapLayout)
43830{
43831 MA_ASSERT(pHeapLayout != NULL);
43832
43833 MA_ZERO_OBJECT(pHeapLayout);
43834
43835 if (pConfig == NULL) {
43836 return MA_INVALID_ARGS;
43837 }
43838
43839 if (pConfig->channels == 0) {
43840 return MA_INVALID_ARGS;
43841 }
43842
43843 pHeapLayout->sizeInBytes = 0;
43844
43845 /* R1 */
43846 pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;
43847 pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
43848
43849 /* Make sure allocation size is aligned. */
43850 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
43851
43852 return MA_SUCCESS;
43853}
43854
43855MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes)
43856{
43857 ma_result result;
43858 ma_lpf1_heap_layout heapLayout;
43859
43860 if (pHeapSizeInBytes == NULL) {
43861 return MA_INVALID_ARGS;
43862 }
43863
43864 result = ma_lpf1_get_heap_layout(pConfig, &heapLayout);
43865 if (result != MA_SUCCESS) {
43866 return result;
43867 }
43868
43869 *pHeapSizeInBytes = heapLayout.sizeInBytes;
43870
43871 return MA_SUCCESS;
43872}
43873
43874MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF)
43875{
43876 ma_result result;
43877 ma_lpf1_heap_layout heapLayout;
43878
43879 if (pLPF == NULL) {
43880 return MA_INVALID_ARGS;
43881 }
43882
43883 MA_ZERO_OBJECT(pLPF);
43884
43885 result = ma_lpf1_get_heap_layout(pConfig, &heapLayout);
43886 if (result != MA_SUCCESS) {
43887 return result;
43888 }
43889
43890 pLPF->_pHeap = pHeap;
43891 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
43892
43893 pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);
43894
43895 return ma_lpf1_reinit(pConfig, pLPF);
43896}
43897
43898MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF)
43899{
43900 ma_result result;
43901 size_t heapSizeInBytes;
43902 void* pHeap;
43903
43904 result = ma_lpf1_get_heap_size(pConfig, &heapSizeInBytes);
43905 if (result != MA_SUCCESS) {
43906 return result;
43907 }
43908
43909 if (heapSizeInBytes > 0) {
43910 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
43911 if (pHeap == NULL) {
43912 return MA_OUT_OF_MEMORY;
43913 }
43914 } else {
43915 pHeap = NULL;
43916 }
43917
43918 result = ma_lpf1_init_preallocated(pConfig, pHeap, pLPF);
43919 if (result != MA_SUCCESS) {
43920 ma_free(pHeap, pAllocationCallbacks);
43921 return result;
43922 }
43923
43924 pLPF->_ownsHeap = MA_TRUE;
43925 return MA_SUCCESS;
43926}
43927
43928MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)
43929{
43930 if (pLPF == NULL) {
43931 return;
43932 }
43933
43934 if (pLPF->_ownsHeap) {
43935 ma_free(pLPF->_pHeap, pAllocationCallbacks);
43936 }
43937}
43938
43940{
43941 double a;
43942
43943 if (pLPF == NULL || pConfig == NULL) {
43944 return MA_INVALID_ARGS;
43945 }
43946
43947 /* Only supporting f32 and s16. */
43948 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
43949 return MA_INVALID_ARGS;
43950 }
43951
43952 /* The format cannot be changed after initialization. */
43953 if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {
43954 return MA_INVALID_OPERATION;
43955 }
43956
43957 /* The channel count cannot be changed after initialization. */
43958 if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {
43959 return MA_INVALID_OPERATION;
43960 }
43961
43962 pLPF->format = pConfig->format;
43963 pLPF->channels = pConfig->channels;
43964
43965 a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);
43966 if (pConfig->format == ma_format_f32) {
43967 pLPF->a.f32 = (float)a;
43968 } else {
43969 pLPF->a.s32 = ma_biquad_float_to_fp(a);
43970 }
43971
43972 return MA_SUCCESS;
43973}
43974
43975static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX)
43976{
43977 ma_uint32 c;
43978 const ma_uint32 channels = pLPF->channels;
43979 const float a = pLPF->a.f32;
43980 const float b = 1 - a;
43981
43982 MA_ASSUME(channels > 0);
43983 for (c = 0; c < channels; c += 1) {
43984 float r1 = pLPF->pR1[c].f32;
43985 float x = pX[c];
43986 float y;
43987
43988 y = b*x + a*r1;
43989
43990 pY[c] = y;
43991 pLPF->pR1[c].f32 = y;
43992 }
43993}
43994
43995static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX)
43996{
43997 ma_uint32 c;
43998 const ma_uint32 channels = pLPF->channels;
43999 const ma_int32 a = pLPF->a.s32;
44000 const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);
44001
44002 MA_ASSUME(channels > 0);
44003 for (c = 0; c < channels; c += 1) {
44004 ma_int32 r1 = pLPF->pR1[c].s32;
44005 ma_int32 x = pX[c];
44006 ma_int32 y;
44007
44008 y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
44009
44010 pY[c] = (ma_int16)y;
44011 pLPF->pR1[c].s32 = (ma_int32)y;
44012 }
44013}
44014
44015MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
44016{
44017 ma_uint32 n;
44018
44019 if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {
44020 return MA_INVALID_ARGS;
44021 }
44022
44023 /* 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. */
44024
44025 if (pLPF->format == ma_format_f32) {
44026 /* */ float* pY = ( float*)pFramesOut;
44027 const float* pX = (const float*)pFramesIn;
44028
44029 for (n = 0; n < frameCount; n += 1) {
44030 ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX);
44031 pY += pLPF->channels;
44032 pX += pLPF->channels;
44033 }
44034 } else if (pLPF->format == ma_format_s16) {
44035 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
44036 const ma_int16* pX = (const ma_int16*)pFramesIn;
44037
44038 for (n = 0; n < frameCount; n += 1) {
44039 ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX);
44040 pY += pLPF->channels;
44041 pX += pLPF->channels;
44042 }
44043 } else {
44044 MA_ASSERT(MA_FALSE);
44045 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
44046 }
44047
44048 return MA_SUCCESS;
44049}
44050
44052{
44053 if (pLPF == NULL) {
44054 return 0;
44055 }
44056
44057 return 1;
44058}
44059
44060
44061static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig)
44062{
44063 ma_biquad_config bqConfig;
44064 double q;
44065 double w;
44066 double s;
44067 double c;
44068 double a;
44069
44070 MA_ASSERT(pConfig != NULL);
44071
44072 q = pConfig->q;
44073 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
44074 s = ma_sind(w);
44075 c = ma_cosd(w);
44076 a = s / (2*q);
44077
44078 bqConfig.b0 = (1 - c) / 2;
44079 bqConfig.b1 = 1 - c;
44080 bqConfig.b2 = (1 - c) / 2;
44081 bqConfig.a0 = 1 + a;
44082 bqConfig.a1 = -2 * c;
44083 bqConfig.a2 = 1 - a;
44084
44085 bqConfig.format = pConfig->format;
44086 bqConfig.channels = pConfig->channels;
44087
44088 return bqConfig;
44089}
44090
44091MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes)
44092{
44093 ma_biquad_config bqConfig;
44094 bqConfig = ma_lpf2__get_biquad_config(pConfig);
44095
44096 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
44097}
44098
44099MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pLPF)
44100{
44101 ma_result result;
44102 ma_biquad_config bqConfig;
44103
44104 if (pLPF == NULL) {
44105 return MA_INVALID_ARGS;
44106 }
44107
44108 MA_ZERO_OBJECT(pLPF);
44109
44110 if (pConfig == NULL) {
44111 return MA_INVALID_ARGS;
44112 }
44113
44114 bqConfig = ma_lpf2__get_biquad_config(pConfig);
44115 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pLPF->bq);
44116 if (result != MA_SUCCESS) {
44117 return result;
44118 }
44119
44120 return MA_SUCCESS;
44121}
44122
44123MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF)
44124{
44125 ma_result result;
44126 size_t heapSizeInBytes;
44127 void* pHeap;
44128
44129 result = ma_lpf2_get_heap_size(pConfig, &heapSizeInBytes);
44130 if (result != MA_SUCCESS) {
44131 return result;
44132 }
44133
44134 if (heapSizeInBytes > 0) {
44135 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
44136 if (pHeap == NULL) {
44137 return MA_OUT_OF_MEMORY;
44138 }
44139 } else {
44140 pHeap = NULL;
44141 }
44142
44143 result = ma_lpf2_init_preallocated(pConfig, pHeap, pLPF);
44144 if (result != MA_SUCCESS) {
44145 ma_free(pHeap, pAllocationCallbacks);
44146 return result;
44147 }
44148
44149 pLPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
44150 return MA_SUCCESS;
44151}
44152
44153MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)
44154{
44155 if (pLPF == NULL) {
44156 return;
44157 }
44158
44159 ma_biquad_uninit(&pLPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
44160}
44161
44163{
44164 ma_result result;
44165 ma_biquad_config bqConfig;
44166
44167 if (pLPF == NULL || pConfig == NULL) {
44168 return MA_INVALID_ARGS;
44169 }
44170
44171 bqConfig = ma_lpf2__get_biquad_config(pConfig);
44172 result = ma_biquad_reinit(&bqConfig, &pLPF->bq);
44173 if (result != MA_SUCCESS) {
44174 return result;
44175 }
44176
44177 return MA_SUCCESS;
44178}
44179
44180static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
44181{
44182 ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn);
44183}
44184
44185static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn)
44186{
44187 ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn);
44188}
44189
44190MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
44191{
44192 if (pLPF == NULL) {
44193 return MA_INVALID_ARGS;
44194 }
44195
44196 return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount);
44197}
44198
44200{
44201 if (pLPF == NULL) {
44202 return 0;
44203 }
44204
44205 return ma_biquad_get_latency(&pLPF->bq);
44206}
44207
44208
44209MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
44210{
44211 ma_lpf_config config;
44212
44213 MA_ZERO_OBJECT(&config);
44214 config.format = format;
44215 config.channels = channels;
44216 config.sampleRate = sampleRate;
44217 config.cutoffFrequency = cutoffFrequency;
44218 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
44219
44220 return config;
44221}
44222
44223
44224typedef struct
44225{
44226 size_t sizeInBytes;
44227 size_t lpf1Offset;
44228 size_t lpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */
44229} ma_lpf_heap_layout;
44230
44231static void ma_lpf_calculate_sub_lpf_counts(ma_uint32 order, ma_uint32* pLPF1Count, ma_uint32* pLPF2Count)
44232{
44233 MA_ASSERT(pLPF1Count != NULL);
44234 MA_ASSERT(pLPF2Count != NULL);
44235
44236 *pLPF1Count = order % 2;
44237 *pLPF2Count = order / 2;
44238}
44239
44240static ma_result ma_lpf_get_heap_layout(const ma_lpf_config* pConfig, ma_lpf_heap_layout* pHeapLayout)
44241{
44242 ma_result result;
44243 ma_uint32 lpf1Count;
44244 ma_uint32 lpf2Count;
44245 ma_uint32 ilpf1;
44246 ma_uint32 ilpf2;
44247
44248 MA_ASSERT(pHeapLayout != NULL);
44249
44250 MA_ZERO_OBJECT(pHeapLayout);
44251
44252 if (pConfig == NULL) {
44253 return MA_INVALID_ARGS;
44254 }
44255
44256 if (pConfig->channels == 0) {
44257 return MA_INVALID_ARGS;
44258 }
44259
44260 if (pConfig->order > MA_MAX_FILTER_ORDER) {
44261 return MA_INVALID_ARGS;
44262 }
44263
44264 ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count);
44265
44266 pHeapLayout->sizeInBytes = 0;
44267
44268 /* LPF 1 */
44269 pHeapLayout->lpf1Offset = pHeapLayout->sizeInBytes;
44270 for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) {
44271 size_t lpf1HeapSizeInBytes;
44272 ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
44273
44274 result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes);
44275 if (result != MA_SUCCESS) {
44276 return result;
44277 }
44278
44279 pHeapLayout->sizeInBytes += sizeof(ma_lpf1) + lpf1HeapSizeInBytes;
44280 }
44281
44282 /* LPF 2*/
44283 pHeapLayout->lpf2Offset = pHeapLayout->sizeInBytes;
44284 for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) {
44285 size_t lpf2HeapSizeInBytes;
44286 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. */
44287
44288 result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes);
44289 if (result != MA_SUCCESS) {
44290 return result;
44291 }
44292
44293 pHeapLayout->sizeInBytes += sizeof(ma_lpf2) + lpf2HeapSizeInBytes;
44294 }
44295
44296 /* Make sure allocation size is aligned. */
44297 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
44298
44299 return MA_SUCCESS;
44300}
44301
44302static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF, ma_bool32 isNew)
44303{
44304 ma_result result;
44305 ma_uint32 lpf1Count;
44306 ma_uint32 lpf2Count;
44307 ma_uint32 ilpf1;
44308 ma_uint32 ilpf2;
44309 ma_lpf_heap_layout heapLayout; /* Only used if isNew is true. */
44310
44311 if (pLPF == NULL || pConfig == NULL) {
44312 return MA_INVALID_ARGS;
44313 }
44314
44315 /* Only supporting f32 and s16. */
44316 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
44317 return MA_INVALID_ARGS;
44318 }
44319
44320 /* The format cannot be changed after initialization. */
44321 if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {
44322 return MA_INVALID_OPERATION;
44323 }
44324
44325 /* The channel count cannot be changed after initialization. */
44326 if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {
44327 return MA_INVALID_OPERATION;
44328 }
44329
44330 if (pConfig->order > MA_MAX_FILTER_ORDER) {
44331 return MA_INVALID_ARGS;
44332 }
44333
44334 ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count);
44335
44336 /* The filter order can't change between reinits. */
44337 if (!isNew) {
44338 if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) {
44339 return MA_INVALID_OPERATION;
44340 }
44341 }
44342
44343 if (isNew) {
44344 result = ma_lpf_get_heap_layout(pConfig, &heapLayout);
44345 if (result != MA_SUCCESS) {
44346 return result;
44347 }
44348
44349 pLPF->_pHeap = pHeap;
44350 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
44351
44352 pLPF->pLPF1 = (ma_lpf1*)ma_offset_ptr(pHeap, heapLayout.lpf1Offset);
44353 pLPF->pLPF2 = (ma_lpf2*)ma_offset_ptr(pHeap, heapLayout.lpf2Offset);
44354 } else {
44355 MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */
44356 }
44357
44358 for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) {
44359 ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
44360
44361 if (isNew) {
44362 size_t lpf1HeapSizeInBytes;
44363
44364 result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes);
44365 if (result == MA_SUCCESS) {
44366 result = ma_lpf1_init_preallocated(&lpf1Config, ma_offset_ptr(pHeap, heapLayout.lpf1Offset + (sizeof(ma_lpf1) * lpf1Count) + (ilpf1 * lpf1HeapSizeInBytes)), &pLPF->pLPF1[ilpf1]);
44367 }
44368 } else {
44369 result = ma_lpf1_reinit(&lpf1Config, &pLPF->pLPF1[ilpf1]);
44370 }
44371
44372 if (result != MA_SUCCESS) {
44373 ma_uint32 jlpf1;
44374
44375 for (jlpf1 = 0; jlpf1 < ilpf1; jlpf1 += 1) {
44376 ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
44377 }
44378
44379 return result;
44380 }
44381 }
44382
44383 for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) {
44384 ma_lpf2_config lpf2Config;
44385 double q;
44386 double a;
44387
44388 /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */
44389 if (lpf1Count == 1) {
44390 a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */
44391 } else {
44392 a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */
44393 }
44394 q = 1 / (2*ma_cosd(a));
44395
44396 lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
44397
44398 if (isNew) {
44399 size_t lpf2HeapSizeInBytes;
44400
44401 result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes);
44402 if (result == MA_SUCCESS) {
44403 result = ma_lpf2_init_preallocated(&lpf2Config, ma_offset_ptr(pHeap, heapLayout.lpf2Offset + (sizeof(ma_lpf2) * lpf2Count) + (ilpf2 * lpf2HeapSizeInBytes)), &pLPF->pLPF2[ilpf2]);
44404 }
44405 } else {
44406 result = ma_lpf2_reinit(&lpf2Config, &pLPF->pLPF2[ilpf2]);
44407 }
44408
44409 if (result != MA_SUCCESS) {
44410 ma_uint32 jlpf1;
44411 ma_uint32 jlpf2;
44412
44413 for (jlpf1 = 0; jlpf1 < lpf1Count; jlpf1 += 1) {
44414 ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
44415 }
44416
44417 for (jlpf2 = 0; jlpf2 < ilpf2; jlpf2 += 1) {
44418 ma_lpf2_uninit(&pLPF->pLPF2[jlpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
44419 }
44420
44421 return result;
44422 }
44423 }
44424
44425 pLPF->lpf1Count = lpf1Count;
44426 pLPF->lpf2Count = lpf2Count;
44427 pLPF->format = pConfig->format;
44428 pLPF->channels = pConfig->channels;
44429 pLPF->sampleRate = pConfig->sampleRate;
44430
44431 return MA_SUCCESS;
44432}
44433
44434MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes)
44435{
44436 ma_result result;
44437 ma_lpf_heap_layout heapLayout;
44438
44439 if (pHeapSizeInBytes == NULL) {
44440 return MA_INVALID_ARGS;
44441 }
44442
44443 *pHeapSizeInBytes = 0;
44444
44445 result = ma_lpf_get_heap_layout(pConfig, &heapLayout);
44446 if (result != MA_SUCCESS) {
44447 return result;
44448 }
44449
44450 *pHeapSizeInBytes = heapLayout.sizeInBytes;
44451
44452 return result;
44453}
44454
44455MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF)
44456{
44457 if (pLPF == NULL) {
44458 return MA_INVALID_ARGS;
44459 }
44460
44461 MA_ZERO_OBJECT(pLPF);
44462
44463 return ma_lpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE);
44464}
44465
44466MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF)
44467{
44468 ma_result result;
44469 size_t heapSizeInBytes;
44470 void* pHeap;
44471
44472 result = ma_lpf_get_heap_size(pConfig, &heapSizeInBytes);
44473 if (result != MA_SUCCESS) {
44474 return result;
44475 }
44476
44477 if (heapSizeInBytes > 0) {
44478 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
44479 if (pHeap == NULL) {
44480 return MA_OUT_OF_MEMORY;
44481 }
44482 } else {
44483 pHeap = NULL;
44484 }
44485
44486 result = ma_lpf_init_preallocated(pConfig, pHeap, pLPF);
44487 if (result != MA_SUCCESS) {
44488 ma_free(pHeap, pAllocationCallbacks);
44489 return result;
44490 }
44491
44492 pLPF->_ownsHeap = MA_TRUE;
44493 return MA_SUCCESS;
44494}
44495
44496MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)
44497{
44498 ma_uint32 ilpf1;
44499 ma_uint32 ilpf2;
44500
44501 if (pLPF == NULL) {
44502 return;
44503 }
44504
44505 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
44506 ma_lpf1_uninit(&pLPF->pLPF1[ilpf1], pAllocationCallbacks);
44507 }
44508
44509 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
44510 ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks);
44511 }
44512
44513 if (pLPF->_ownsHeap) {
44514 ma_free(pLPF->_pHeap, pAllocationCallbacks);
44515 }
44516}
44517
44518MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF)
44519{
44520 return ma_lpf_reinit__internal(pConfig, NULL, pLPF, /*isNew*/MA_FALSE);
44521}
44522
44523static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX)
44524{
44525 ma_uint32 ilpf1;
44526 ma_uint32 ilpf2;
44527
44528 MA_ASSERT(pLPF->format == ma_format_f32);
44529
44530 MA_COPY_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));
44531
44532 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
44533 ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY);
44534 }
44535
44536 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
44537 ma_lpf2_process_pcm_frame_f32(&pLPF->pLPF2[ilpf2], pY, pY);
44538 }
44539}
44540
44541static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX)
44542{
44543 ma_uint32 ilpf1;
44544 ma_uint32 ilpf2;
44545
44546 MA_ASSERT(pLPF->format == ma_format_s16);
44547
44548 MA_COPY_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));
44549
44550 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
44551 ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY);
44552 }
44553
44554 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
44555 ma_lpf2_process_pcm_frame_s16(&pLPF->pLPF2[ilpf2], pY, pY);
44556 }
44557}
44558
44559MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
44560{
44561 ma_result result;
44562 ma_uint32 ilpf1;
44563 ma_uint32 ilpf2;
44564
44565 if (pLPF == NULL) {
44566 return MA_INVALID_ARGS;
44567 }
44568
44569 /* Faster path for in-place. */
44570 if (pFramesOut == pFramesIn) {
44571 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
44572 result = ma_lpf1_process_pcm_frames(&pLPF->pLPF1[ilpf1], pFramesOut, pFramesOut, frameCount);
44573 if (result != MA_SUCCESS) {
44574 return result;
44575 }
44576 }
44577
44578 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
44579 result = ma_lpf2_process_pcm_frames(&pLPF->pLPF2[ilpf2], pFramesOut, pFramesOut, frameCount);
44580 if (result != MA_SUCCESS) {
44581 return result;
44582 }
44583 }
44584 }
44585
44586 /* Slightly slower path for copying. */
44587 if (pFramesOut != pFramesIn) {
44588 ma_uint32 iFrame;
44589
44590 /* */ if (pLPF->format == ma_format_f32) {
44591 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
44592 const float* pFramesInF32 = (const float*)pFramesIn;
44593
44594 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
44595 ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32);
44596 pFramesOutF32 += pLPF->channels;
44597 pFramesInF32 += pLPF->channels;
44598 }
44599 } else if (pLPF->format == ma_format_s16) {
44600 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
44601 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
44602
44603 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
44604 ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16);
44605 pFramesOutS16 += pLPF->channels;
44606 pFramesInS16 += pLPF->channels;
44607 }
44608 } else {
44609 MA_ASSERT(MA_FALSE);
44610 return MA_INVALID_OPERATION; /* Should never hit this. */
44611 }
44612 }
44613
44614 return MA_SUCCESS;
44615}
44616
44618{
44619 if (pLPF == NULL) {
44620 return 0;
44621 }
44622
44623 return pLPF->lpf2Count*2 + pLPF->lpf1Count;
44624}
44625
44626
44627
44632MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
44633{
44634 ma_hpf1_config config;
44635
44636 MA_ZERO_OBJECT(&config);
44637 config.format = format;
44638 config.channels = channels;
44639 config.sampleRate = sampleRate;
44640 config.cutoffFrequency = cutoffFrequency;
44641
44642 return config;
44643}
44644
44645MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
44646{
44647 ma_hpf2_config config;
44648
44649 MA_ZERO_OBJECT(&config);
44650 config.format = format;
44651 config.channels = channels;
44652 config.sampleRate = sampleRate;
44653 config.cutoffFrequency = cutoffFrequency;
44654 config.q = q;
44655
44656 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
44657 if (config.q == 0) {
44658 config.q = 0.707107;
44659 }
44660
44661 return config;
44662}
44663
44664
44665typedef struct
44666{
44667 size_t sizeInBytes;
44668 size_t r1Offset;
44669} ma_hpf1_heap_layout;
44670
44671static ma_result ma_hpf1_get_heap_layout(const ma_hpf1_config* pConfig, ma_hpf1_heap_layout* pHeapLayout)
44672{
44673 MA_ASSERT(pHeapLayout != NULL);
44674
44675 MA_ZERO_OBJECT(pHeapLayout);
44676
44677 if (pConfig == NULL) {
44678 return MA_INVALID_ARGS;
44679 }
44680
44681 if (pConfig->channels == 0) {
44682 return MA_INVALID_ARGS;
44683 }
44684
44685 pHeapLayout->sizeInBytes = 0;
44686
44687 /* R1 */
44688 pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;
44689 pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
44690
44691 /* Make sure allocation size is aligned. */
44692 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
44693
44694 return MA_SUCCESS;
44695}
44696
44697MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes)
44698{
44699 ma_result result;
44700 ma_hpf1_heap_layout heapLayout;
44701
44702 if (pHeapSizeInBytes == NULL) {
44703 return MA_INVALID_ARGS;
44704 }
44705
44706 result = ma_hpf1_get_heap_layout(pConfig, &heapLayout);
44707 if (result != MA_SUCCESS) {
44708 return result;
44709 }
44710
44711 *pHeapSizeInBytes = heapLayout.sizeInBytes;
44712
44713 return MA_SUCCESS;
44714}
44715
44716MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF)
44717{
44718 ma_result result;
44719 ma_hpf1_heap_layout heapLayout;
44720
44721 if (pLPF == NULL) {
44722 return MA_INVALID_ARGS;
44723 }
44724
44725 MA_ZERO_OBJECT(pLPF);
44726
44727 result = ma_hpf1_get_heap_layout(pConfig, &heapLayout);
44728 if (result != MA_SUCCESS) {
44729 return result;
44730 }
44731
44732 pLPF->_pHeap = pHeap;
44733 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
44734
44735 pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);
44736
44737 return ma_hpf1_reinit(pConfig, pLPF);
44738}
44739
44740MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pLPF)
44741{
44742 ma_result result;
44743 size_t heapSizeInBytes;
44744 void* pHeap;
44745
44746 result = ma_hpf1_get_heap_size(pConfig, &heapSizeInBytes);
44747 if (result != MA_SUCCESS) {
44748 return result;
44749 }
44750
44751 if (heapSizeInBytes > 0) {
44752 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
44753 if (pHeap == NULL) {
44754 return MA_OUT_OF_MEMORY;
44755 }
44756 } else {
44757 pHeap = NULL;
44758 }
44759
44760 result = ma_hpf1_init_preallocated(pConfig, pHeap, pLPF);
44761 if (result != MA_SUCCESS) {
44762 ma_free(pHeap, pAllocationCallbacks);
44763 return result;
44764 }
44765
44766 pLPF->_ownsHeap = MA_TRUE;
44767 return MA_SUCCESS;
44768}
44769
44770MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)
44771{
44772 if (pHPF == NULL) {
44773 return;
44774 }
44775
44776 if (pHPF->_ownsHeap) {
44777 ma_free(pHPF->_pHeap, pAllocationCallbacks);
44778 }
44779}
44780
44782{
44783 double a;
44784
44785 if (pHPF == NULL || pConfig == NULL) {
44786 return MA_INVALID_ARGS;
44787 }
44788
44789 /* Only supporting f32 and s16. */
44790 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
44791 return MA_INVALID_ARGS;
44792 }
44793
44794 /* The format cannot be changed after initialization. */
44795 if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {
44796 return MA_INVALID_OPERATION;
44797 }
44798
44799 /* The channel count cannot be changed after initialization. */
44800 if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {
44801 return MA_INVALID_OPERATION;
44802 }
44803
44804 pHPF->format = pConfig->format;
44805 pHPF->channels = pConfig->channels;
44806
44807 a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);
44808 if (pConfig->format == ma_format_f32) {
44809 pHPF->a.f32 = (float)a;
44810 } else {
44811 pHPF->a.s32 = ma_biquad_float_to_fp(a);
44812 }
44813
44814 return MA_SUCCESS;
44815}
44816
44817static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX)
44818{
44819 ma_uint32 c;
44820 const ma_uint32 channels = pHPF->channels;
44821 const float a = 1 - pHPF->a.f32;
44822 const float b = 1 - a;
44823
44824 MA_ASSUME(channels > 0);
44825 for (c = 0; c < channels; c += 1) {
44826 float r1 = pHPF->pR1[c].f32;
44827 float x = pX[c];
44828 float y;
44829
44830 y = b*x - a*r1;
44831
44832 pY[c] = y;
44833 pHPF->pR1[c].f32 = y;
44834 }
44835}
44836
44837static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX)
44838{
44839 ma_uint32 c;
44840 const ma_uint32 channels = pHPF->channels;
44841 const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32);
44842 const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);
44843
44844 MA_ASSUME(channels > 0);
44845 for (c = 0; c < channels; c += 1) {
44846 ma_int32 r1 = pHPF->pR1[c].s32;
44847 ma_int32 x = pX[c];
44848 ma_int32 y;
44849
44850 y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
44851
44852 pY[c] = (ma_int16)y;
44853 pHPF->pR1[c].s32 = (ma_int32)y;
44854 }
44855}
44856
44857MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
44858{
44859 ma_uint32 n;
44860
44861 if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {
44862 return MA_INVALID_ARGS;
44863 }
44864
44865 /* 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. */
44866
44867 if (pHPF->format == ma_format_f32) {
44868 /* */ float* pY = ( float*)pFramesOut;
44869 const float* pX = (const float*)pFramesIn;
44870
44871 for (n = 0; n < frameCount; n += 1) {
44872 ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX);
44873 pY += pHPF->channels;
44874 pX += pHPF->channels;
44875 }
44876 } else if (pHPF->format == ma_format_s16) {
44877 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
44878 const ma_int16* pX = (const ma_int16*)pFramesIn;
44879
44880 for (n = 0; n < frameCount; n += 1) {
44881 ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX);
44882 pY += pHPF->channels;
44883 pX += pHPF->channels;
44884 }
44885 } else {
44886 MA_ASSERT(MA_FALSE);
44887 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
44888 }
44889
44890 return MA_SUCCESS;
44891}
44892
44894{
44895 if (pHPF == NULL) {
44896 return 0;
44897 }
44898
44899 return 1;
44900}
44901
44902
44903static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig)
44904{
44905 ma_biquad_config bqConfig;
44906 double q;
44907 double w;
44908 double s;
44909 double c;
44910 double a;
44911
44912 MA_ASSERT(pConfig != NULL);
44913
44914 q = pConfig->q;
44915 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
44916 s = ma_sind(w);
44917 c = ma_cosd(w);
44918 a = s / (2*q);
44919
44920 bqConfig.b0 = (1 + c) / 2;
44921 bqConfig.b1 = -(1 + c);
44922 bqConfig.b2 = (1 + c) / 2;
44923 bqConfig.a0 = 1 + a;
44924 bqConfig.a1 = -2 * c;
44925 bqConfig.a2 = 1 - a;
44926
44927 bqConfig.format = pConfig->format;
44928 bqConfig.channels = pConfig->channels;
44929
44930 return bqConfig;
44931}
44932
44933MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes)
44934{
44935 ma_biquad_config bqConfig;
44936 bqConfig = ma_hpf2__get_biquad_config(pConfig);
44937
44938 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
44939}
44940
44941MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF)
44942{
44943 ma_result result;
44944 ma_biquad_config bqConfig;
44945
44946 if (pHPF == NULL) {
44947 return MA_INVALID_ARGS;
44948 }
44949
44950 MA_ZERO_OBJECT(pHPF);
44951
44952 if (pConfig == NULL) {
44953 return MA_INVALID_ARGS;
44954 }
44955
44956 bqConfig = ma_hpf2__get_biquad_config(pConfig);
44957 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pHPF->bq);
44958 if (result != MA_SUCCESS) {
44959 return result;
44960 }
44961
44962 return MA_SUCCESS;
44963}
44964
44965MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF)
44966{
44967 ma_result result;
44968 size_t heapSizeInBytes;
44969 void* pHeap;
44970
44971 result = ma_hpf2_get_heap_size(pConfig, &heapSizeInBytes);
44972 if (result != MA_SUCCESS) {
44973 return result;
44974 }
44975
44976 if (heapSizeInBytes > 0) {
44977 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
44978 if (pHeap == NULL) {
44979 return MA_OUT_OF_MEMORY;
44980 }
44981 } else {
44982 pHeap = NULL;
44983 }
44984
44985 result = ma_hpf2_init_preallocated(pConfig, pHeap, pHPF);
44986 if (result != MA_SUCCESS) {
44987 ma_free(pHeap, pAllocationCallbacks);
44988 return result;
44989 }
44990
44991 pHPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
44992 return MA_SUCCESS;
44993}
44994
44995MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)
44996{
44997 if (pHPF == NULL) {
44998 return;
44999 }
45000
45001 ma_biquad_uninit(&pHPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
45002}
45003
45005{
45006 ma_result result;
45007 ma_biquad_config bqConfig;
45008
45009 if (pHPF == NULL || pConfig == NULL) {
45010 return MA_INVALID_ARGS;
45011 }
45012
45013 bqConfig = ma_hpf2__get_biquad_config(pConfig);
45014 result = ma_biquad_reinit(&bqConfig, &pHPF->bq);
45015 if (result != MA_SUCCESS) {
45016 return result;
45017 }
45018
45019 return MA_SUCCESS;
45020}
45021
45022static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
45023{
45024 ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn);
45025}
45026
45027static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn)
45028{
45029 ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn);
45030}
45031
45032MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
45033{
45034 if (pHPF == NULL) {
45035 return MA_INVALID_ARGS;
45036 }
45037
45038 return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount);
45039}
45040
45042{
45043 if (pHPF == NULL) {
45044 return 0;
45045 }
45046
45047 return ma_biquad_get_latency(&pHPF->bq);
45048}
45049
45050
45051MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
45052{
45053 ma_hpf_config config;
45054
45055 MA_ZERO_OBJECT(&config);
45056 config.format = format;
45057 config.channels = channels;
45058 config.sampleRate = sampleRate;
45059 config.cutoffFrequency = cutoffFrequency;
45060 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
45061
45062 return config;
45063}
45064
45065
45066typedef struct
45067{
45068 size_t sizeInBytes;
45069 size_t hpf1Offset;
45070 size_t hpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */
45071} ma_hpf_heap_layout;
45072
45073static void ma_hpf_calculate_sub_hpf_counts(ma_uint32 order, ma_uint32* pHPF1Count, ma_uint32* pHPF2Count)
45074{
45075 MA_ASSERT(pHPF1Count != NULL);
45076 MA_ASSERT(pHPF2Count != NULL);
45077
45078 *pHPF1Count = order % 2;
45079 *pHPF2Count = order / 2;
45080}
45081
45082static ma_result ma_hpf_get_heap_layout(const ma_hpf_config* pConfig, ma_hpf_heap_layout* pHeapLayout)
45083{
45084 ma_result result;
45085 ma_uint32 hpf1Count;
45086 ma_uint32 hpf2Count;
45087 ma_uint32 ihpf1;
45088 ma_uint32 ihpf2;
45089
45090 MA_ASSERT(pHeapLayout != NULL);
45091
45092 MA_ZERO_OBJECT(pHeapLayout);
45093
45094 if (pConfig == NULL) {
45095 return MA_INVALID_ARGS;
45096 }
45097
45098 if (pConfig->channels == 0) {
45099 return MA_INVALID_ARGS;
45100 }
45101
45102 if (pConfig->order > MA_MAX_FILTER_ORDER) {
45103 return MA_INVALID_ARGS;
45104 }
45105
45106 ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count);
45107
45108 pHeapLayout->sizeInBytes = 0;
45109
45110 /* HPF 1 */
45111 pHeapLayout->hpf1Offset = pHeapLayout->sizeInBytes;
45112 for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) {
45113 size_t hpf1HeapSizeInBytes;
45114 ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
45115
45116 result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes);
45117 if (result != MA_SUCCESS) {
45118 return result;
45119 }
45120
45121 pHeapLayout->sizeInBytes += sizeof(ma_hpf1) + hpf1HeapSizeInBytes;
45122 }
45123
45124 /* HPF 2*/
45125 pHeapLayout->hpf2Offset = pHeapLayout->sizeInBytes;
45126 for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) {
45127 size_t hpf2HeapSizeInBytes;
45128 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. */
45129
45130 result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes);
45131 if (result != MA_SUCCESS) {
45132 return result;
45133 }
45134
45135 pHeapLayout->sizeInBytes += sizeof(ma_hpf2) + hpf2HeapSizeInBytes;
45136 }
45137
45138 /* Make sure allocation size is aligned. */
45139 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
45140
45141 return MA_SUCCESS;
45142}
45143
45144static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pHPF, ma_bool32 isNew)
45145{
45146 ma_result result;
45147 ma_uint32 hpf1Count;
45148 ma_uint32 hpf2Count;
45149 ma_uint32 ihpf1;
45150 ma_uint32 ihpf2;
45151 ma_hpf_heap_layout heapLayout; /* Only used if isNew is true. */
45152
45153 if (pHPF == NULL || pConfig == NULL) {
45154 return MA_INVALID_ARGS;
45155 }
45156
45157 /* Only supporting f32 and s16. */
45158 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
45159 return MA_INVALID_ARGS;
45160 }
45161
45162 /* The format cannot be changed after initialization. */
45163 if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {
45164 return MA_INVALID_OPERATION;
45165 }
45166
45167 /* The channel count cannot be changed after initialization. */
45168 if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {
45169 return MA_INVALID_OPERATION;
45170 }
45171
45172 if (pConfig->order > MA_MAX_FILTER_ORDER) {
45173 return MA_INVALID_ARGS;
45174 }
45175
45176 ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count);
45177
45178 /* The filter order can't change between reinits. */
45179 if (!isNew) {
45180 if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) {
45181 return MA_INVALID_OPERATION;
45182 }
45183 }
45184
45185 if (isNew) {
45186 result = ma_hpf_get_heap_layout(pConfig, &heapLayout);
45187 if (result != MA_SUCCESS) {
45188 return result;
45189 }
45190
45191 pHPF->_pHeap = pHeap;
45192 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
45193
45194 pHPF->pHPF1 = (ma_hpf1*)ma_offset_ptr(pHeap, heapLayout.hpf1Offset);
45195 pHPF->pHPF2 = (ma_hpf2*)ma_offset_ptr(pHeap, heapLayout.hpf2Offset);
45196 } else {
45197 MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */
45198 }
45199
45200 for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) {
45201 ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
45202
45203 if (isNew) {
45204 size_t hpf1HeapSizeInBytes;
45205
45206 result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes);
45207 if (result == MA_SUCCESS) {
45208 result = ma_hpf1_init_preallocated(&hpf1Config, ma_offset_ptr(pHeap, heapLayout.hpf1Offset + (sizeof(ma_hpf1) * hpf1Count) + (ihpf1 * hpf1HeapSizeInBytes)), &pHPF->pHPF1[ihpf1]);
45209 }
45210 } else {
45211 result = ma_hpf1_reinit(&hpf1Config, &pHPF->pHPF1[ihpf1]);
45212 }
45213
45214 if (result != MA_SUCCESS) {
45215 ma_uint32 jhpf1;
45216
45217 for (jhpf1 = 0; jhpf1 < ihpf1; jhpf1 += 1) {
45218 ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
45219 }
45220
45221 return result;
45222 }
45223 }
45224
45225 for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) {
45226 ma_hpf2_config hpf2Config;
45227 double q;
45228 double a;
45229
45230 /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */
45231 if (hpf1Count == 1) {
45232 a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */
45233 } else {
45234 a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */
45235 }
45236 q = 1 / (2*ma_cosd(a));
45237
45238 hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
45239
45240 if (isNew) {
45241 size_t hpf2HeapSizeInBytes;
45242
45243 result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes);
45244 if (result == MA_SUCCESS) {
45245 result = ma_hpf2_init_preallocated(&hpf2Config, ma_offset_ptr(pHeap, heapLayout.hpf2Offset + (sizeof(ma_hpf2) * hpf2Count) + (ihpf2 * hpf2HeapSizeInBytes)), &pHPF->pHPF2[ihpf2]);
45246 }
45247 } else {
45248 result = ma_hpf2_reinit(&hpf2Config, &pHPF->pHPF2[ihpf2]);
45249 }
45250
45251 if (result != MA_SUCCESS) {
45252 ma_uint32 jhpf1;
45253 ma_uint32 jhpf2;
45254
45255 for (jhpf1 = 0; jhpf1 < hpf1Count; jhpf1 += 1) {
45256 ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
45257 }
45258
45259 for (jhpf2 = 0; jhpf2 < ihpf2; jhpf2 += 1) {
45260 ma_hpf2_uninit(&pHPF->pHPF2[jhpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
45261 }
45262
45263 return result;
45264 }
45265 }
45266
45267 pHPF->hpf1Count = hpf1Count;
45268 pHPF->hpf2Count = hpf2Count;
45269 pHPF->format = pConfig->format;
45270 pHPF->channels = pConfig->channels;
45271 pHPF->sampleRate = pConfig->sampleRate;
45272
45273 return MA_SUCCESS;
45274}
45275
45276MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes)
45277{
45278 ma_result result;
45279 ma_hpf_heap_layout heapLayout;
45280
45281 if (pHeapSizeInBytes == NULL) {
45282 return MA_INVALID_ARGS;
45283 }
45284
45285 *pHeapSizeInBytes = 0;
45286
45287 result = ma_hpf_get_heap_layout(pConfig, &heapLayout);
45288 if (result != MA_SUCCESS) {
45289 return result;
45290 }
45291
45292 *pHeapSizeInBytes = heapLayout.sizeInBytes;
45293
45294 return result;
45295}
45296
45297MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF)
45298{
45299 if (pLPF == NULL) {
45300 return MA_INVALID_ARGS;
45301 }
45302
45303 MA_ZERO_OBJECT(pLPF);
45304
45305 return ma_hpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE);
45306}
45307
45308MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF)
45309{
45310 ma_result result;
45311 size_t heapSizeInBytes;
45312 void* pHeap;
45313
45314 result = ma_hpf_get_heap_size(pConfig, &heapSizeInBytes);
45315 if (result != MA_SUCCESS) {
45316 return result;
45317 }
45318
45319 if (heapSizeInBytes > 0) {
45320 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
45321 if (pHeap == NULL) {
45322 return MA_OUT_OF_MEMORY;
45323 }
45324 } else {
45325 pHeap = NULL;
45326 }
45327
45328 result = ma_hpf_init_preallocated(pConfig, pHeap, pHPF);
45329 if (result != MA_SUCCESS) {
45330 ma_free(pHeap, pAllocationCallbacks);
45331 return result;
45332 }
45333
45334 pHPF->_ownsHeap = MA_TRUE;
45335 return MA_SUCCESS;
45336}
45337
45338MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)
45339{
45340 ma_uint32 ihpf1;
45341 ma_uint32 ihpf2;
45342
45343 if (pHPF == NULL) {
45344 return;
45345 }
45346
45347 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
45348 ma_hpf1_uninit(&pHPF->pHPF1[ihpf1], pAllocationCallbacks);
45349 }
45350
45351 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
45352 ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks);
45353 }
45354
45355 if (pHPF->_ownsHeap) {
45356 ma_free(pHPF->_pHeap, pAllocationCallbacks);
45357 }
45358}
45359
45360MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF)
45361{
45362 return ma_hpf_reinit__internal(pConfig, NULL, pHPF, /*isNew*/MA_FALSE);
45363}
45364
45365MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
45366{
45367 ma_result result;
45368 ma_uint32 ihpf1;
45369 ma_uint32 ihpf2;
45370
45371 if (pHPF == NULL) {
45372 return MA_INVALID_ARGS;
45373 }
45374
45375 /* Faster path for in-place. */
45376 if (pFramesOut == pFramesIn) {
45377 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
45378 result = ma_hpf1_process_pcm_frames(&pHPF->pHPF1[ihpf1], pFramesOut, pFramesOut, frameCount);
45379 if (result != MA_SUCCESS) {
45380 return result;
45381 }
45382 }
45383
45384 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
45385 result = ma_hpf2_process_pcm_frames(&pHPF->pHPF2[ihpf2], pFramesOut, pFramesOut, frameCount);
45386 if (result != MA_SUCCESS) {
45387 return result;
45388 }
45389 }
45390 }
45391
45392 /* Slightly slower path for copying. */
45393 if (pFramesOut != pFramesIn) {
45394 ma_uint32 iFrame;
45395
45396 /* */ if (pHPF->format == ma_format_f32) {
45397 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
45398 const float* pFramesInF32 = (const float*)pFramesIn;
45399
45400 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
45401 MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));
45402
45403 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
45404 ma_hpf1_process_pcm_frame_f32(&pHPF->pHPF1[ihpf1], pFramesOutF32, pFramesOutF32);
45405 }
45406
45407 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
45408 ma_hpf2_process_pcm_frame_f32(&pHPF->pHPF2[ihpf2], pFramesOutF32, pFramesOutF32);
45409 }
45410
45411 pFramesOutF32 += pHPF->channels;
45412 pFramesInF32 += pHPF->channels;
45413 }
45414 } else if (pHPF->format == ma_format_s16) {
45415 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
45416 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
45417
45418 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
45419 MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));
45420
45421 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
45422 ma_hpf1_process_pcm_frame_s16(&pHPF->pHPF1[ihpf1], pFramesOutS16, pFramesOutS16);
45423 }
45424
45425 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
45426 ma_hpf2_process_pcm_frame_s16(&pHPF->pHPF2[ihpf2], pFramesOutS16, pFramesOutS16);
45427 }
45428
45429 pFramesOutS16 += pHPF->channels;
45430 pFramesInS16 += pHPF->channels;
45431 }
45432 } else {
45433 MA_ASSERT(MA_FALSE);
45434 return MA_INVALID_OPERATION; /* Should never hit this. */
45435 }
45436 }
45437
45438 return MA_SUCCESS;
45439}
45440
45442{
45443 if (pHPF == NULL) {
45444 return 0;
45445 }
45446
45447 return pHPF->hpf2Count*2 + pHPF->hpf1Count;
45448}
45449
45450
45451
45456MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
45457{
45458 ma_bpf2_config config;
45459
45460 MA_ZERO_OBJECT(&config);
45461 config.format = format;
45462 config.channels = channels;
45463 config.sampleRate = sampleRate;
45464 config.cutoffFrequency = cutoffFrequency;
45465 config.q = q;
45466
45467 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
45468 if (config.q == 0) {
45469 config.q = 0.707107;
45470 }
45471
45472 return config;
45473}
45474
45475
45476static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig)
45477{
45478 ma_biquad_config bqConfig;
45479 double q;
45480 double w;
45481 double s;
45482 double c;
45483 double a;
45484
45485 MA_ASSERT(pConfig != NULL);
45486
45487 q = pConfig->q;
45488 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
45489 s = ma_sind(w);
45490 c = ma_cosd(w);
45491 a = s / (2*q);
45492
45493 bqConfig.b0 = q * a;
45494 bqConfig.b1 = 0;
45495 bqConfig.b2 = -q * a;
45496 bqConfig.a0 = 1 + a;
45497 bqConfig.a1 = -2 * c;
45498 bqConfig.a2 = 1 - a;
45499
45500 bqConfig.format = pConfig->format;
45501 bqConfig.channels = pConfig->channels;
45502
45503 return bqConfig;
45504}
45505
45506MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes)
45507{
45508 ma_biquad_config bqConfig;
45509 bqConfig = ma_bpf2__get_biquad_config(pConfig);
45510
45511 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
45512}
45513
45514MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF)
45515{
45516 ma_result result;
45517 ma_biquad_config bqConfig;
45518
45519 if (pBPF == NULL) {
45520 return MA_INVALID_ARGS;
45521 }
45522
45523 MA_ZERO_OBJECT(pBPF);
45524
45525 if (pConfig == NULL) {
45526 return MA_INVALID_ARGS;
45527 }
45528
45529 bqConfig = ma_bpf2__get_biquad_config(pConfig);
45530 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pBPF->bq);
45531 if (result != MA_SUCCESS) {
45532 return result;
45533 }
45534
45535 return MA_SUCCESS;
45536}
45537
45538MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF)
45539{
45540 ma_result result;
45541 size_t heapSizeInBytes;
45542 void* pHeap;
45543
45544 result = ma_bpf2_get_heap_size(pConfig, &heapSizeInBytes);
45545 if (result != MA_SUCCESS) {
45546 return result;
45547 }
45548
45549 if (heapSizeInBytes > 0) {
45550 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
45551 if (pHeap == NULL) {
45552 return MA_OUT_OF_MEMORY;
45553 }
45554 } else {
45555 pHeap = NULL;
45556 }
45557
45558 result = ma_bpf2_init_preallocated(pConfig, pHeap, pBPF);
45559 if (result != MA_SUCCESS) {
45560 ma_free(pHeap, pAllocationCallbacks);
45561 return result;
45562 }
45563
45564 pBPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
45565 return MA_SUCCESS;
45566}
45567
45568MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks)
45569{
45570 if (pBPF == NULL) {
45571 return;
45572 }
45573
45574 ma_biquad_uninit(&pBPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
45575}
45576
45578{
45579 ma_result result;
45580 ma_biquad_config bqConfig;
45581
45582 if (pBPF == NULL || pConfig == NULL) {
45583 return MA_INVALID_ARGS;
45584 }
45585
45586 bqConfig = ma_bpf2__get_biquad_config(pConfig);
45587 result = ma_biquad_reinit(&bqConfig, &pBPF->bq);
45588 if (result != MA_SUCCESS) {
45589 return result;
45590 }
45591
45592 return MA_SUCCESS;
45593}
45594
45595static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
45596{
45597 ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn);
45598}
45599
45600static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn)
45601{
45602 ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn);
45603}
45604
45605MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
45606{
45607 if (pBPF == NULL) {
45608 return MA_INVALID_ARGS;
45609 }
45610
45611 return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount);
45612}
45613
45615{
45616 if (pBPF == NULL) {
45617 return 0;
45618 }
45619
45620 return ma_biquad_get_latency(&pBPF->bq);
45621}
45622
45623
45624MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
45625{
45626 ma_bpf_config config;
45627
45628 MA_ZERO_OBJECT(&config);
45629 config.format = format;
45630 config.channels = channels;
45631 config.sampleRate = sampleRate;
45632 config.cutoffFrequency = cutoffFrequency;
45633 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
45634
45635 return config;
45636}
45637
45638
45639typedef struct
45640{
45641 size_t sizeInBytes;
45642 size_t bpf2Offset;
45643} ma_bpf_heap_layout;
45644
45645static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_heap_layout* pHeapLayout)
45646{
45647 ma_result result;
45648 ma_uint32 bpf2Count;
45649 ma_uint32 ibpf2;
45650
45651 MA_ASSERT(pHeapLayout != NULL);
45652
45653 MA_ZERO_OBJECT(pHeapLayout);
45654
45655 if (pConfig == NULL) {
45656 return MA_INVALID_ARGS;
45657 }
45658
45659 if (pConfig->order > MA_MAX_FILTER_ORDER) {
45660 return MA_INVALID_ARGS;
45661 }
45662
45663 /* We must have an even number of order. */
45664 if ((pConfig->order & 0x1) != 0) {
45665 return MA_INVALID_ARGS;
45666 }
45667
45668 bpf2Count = pConfig->channels / 2;
45669
45670 pHeapLayout->sizeInBytes = 0;
45671
45672 /* BPF 2 */
45673 pHeapLayout->bpf2Offset = pHeapLayout->sizeInBytes;
45674 for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) {
45675 size_t bpf2HeapSizeInBytes;
45676 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. */
45677
45678 result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes);
45679 if (result != MA_SUCCESS) {
45680 return result;
45681 }
45682
45683 pHeapLayout->sizeInBytes += sizeof(ma_bpf2) + bpf2HeapSizeInBytes;
45684 }
45685
45686 /* Make sure allocation size is aligned. */
45687 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
45688
45689 return MA_SUCCESS;
45690}
45691
45692static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF, ma_bool32 isNew)
45693{
45694 ma_result result;
45695 ma_uint32 bpf2Count;
45696 ma_uint32 ibpf2;
45697 ma_bpf_heap_layout heapLayout; /* Only used if isNew is true. */
45698
45699 if (pBPF == NULL || pConfig == NULL) {
45700 return MA_INVALID_ARGS;
45701 }
45702
45703 /* Only supporting f32 and s16. */
45704 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
45705 return MA_INVALID_ARGS;
45706 }
45707
45708 /* The format cannot be changed after initialization. */
45709 if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) {
45710 return MA_INVALID_OPERATION;
45711 }
45712
45713 /* The channel count cannot be changed after initialization. */
45714 if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) {
45715 return MA_INVALID_OPERATION;
45716 }
45717
45718 if (pConfig->order > MA_MAX_FILTER_ORDER) {
45719 return MA_INVALID_ARGS;
45720 }
45721
45722 /* We must have an even number of order. */
45723 if ((pConfig->order & 0x1) != 0) {
45724 return MA_INVALID_ARGS;
45725 }
45726
45727 bpf2Count = pConfig->order / 2;
45728
45729 /* The filter order can't change between reinits. */
45730 if (!isNew) {
45731 if (pBPF->bpf2Count != bpf2Count) {
45732 return MA_INVALID_OPERATION;
45733 }
45734 }
45735
45736 if (isNew) {
45737 result = ma_bpf_get_heap_layout(pConfig, &heapLayout);
45738 if (result != MA_SUCCESS) {
45739 return result;
45740 }
45741
45742 pBPF->_pHeap = pHeap;
45743 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
45744
45745 pBPF->pBPF2 = (ma_bpf2*)ma_offset_ptr(pHeap, heapLayout.bpf2Offset);
45746 } else {
45747 MA_ZERO_OBJECT(&heapLayout);
45748 }
45749
45750 for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) {
45751 ma_bpf2_config bpf2Config;
45752 double q;
45753
45754 /* TODO: Calculate Q to make this a proper Butterworth filter. */
45755 q = 0.707107;
45756
45757 bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
45758
45759 if (isNew) {
45760 size_t bpf2HeapSizeInBytes;
45761
45762 result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes);
45763 if (result == MA_SUCCESS) {
45764 result = ma_bpf2_init_preallocated(&bpf2Config, ma_offset_ptr(pHeap, heapLayout.bpf2Offset + (sizeof(ma_bpf2) * bpf2Count) + (ibpf2 * bpf2HeapSizeInBytes)), &pBPF->pBPF2[ibpf2]);
45765 }
45766 } else {
45767 result = ma_bpf2_reinit(&bpf2Config, &pBPF->pBPF2[ibpf2]);
45768 }
45769
45770 if (result != MA_SUCCESS) {
45771 return result;
45772 }
45773 }
45774
45775 pBPF->bpf2Count = bpf2Count;
45776 pBPF->format = pConfig->format;
45777 pBPF->channels = pConfig->channels;
45778
45779 return MA_SUCCESS;
45780}
45781
45782
45783MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes)
45784{
45785 ma_result result;
45786 ma_bpf_heap_layout heapLayout;
45787
45788 if (pHeapSizeInBytes == NULL) {
45789 return MA_INVALID_ARGS;
45790 }
45791
45792 *pHeapSizeInBytes = 0;
45793
45794 result = ma_bpf_get_heap_layout(pConfig, &heapLayout);
45795 if (result != MA_SUCCESS) {
45796 return result;
45797 }
45798
45799 *pHeapSizeInBytes = heapLayout.sizeInBytes;
45800
45801 return MA_SUCCESS;
45802}
45803
45804MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF)
45805{
45806 if (pBPF == NULL) {
45807 return MA_INVALID_ARGS;
45808 }
45809
45810 MA_ZERO_OBJECT(pBPF);
45811
45812 return ma_bpf_reinit__internal(pConfig, pHeap, pBPF, /*isNew*/MA_TRUE);
45813}
45814
45815MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF)
45816{
45817 ma_result result;
45818 size_t heapSizeInBytes;
45819 void* pHeap;
45820
45821 result = ma_bpf_get_heap_size(pConfig, &heapSizeInBytes);
45822 if (result != MA_SUCCESS) {
45823 return result;
45824 }
45825
45826 if (heapSizeInBytes > 0) {
45827 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
45828 if (pHeap == NULL) {
45829 return MA_OUT_OF_MEMORY;
45830 }
45831 } else {
45832 pHeap = NULL;
45833 }
45834
45835 result = ma_bpf_init_preallocated(pConfig, pHeap, pBPF);
45836 if (result != MA_SUCCESS) {
45837 ma_free(pHeap, pAllocationCallbacks);
45838 return result;
45839 }
45840
45841 pBPF->_ownsHeap = MA_TRUE;
45842 return MA_SUCCESS;
45843}
45844
45845MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks)
45846{
45847 ma_uint32 ibpf2;
45848
45849 if (pBPF == NULL) {
45850 return;
45851 }
45852
45853 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
45854 ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks);
45855 }
45856
45857 if (pBPF->_ownsHeap) {
45858 ma_free(pBPF->_pHeap, pAllocationCallbacks);
45859 }
45860}
45861
45862MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF)
45863{
45864 return ma_bpf_reinit__internal(pConfig, NULL, pBPF, /*isNew*/MA_FALSE);
45865}
45866
45867MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
45868{
45869 ma_result result;
45870 ma_uint32 ibpf2;
45871
45872 if (pBPF == NULL) {
45873 return MA_INVALID_ARGS;
45874 }
45875
45876 /* Faster path for in-place. */
45877 if (pFramesOut == pFramesIn) {
45878 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
45879 result = ma_bpf2_process_pcm_frames(&pBPF->pBPF2[ibpf2], pFramesOut, pFramesOut, frameCount);
45880 if (result != MA_SUCCESS) {
45881 return result;
45882 }
45883 }
45884 }
45885
45886 /* Slightly slower path for copying. */
45887 if (pFramesOut != pFramesIn) {
45888 ma_uint32 iFrame;
45889
45890 /* */ if (pBPF->format == ma_format_f32) {
45891 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
45892 const float* pFramesInF32 = (const float*)pFramesIn;
45893
45894 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
45895 MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));
45896
45897 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
45898 ma_bpf2_process_pcm_frame_f32(&pBPF->pBPF2[ibpf2], pFramesOutF32, pFramesOutF32);
45899 }
45900
45901 pFramesOutF32 += pBPF->channels;
45902 pFramesInF32 += pBPF->channels;
45903 }
45904 } else if (pBPF->format == ma_format_s16) {
45905 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
45906 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
45907
45908 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
45909 MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));
45910
45911 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
45912 ma_bpf2_process_pcm_frame_s16(&pBPF->pBPF2[ibpf2], pFramesOutS16, pFramesOutS16);
45913 }
45914
45915 pFramesOutS16 += pBPF->channels;
45916 pFramesInS16 += pBPF->channels;
45917 }
45918 } else {
45919 MA_ASSERT(MA_FALSE);
45920 return MA_INVALID_OPERATION; /* Should never hit this. */
45921 }
45922 }
45923
45924 return MA_SUCCESS;
45925}
45926
45928{
45929 if (pBPF == NULL) {
45930 return 0;
45931 }
45932
45933 return pBPF->bpf2Count*2;
45934}
45935
45936
45937
45942MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
45943{
45944 ma_notch2_config config;
45945
45946 MA_ZERO_OBJECT(&config);
45947 config.format = format;
45948 config.channels = channels;
45949 config.sampleRate = sampleRate;
45950 config.q = q;
45951 config.frequency = frequency;
45952
45953 if (config.q == 0) {
45954 config.q = 0.707107;
45955 }
45956
45957 return config;
45958}
45959
45960
45961static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig)
45962{
45963 ma_biquad_config bqConfig;
45964 double q;
45965 double w;
45966 double s;
45967 double c;
45968 double a;
45969
45970 MA_ASSERT(pConfig != NULL);
45971
45972 q = pConfig->q;
45973 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
45974 s = ma_sind(w);
45975 c = ma_cosd(w);
45976 a = s / (2*q);
45977
45978 bqConfig.b0 = 1;
45979 bqConfig.b1 = -2 * c;
45980 bqConfig.b2 = 1;
45981 bqConfig.a0 = 1 + a;
45982 bqConfig.a1 = -2 * c;
45983 bqConfig.a2 = 1 - a;
45984
45985 bqConfig.format = pConfig->format;
45986 bqConfig.channels = pConfig->channels;
45987
45988 return bqConfig;
45989}
45990
45991MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes)
45992{
45993 ma_biquad_config bqConfig;
45994 bqConfig = ma_notch2__get_biquad_config(pConfig);
45995
45996 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
45997}
45998
45999MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter)
46000{
46001 ma_result result;
46002 ma_biquad_config bqConfig;
46003
46004 if (pFilter == NULL) {
46005 return MA_INVALID_ARGS;
46006 }
46007
46008 MA_ZERO_OBJECT(pFilter);
46009
46010 if (pConfig == NULL) {
46011 return MA_INVALID_ARGS;
46012 }
46013
46014 bqConfig = ma_notch2__get_biquad_config(pConfig);
46015 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
46016 if (result != MA_SUCCESS) {
46017 return result;
46018 }
46019
46020 return MA_SUCCESS;
46021}
46022
46023MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter)
46024{
46025 ma_result result;
46026 size_t heapSizeInBytes;
46027 void* pHeap;
46028
46029 result = ma_notch2_get_heap_size(pConfig, &heapSizeInBytes);
46030 if (result != MA_SUCCESS) {
46031 return result;
46032 }
46033
46034 if (heapSizeInBytes > 0) {
46035 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
46036 if (pHeap == NULL) {
46037 return MA_OUT_OF_MEMORY;
46038 }
46039 } else {
46040 pHeap = NULL;
46041 }
46042
46043 result = ma_notch2_init_preallocated(pConfig, pHeap, pFilter);
46044 if (result != MA_SUCCESS) {
46045 ma_free(pHeap, pAllocationCallbacks);
46046 return result;
46047 }
46048
46049 pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
46050 return MA_SUCCESS;
46051}
46052
46053MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
46054{
46055 if (pFilter == NULL) {
46056 return;
46057 }
46058
46059 ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
46060}
46061
46063{
46064 ma_result result;
46065 ma_biquad_config bqConfig;
46066
46067 if (pFilter == NULL || pConfig == NULL) {
46068 return MA_INVALID_ARGS;
46069 }
46070
46071 bqConfig = ma_notch2__get_biquad_config(pConfig);
46072 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
46073 if (result != MA_SUCCESS) {
46074 return result;
46075 }
46076
46077 return MA_SUCCESS;
46078}
46079
46080static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
46081{
46082 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
46083}
46084
46085static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn)
46086{
46087 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
46088}
46089
46090MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
46091{
46092 if (pFilter == NULL) {
46093 return MA_INVALID_ARGS;
46094 }
46095
46096 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
46097}
46098
46100{
46101 if (pFilter == NULL) {
46102 return 0;
46103 }
46104
46105 return ma_biquad_get_latency(&pFilter->bq);
46106}
46107
46108
46109
46110
46115MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
46116{
46117 ma_peak2_config config;
46118
46119 MA_ZERO_OBJECT(&config);
46120 config.format = format;
46121 config.channels = channels;
46122 config.sampleRate = sampleRate;
46123 config.gainDB = gainDB;
46124 config.q = q;
46125 config.frequency = frequency;
46126
46127 if (config.q == 0) {
46128 config.q = 0.707107;
46129 }
46130
46131 return config;
46132}
46133
46134
46135static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig)
46136{
46137 ma_biquad_config bqConfig;
46138 double q;
46139 double w;
46140 double s;
46141 double c;
46142 double a;
46143 double A;
46144
46145 MA_ASSERT(pConfig != NULL);
46146
46147 q = pConfig->q;
46148 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
46149 s = ma_sind(w);
46150 c = ma_cosd(w);
46151 a = s / (2*q);
46152 A = ma_powd(10, (pConfig->gainDB / 40));
46153
46154 bqConfig.b0 = 1 + (a * A);
46155 bqConfig.b1 = -2 * c;
46156 bqConfig.b2 = 1 - (a * A);
46157 bqConfig.a0 = 1 + (a / A);
46158 bqConfig.a1 = -2 * c;
46159 bqConfig.a2 = 1 - (a / A);
46160
46161 bqConfig.format = pConfig->format;
46162 bqConfig.channels = pConfig->channels;
46163
46164 return bqConfig;
46165}
46166
46167MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes)
46168{
46169 ma_biquad_config bqConfig;
46170 bqConfig = ma_peak2__get_biquad_config(pConfig);
46171
46172 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
46173}
46174
46175MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter)
46176{
46177 ma_result result;
46178 ma_biquad_config bqConfig;
46179
46180 if (pFilter == NULL) {
46181 return MA_INVALID_ARGS;
46182 }
46183
46184 MA_ZERO_OBJECT(pFilter);
46185
46186 if (pConfig == NULL) {
46187 return MA_INVALID_ARGS;
46188 }
46189
46190 bqConfig = ma_peak2__get_biquad_config(pConfig);
46191 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
46192 if (result != MA_SUCCESS) {
46193 return result;
46194 }
46195
46196 return MA_SUCCESS;
46197}
46198
46199MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter)
46200{
46201 ma_result result;
46202 size_t heapSizeInBytes;
46203 void* pHeap;
46204
46205 result = ma_peak2_get_heap_size(pConfig, &heapSizeInBytes);
46206 if (result != MA_SUCCESS) {
46207 return result;
46208 }
46209
46210 if (heapSizeInBytes > 0) {
46211 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
46212 if (pHeap == NULL) {
46213 return MA_OUT_OF_MEMORY;
46214 }
46215 } else {
46216 pHeap = NULL;
46217 }
46218
46219 result = ma_peak2_init_preallocated(pConfig, pHeap, pFilter);
46220 if (result != MA_SUCCESS) {
46221 ma_free(pHeap, pAllocationCallbacks);
46222 return result;
46223 }
46224
46225 pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
46226 return MA_SUCCESS;
46227}
46228
46229MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
46230{
46231 if (pFilter == NULL) {
46232 return;
46233 }
46234
46235 ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
46236}
46237
46239{
46240 ma_result result;
46241 ma_biquad_config bqConfig;
46242
46243 if (pFilter == NULL || pConfig == NULL) {
46244 return MA_INVALID_ARGS;
46245 }
46246
46247 bqConfig = ma_peak2__get_biquad_config(pConfig);
46248 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
46249 if (result != MA_SUCCESS) {
46250 return result;
46251 }
46252
46253 return MA_SUCCESS;
46254}
46255
46256static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
46257{
46258 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
46259}
46260
46261static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn)
46262{
46263 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
46264}
46265
46266MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
46267{
46268 if (pFilter == NULL) {
46269 return MA_INVALID_ARGS;
46270 }
46271
46272 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
46273}
46274
46276{
46277 if (pFilter == NULL) {
46278 return 0;
46279 }
46280
46281 return ma_biquad_get_latency(&pFilter->bq);
46282}
46283
46284
46285
46290MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
46291{
46292 ma_loshelf2_config config;
46293
46294 MA_ZERO_OBJECT(&config);
46295 config.format = format;
46296 config.channels = channels;
46297 config.sampleRate = sampleRate;
46298 config.gainDB = gainDB;
46299 config.shelfSlope = shelfSlope;
46300 config.frequency = frequency;
46301
46302 return config;
46303}
46304
46305
46306static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig)
46307{
46308 ma_biquad_config bqConfig;
46309 double w;
46310 double s;
46311 double c;
46312 double A;
46313 double S;
46314 double a;
46315 double sqrtA;
46316
46317 MA_ASSERT(pConfig != NULL);
46318
46319 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
46320 s = ma_sind(w);
46321 c = ma_cosd(w);
46322 A = ma_powd(10, (pConfig->gainDB / 40));
46323 S = pConfig->shelfSlope;
46324 a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2);
46325 sqrtA = 2*ma_sqrtd(A)*a;
46326
46327 bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA);
46328 bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c);
46329 bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA);
46330 bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA;
46331 bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c);
46332 bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA;
46333
46334 bqConfig.format = pConfig->format;
46335 bqConfig.channels = pConfig->channels;
46336
46337 return bqConfig;
46338}
46339
46340MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes)
46341{
46342 ma_biquad_config bqConfig;
46343 bqConfig = ma_loshelf2__get_biquad_config(pConfig);
46344
46345 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
46346}
46347
46349{
46350 ma_result result;
46351 ma_biquad_config bqConfig;
46352
46353 if (pFilter == NULL) {
46354 return MA_INVALID_ARGS;
46355 }
46356
46357 MA_ZERO_OBJECT(pFilter);
46358
46359 if (pConfig == NULL) {
46360 return MA_INVALID_ARGS;
46361 }
46362
46363 bqConfig = ma_loshelf2__get_biquad_config(pConfig);
46364 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
46365 if (result != MA_SUCCESS) {
46366 return result;
46367 }
46368
46369 return MA_SUCCESS;
46370}
46371
46372MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter)
46373{
46374 ma_result result;
46375 size_t heapSizeInBytes;
46376 void* pHeap;
46377
46378 result = ma_loshelf2_get_heap_size(pConfig, &heapSizeInBytes);
46379 if (result != MA_SUCCESS) {
46380 return result;
46381 }
46382
46383 if (heapSizeInBytes > 0) {
46384 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
46385 if (pHeap == NULL) {
46386 return MA_OUT_OF_MEMORY;
46387 }
46388 } else {
46389 pHeap = NULL;
46390 }
46391
46392 result = ma_loshelf2_init_preallocated(pConfig, pHeap, pFilter);
46393 if (result != MA_SUCCESS) {
46394 ma_free(pHeap, pAllocationCallbacks);
46395 return result;
46396 }
46397
46398 pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
46399 return MA_SUCCESS;
46400}
46401
46402MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
46403{
46404 if (pFilter == NULL) {
46405 return;
46406 }
46407
46408 ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
46409}
46410
46412{
46413 ma_result result;
46414 ma_biquad_config bqConfig;
46415
46416 if (pFilter == NULL || pConfig == NULL) {
46417 return MA_INVALID_ARGS;
46418 }
46419
46420 bqConfig = ma_loshelf2__get_biquad_config(pConfig);
46421 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
46422 if (result != MA_SUCCESS) {
46423 return result;
46424 }
46425
46426 return MA_SUCCESS;
46427}
46428
46429static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
46430{
46431 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
46432}
46433
46434static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn)
46435{
46436 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
46437}
46438
46439MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
46440{
46441 if (pFilter == NULL) {
46442 return MA_INVALID_ARGS;
46443 }
46444
46445 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
46446}
46447
46449{
46450 if (pFilter == NULL) {
46451 return 0;
46452 }
46453
46454 return ma_biquad_get_latency(&pFilter->bq);
46455}
46456
46457
46458
46463MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
46464{
46465 ma_hishelf2_config config;
46466
46467 MA_ZERO_OBJECT(&config);
46468 config.format = format;
46469 config.channels = channels;
46470 config.sampleRate = sampleRate;
46471 config.gainDB = gainDB;
46472 config.shelfSlope = shelfSlope;
46473 config.frequency = frequency;
46474
46475 return config;
46476}
46477
46478
46479static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig)
46480{
46481 ma_biquad_config bqConfig;
46482 double w;
46483 double s;
46484 double c;
46485 double A;
46486 double S;
46487 double a;
46488 double sqrtA;
46489
46490 MA_ASSERT(pConfig != NULL);
46491
46492 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
46493 s = ma_sind(w);
46494 c = ma_cosd(w);
46495 A = ma_powd(10, (pConfig->gainDB / 40));
46496 S = pConfig->shelfSlope;
46497 a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2);
46498 sqrtA = 2*ma_sqrtd(A)*a;
46499
46500 bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA);
46501 bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c);
46502 bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA);
46503 bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA;
46504 bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c);
46505 bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA;
46506
46507 bqConfig.format = pConfig->format;
46508 bqConfig.channels = pConfig->channels;
46509
46510 return bqConfig;
46511}
46512
46513MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes)
46514{
46515 ma_biquad_config bqConfig;
46516 bqConfig = ma_hishelf2__get_biquad_config(pConfig);
46517
46518 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
46519}
46520
46522{
46523 ma_result result;
46524 ma_biquad_config bqConfig;
46525
46526 if (pFilter == NULL) {
46527 return MA_INVALID_ARGS;
46528 }
46529
46530 MA_ZERO_OBJECT(pFilter);
46531
46532 if (pConfig == NULL) {
46533 return MA_INVALID_ARGS;
46534 }
46535
46536 bqConfig = ma_hishelf2__get_biquad_config(pConfig);
46537 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
46538 if (result != MA_SUCCESS) {
46539 return result;
46540 }
46541
46542 return MA_SUCCESS;
46543}
46544
46545MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter)
46546{
46547 ma_result result;
46548 size_t heapSizeInBytes;
46549 void* pHeap;
46550
46551 result = ma_hishelf2_get_heap_size(pConfig, &heapSizeInBytes);
46552 if (result != MA_SUCCESS) {
46553 return result;
46554 }
46555
46556 if (heapSizeInBytes > 0) {
46557 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
46558 if (pHeap == NULL) {
46559 return MA_OUT_OF_MEMORY;
46560 }
46561 } else {
46562 pHeap = NULL;
46563 }
46564
46565 result = ma_hishelf2_init_preallocated(pConfig, pHeap, pFilter);
46566 if (result != MA_SUCCESS) {
46567 ma_free(pHeap, pAllocationCallbacks);
46568 return result;
46569 }
46570
46571 pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
46572 return MA_SUCCESS;
46573}
46574
46575MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
46576{
46577 if (pFilter == NULL) {
46578 return;
46579 }
46580
46581 ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
46582}
46583
46585{
46586 ma_result result;
46587 ma_biquad_config bqConfig;
46588
46589 if (pFilter == NULL || pConfig == NULL) {
46590 return MA_INVALID_ARGS;
46591 }
46592
46593 bqConfig = ma_hishelf2__get_biquad_config(pConfig);
46594 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
46595 if (result != MA_SUCCESS) {
46596 return result;
46597 }
46598
46599 return MA_SUCCESS;
46600}
46601
46602static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
46603{
46604 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
46605}
46606
46607static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn)
46608{
46609 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
46610}
46611
46612MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
46613{
46614 if (pFilter == NULL) {
46615 return MA_INVALID_ARGS;
46616 }
46617
46618 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
46619}
46620
46622{
46623 if (pFilter == NULL) {
46624 return 0;
46625 }
46626
46627 return ma_biquad_get_latency(&pFilter->bq);
46628}
46629
46630
46631
46632/*
46633Delay
46634*/
46635MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)
46636{
46637 ma_delay_config config;
46638
46639 MA_ZERO_OBJECT(&config);
46640 config.channels = channels;
46641 config.sampleRate = sampleRate;
46642 config.delayInFrames = delayInFrames;
46643 config.delayStart = (decay == 0) ? MA_TRUE : MA_FALSE; /* Delay the start if it looks like we're not configuring an echo. */
46644 config.wet = 1;
46645 config.dry = 1;
46646 config.decay = decay;
46647
46648 return config;
46649}
46650
46651
46652MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay)
46653{
46654 if (pDelay == NULL) {
46655 return MA_INVALID_ARGS;
46656 }
46657
46658 MA_ZERO_OBJECT(pDelay);
46659
46660 if (pConfig == NULL) {
46661 return MA_INVALID_ARGS;
46662 }
46663
46664 if (pConfig->decay < 0 || pConfig->decay > 1) {
46665 return MA_INVALID_ARGS;
46666 }
46667
46668 pDelay->config = *pConfig;
46669 pDelay->bufferSizeInFrames = pConfig->delayInFrames;
46670 pDelay->cursor = 0;
46671
46672 pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks);
46673 if (pDelay->pBuffer == NULL) {
46674 return MA_OUT_OF_MEMORY;
46675 }
46676
46678
46679 return MA_SUCCESS;
46680}
46681
46682MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks)
46683{
46684 if (pDelay == NULL) {
46685 return;
46686 }
46687
46688 ma_free(pDelay->pBuffer, pAllocationCallbacks);
46689}
46690
46691MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
46692{
46693 ma_uint32 iFrame;
46694 ma_uint32 iChannel;
46695 float* pFramesOutF32 = (float*)pFramesOut;
46696 const float* pFramesInF32 = (const float*)pFramesIn;
46697
46698 if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) {
46699 return MA_INVALID_ARGS;
46700 }
46701
46702 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
46703 for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) {
46704 ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel;
46705
46706 if (pDelay->config.delayStart) {
46707 /* Delayed start. */
46708
46709 /* Read */
46710 pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet;
46711
46712 /* Feedback */
46713 pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry);
46714 } else {
46715 /* Immediate start */
46716
46717 /* Feedback */
46718 pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry);
46719
46720 /* Read */
46721 pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet;
46722 }
46723 }
46724
46725 pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames;
46726
46727 pFramesOutF32 += pDelay->config.channels;
46728 pFramesInF32 += pDelay->config.channels;
46729 }
46730
46731 return MA_SUCCESS;
46732}
46733
46734MA_API void ma_delay_set_wet(ma_delay* pDelay, float value)
46735{
46736 if (pDelay == NULL) {
46737 return;
46738 }
46739
46740 pDelay->config.wet = value;
46741}
46742
46743MA_API float ma_delay_get_wet(const ma_delay* pDelay)
46744{
46745 if (pDelay == NULL) {
46746 return 0;
46747 }
46748
46749 return pDelay->config.wet;
46750}
46751
46752MA_API void ma_delay_set_dry(ma_delay* pDelay, float value)
46753{
46754 if (pDelay == NULL) {
46755 return;
46756 }
46757
46758 pDelay->config.dry = value;
46759}
46760
46761MA_API float ma_delay_get_dry(const ma_delay* pDelay)
46762{
46763 if (pDelay == NULL) {
46764 return 0;
46765 }
46766
46767 return pDelay->config.dry;
46768}
46769
46770MA_API void ma_delay_set_decay(ma_delay* pDelay, float value)
46771{
46772 if (pDelay == NULL) {
46773 return;
46774 }
46775
46776 pDelay->config.decay = value;
46777}
46778
46779MA_API float ma_delay_get_decay(const ma_delay* pDelay)
46780{
46781 if (pDelay == NULL) {
46782 return 0;
46783 }
46784
46785 return pDelay->config.decay;
46786}
46787
46788
46790{
46791 ma_gainer_config config;
46792
46793 MA_ZERO_OBJECT(&config);
46794 config.channels = channels;
46795 config.smoothTimeInFrames = smoothTimeInFrames;
46796
46797 return config;
46798}
46799
46800
46801typedef struct
46802{
46803 size_t sizeInBytes;
46804 size_t oldGainsOffset;
46805 size_t newGainsOffset;
46806} ma_gainer_heap_layout;
46807
46808static ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout)
46809{
46810 MA_ASSERT(pHeapLayout != NULL);
46811
46812 MA_ZERO_OBJECT(pHeapLayout);
46813
46814 if (pConfig == NULL) {
46815 return MA_INVALID_ARGS;
46816 }
46817
46818 if (pConfig->channels == 0) {
46819 return MA_INVALID_ARGS;
46820 }
46821
46822 pHeapLayout->sizeInBytes = 0;
46823
46824 /* Old gains. */
46825 pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes;
46826 pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
46827
46828 /* New gains. */
46829 pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes;
46830 pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
46831
46832 /* Alignment. */
46833 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
46834
46835 return MA_SUCCESS;
46836}
46837
46838
46839MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes)
46840{
46841 ma_result result;
46842 ma_gainer_heap_layout heapLayout;
46843
46844 if (pHeapSizeInBytes == NULL) {
46845 return MA_INVALID_ARGS;
46846 }
46847
46848 *pHeapSizeInBytes = 0;
46849
46850 result = ma_gainer_get_heap_layout(pConfig, &heapLayout);
46851 if (result != MA_SUCCESS) {
46852 return MA_INVALID_ARGS;
46853 }
46854
46855 *pHeapSizeInBytes = heapLayout.sizeInBytes;
46856
46857 return MA_SUCCESS;
46858}
46859
46860
46861MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer)
46862{
46863 ma_result result;
46864 ma_gainer_heap_layout heapLayout;
46865 ma_uint32 iChannel;
46866
46867 if (pGainer == NULL) {
46868 return MA_INVALID_ARGS;
46869 }
46870
46871 MA_ZERO_OBJECT(pGainer);
46872
46873 if (pConfig == NULL || pHeap == NULL) {
46874 return MA_INVALID_ARGS;
46875 }
46876
46877 result = ma_gainer_get_heap_layout(pConfig, &heapLayout);
46878 if (result != MA_SUCCESS) {
46879 return result;
46880 }
46881
46882 pGainer->_pHeap = pHeap;
46883 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
46884
46885 pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset);
46886 pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset);
46887
46888 pGainer->config = *pConfig;
46889 pGainer->t = (ma_uint32)-1; /* No interpolation by default. */
46890
46891 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
46892 pGainer->pOldGains[iChannel] = 1;
46893 pGainer->pNewGains[iChannel] = 1;
46894 }
46895
46896 return MA_SUCCESS;
46897}
46898
46899MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer)
46900{
46901 ma_result result;
46902 size_t heapSizeInBytes;
46903 void* pHeap;
46904
46905 result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes);
46906 if (result != MA_SUCCESS) {
46907 return result; /* Failed to retrieve the size of the heap allocation. */
46908 }
46909
46910 if (heapSizeInBytes > 0) {
46911 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
46912 if (pHeap == NULL) {
46913 return MA_OUT_OF_MEMORY;
46914 }
46915 } else {
46916 pHeap = NULL;
46917 }
46918
46919 result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer);
46920 if (result != MA_SUCCESS) {
46921 ma_free(pHeap, pAllocationCallbacks);
46922 return result;
46923 }
46924
46925 pGainer->_ownsHeap = MA_TRUE;
46926 return MA_SUCCESS;
46927}
46928
46929MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks)
46930{
46931 if (pGainer == NULL) {
46932 return;
46933 }
46934
46935 if (pGainer->_ownsHeap) {
46936 ma_free(pGainer->_pHeap, pAllocationCallbacks);
46937 }
46938}
46939
46940static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel)
46941{
46942 float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
46943 return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a);
46944}
46945
46946MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
46947{
46948 ma_uint64 iFrame;
46949 ma_uint32 iChannel;
46950 float* pFramesOutF32 = (float*)pFramesOut;
46951 const float* pFramesInF32 = (const float*)pFramesIn;
46952
46953 if (pGainer == NULL) {
46954 return MA_INVALID_ARGS;
46955 }
46956
46957 if (pGainer->t >= pGainer->config.smoothTimeInFrames) {
46958 /* Fast path. No gain calculation required. */
46959 ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains);
46960
46961 /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */
46962 if (pGainer->t == (ma_uint32)-1) {
46963 pGainer->t = pGainer->config.smoothTimeInFrames;
46964 }
46965 } else {
46966 /* Slow path. Need to interpolate the gain for each channel individually. */
46967
46968 /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
46969 if (pFramesOut != NULL && pFramesIn != NULL) {
46970 float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
46971 float d = 1.0f / pGainer->config.smoothTimeInFrames;
46972 ma_uint32 channelCount = pGainer->config.channels;
46973
46974 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
46975 for (iChannel = 0; iChannel < channelCount; iChannel += 1) {
46976 pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a);
46977 }
46978
46979 pFramesOutF32 += channelCount;
46980 pFramesInF32 += channelCount;
46981
46982 a += d;
46983 if (a > 1) {
46984 a = 1;
46985 }
46986 }
46987 }
46988
46989 pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames);
46990
46991 #if 0 /* Reference implementation. */
46992 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
46993 /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
46994 if (pFramesOut != NULL && pFramesIn != NULL) {
46995 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
46996 pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel);
46997 }
46998 }
46999
47000 /* Move interpolation time forward, but don't go beyond our smoothing time. */
47001 pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames);
47002 }
47003 #endif
47004 }
47005
47006 return MA_SUCCESS;
47007}
47008
47009static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel)
47010{
47011 pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel);
47012 pGainer->pNewGains[iChannel] = newGain;
47013}
47014
47015static void ma_gainer_reset_smoothing_time(ma_gainer* pGainer)
47016{
47017 if (pGainer->t == (ma_uint32)-1) {
47018 pGainer->t = pGainer->config.smoothTimeInFrames; /* No smoothing required for initial gains setting. */
47019 } else {
47020 pGainer->t = 0;
47021 }
47022}
47023
47024MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain)
47025{
47026 ma_uint32 iChannel;
47027
47028 if (pGainer == NULL) {
47029 return MA_INVALID_ARGS;
47030 }
47031
47032 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
47033 ma_gainer_set_gain_by_index(pGainer, newGain, iChannel);
47034 }
47035
47036 /* 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. */
47037 ma_gainer_reset_smoothing_time(pGainer);
47038
47039 return MA_SUCCESS;
47040}
47041
47042MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains)
47043{
47044 ma_uint32 iChannel;
47045
47046 if (pGainer == NULL || pNewGains == NULL) {
47047 return MA_INVALID_ARGS;
47048 }
47049
47050 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
47051 ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel);
47052 }
47053
47054 /* 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. */
47055 ma_gainer_reset_smoothing_time(pGainer);
47056
47057 return MA_SUCCESS;
47058}
47059
47060
47062{
47063 ma_panner_config config;
47064
47065 MA_ZERO_OBJECT(&config);
47066 config.format = format;
47067 config.channels = channels;
47068 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. */
47069 config.pan = 0;
47070
47071 return config;
47072}
47073
47074
47076{
47077 if (pPanner == NULL) {
47078 return MA_INVALID_ARGS;
47079 }
47080
47081 MA_ZERO_OBJECT(pPanner);
47082
47083 if (pConfig == NULL) {
47084 return MA_INVALID_ARGS;
47085 }
47086
47087 pPanner->format = pConfig->format;
47088 pPanner->channels = pConfig->channels;
47089 pPanner->mode = pConfig->mode;
47090 pPanner->pan = pConfig->pan;
47091
47092 return MA_SUCCESS;
47093}
47094
47095static void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan)
47096{
47097 ma_uint64 iFrame;
47098
47099 if (pan > 0) {
47100 float factor = 1.0f - pan;
47101 if (pFramesOut == pFramesIn) {
47102 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
47103 pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor;
47104 }
47105 } else {
47106 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
47107 pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor;
47108 pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1];
47109 }
47110 }
47111 } else {
47112 float factor = 1.0f + pan;
47113 if (pFramesOut == pFramesIn) {
47114 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
47115 pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor;
47116 }
47117 } else {
47118 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
47119 pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0];
47120 pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor;
47121 }
47122 }
47123 }
47124}
47125
47126static void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan)
47127{
47128 if (pan == 0) {
47129 /* Fast path. No panning required. */
47130 if (pFramesOut == pFramesIn) {
47131 /* No-op */
47132 } else {
47133 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
47134 }
47135
47136 return;
47137 }
47138
47139 switch (format) {
47140 case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break;
47141
47142 /* Unknown format. Just copy. */
47143 default:
47144 {
47145 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
47146 } break;
47147 }
47148}
47149
47150
47151static void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan)
47152{
47153 ma_uint64 iFrame;
47154
47155 if (pan > 0) {
47156 float factorL0 = 1.0f - pan;
47157 float factorL1 = 0.0f + pan;
47158
47159 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
47160 float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0);
47161 float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1];
47162
47163 pFramesOut[iFrame*2 + 0] = sample0;
47164 pFramesOut[iFrame*2 + 1] = sample1;
47165 }
47166 } else {
47167 float factorR0 = 0.0f - pan;
47168 float factorR1 = 1.0f + pan;
47169
47170 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
47171 float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0);
47172 float sample1 = (pFramesIn[iFrame*2 + 1] * factorR1);
47173
47174 pFramesOut[iFrame*2 + 0] = sample0;
47175 pFramesOut[iFrame*2 + 1] = sample1;
47176 }
47177 }
47178}
47179
47180static void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan)
47181{
47182 if (pan == 0) {
47183 /* Fast path. No panning required. */
47184 if (pFramesOut == pFramesIn) {
47185 /* No-op */
47186 } else {
47187 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
47188 }
47189
47190 return;
47191 }
47192
47193 switch (format) {
47194 case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break;
47195
47196 /* Unknown format. Just copy. */
47197 default:
47198 {
47199 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
47200 } break;
47201 }
47202}
47203
47204MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
47205{
47206 if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) {
47207 return MA_INVALID_ARGS;
47208 }
47209
47210 if (pPanner->channels == 2) {
47211 /* Stereo case. For now assume channel 0 is left and channel right is 1, but should probably add support for a channel map. */
47212 if (pPanner->mode == ma_pan_mode_balance) {
47213 ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan);
47214 } else {
47215 ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan);
47216 }
47217 } else {
47218 if (pPanner->channels == 1) {
47219 /* Panning has no effect on mono streams. */
47220 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels);
47221 } else {
47222 /* For now we're not going to support non-stereo set ups. Not sure how I want to handle this case just yet. */
47223 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels);
47224 }
47225 }
47226
47227 return MA_SUCCESS;
47228}
47229
47231{
47232 if (pPanner == NULL) {
47233 return;
47234 }
47235
47236 pPanner->mode = mode;
47237}
47238
47240{
47241 if (pPanner == NULL) {
47242 return ma_pan_mode_balance;
47243 }
47244
47245 return pPanner->mode;
47246}
47247
47248MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan)
47249{
47250 if (pPanner == NULL) {
47251 return;
47252 }
47253
47254 pPanner->pan = ma_clamp(pan, -1.0f, 1.0f);
47255}
47256
47257MA_API float ma_panner_get_pan(const ma_panner* pPanner)
47258{
47259 if (pPanner == NULL) {
47260 return 0;
47261 }
47262
47263 return pPanner->pan;
47264}
47265
47266
47267
47268
47270{
47271 ma_fader_config config;
47272
47273 MA_ZERO_OBJECT(&config);
47274 config.format = format;
47275 config.channels = channels;
47276 config.sampleRate = sampleRate;
47277
47278 return config;
47279}
47280
47281
47282MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader)
47283{
47284 if (pFader == NULL) {
47285 return MA_INVALID_ARGS;
47286 }
47287
47288 MA_ZERO_OBJECT(pFader);
47289
47290 if (pConfig == NULL) {
47291 return MA_INVALID_ARGS;
47292 }
47293
47294 /* Only f32 is supported for now. */
47295 if (pConfig->format != ma_format_f32) {
47296 return MA_INVALID_ARGS;
47297 }
47298
47299 pFader->config = *pConfig;
47300 pFader->volumeBeg = 1;
47301 pFader->volumeEnd = 1;
47302 pFader->lengthInFrames = 0;
47303 pFader->cursorInFrames = 0;
47304
47305 return MA_SUCCESS;
47306}
47307
47308MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
47309{
47310 if (pFader == NULL) {
47311 return MA_INVALID_ARGS;
47312 }
47313
47314 /*
47315 For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for
47316 the conversion to a float which we use for the linear interpolation. This might be changed later.
47317 */
47318 if (frameCount + pFader->cursorInFrames > UINT_MAX) {
47319 frameCount = UINT_MAX - pFader->cursorInFrames;
47320 }
47321
47322 /* Optimized path if volumeBeg and volumeEnd are equal. */
47323 if (pFader->volumeBeg == pFader->volumeEnd) {
47324 if (pFader->volumeBeg == 1) {
47325 /* Straight copy. */
47326 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels);
47327 } else {
47328 /* Copy with volume. */
47329 ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd);
47330 }
47331 } else {
47332 /* Slower path. Volumes are different, so may need to do an interpolation. */
47333 if (pFader->cursorInFrames >= pFader->lengthInFrames) {
47334 /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */
47335 ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd);
47336 } else {
47337 /* Slow path. This is where we do the actual fading. */
47338 ma_uint64 iFrame;
47339 ma_uint32 iChannel;
47340
47341 /* For now we only support f32. Support for other formats will be added later. */
47342 if (pFader->config.format == ma_format_f32) {
47343 const float* pFramesInF32 = (const float*)pFramesIn;
47344 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
47345
47346 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
47347 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. */
47348 float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a);
47349
47350 for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) {
47351 pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume;
47352 }
47353 }
47354 } else {
47355 return MA_NOT_IMPLEMENTED;
47356 }
47357 }
47358 }
47359
47360 pFader->cursorInFrames += frameCount;
47361
47362 return MA_SUCCESS;
47363}
47364
47365MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
47366{
47367 if (pFader == NULL) {
47368 return;
47369 }
47370
47371 if (pFormat != NULL) {
47372 *pFormat = pFader->config.format;
47373 }
47374
47375 if (pChannels != NULL) {
47376 *pChannels = pFader->config.channels;
47377 }
47378
47379 if (pSampleRate != NULL) {
47380 *pSampleRate = pFader->config.sampleRate;
47381 }
47382}
47383
47384MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames)
47385{
47386 if (pFader == NULL) {
47387 return;
47388 }
47389
47390 /* If the volume is negative, use current volume. */
47391 if (volumeBeg < 0) {
47392 volumeBeg = ma_fader_get_current_volume(pFader);
47393 }
47394
47395 /*
47396 The length needs to be clamped to 32-bits due to how we convert it to a float for linear
47397 interpolation reasons. I might change this requirement later, but for now it's not important.
47398 */
47399 if (lengthInFrames > UINT_MAX) {
47400 lengthInFrames = UINT_MAX;
47401 }
47402
47403 pFader->volumeBeg = volumeBeg;
47404 pFader->volumeEnd = volumeEnd;
47405 pFader->lengthInFrames = lengthInFrames;
47406 pFader->cursorInFrames = 0; /* Reset cursor. */
47407}
47408
47410{
47411 if (pFader == NULL) {
47412 return 0.0f;
47413 }
47414
47415 /* The current volume depends on the position of the cursor. */
47416 if (pFader->cursorInFrames == 0) {
47417 return pFader->volumeBeg;
47418 } else if (pFader->cursorInFrames >= pFader->lengthInFrames) {
47419 return pFader->volumeEnd;
47420 } else {
47421 /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpoluation between volumeBeg and volumeEnd based on our cursor position. */
47422 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(). */
47423 }
47424}
47425
47426
47427
47428
47429
47430MA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z)
47431{
47432 ma_vec3f v;
47433
47434 v.x = x;
47435 v.y = y;
47436 v.z = z;
47437
47438 return v;
47439}
47440
47441MA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b)
47442{
47443 return ma_vec3f_init_3f(
47444 a.x - b.x,
47445 a.y - b.y,
47446 a.z - b.z
47447 );
47448}
47449
47450MA_API ma_vec3f ma_vec3f_neg(ma_vec3f a)
47451{
47452 return ma_vec3f_init_3f(
47453 -a.x,
47454 -a.y,
47455 -a.z
47456 );
47457}
47458
47459MA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b)
47460{
47461 return a.x*b.x + a.y*b.y + a.z*b.z;
47462}
47463
47464MA_API float ma_vec3f_len2(ma_vec3f v)
47465{
47466 return ma_vec3f_dot(v, v);
47467}
47468
47469MA_API float ma_vec3f_len(ma_vec3f v)
47470{
47471 return (float)ma_sqrtd(ma_vec3f_len2(v));
47472}
47473
47474MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b)
47475{
47476 return ma_vec3f_len(ma_vec3f_sub(a, b));
47477}
47478
47479MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v)
47480{
47481 float f;
47482 float l = ma_vec3f_len(v);
47483 if (l == 0) {
47484 return ma_vec3f_init_3f(0, 0, 0);
47485 }
47486
47487 f = 1 / l;
47488 v.x *= f;
47489 v.y *= f;
47490 v.z *= f;
47491
47492 return v;
47493}
47494
47495MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b)
47496{
47497 return ma_vec3f_init_3f(
47498 a.y*b.z - a.z*b.y,
47499 a.z*b.x - a.x*b.z,
47500 a.x*b.y - a.y*b.x
47501 );
47502}
47503
47504
47505
47506static 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);
47507static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition);
47508
47509
47510#ifndef MA_DEFAULT_SPEED_OF_SOUND
47511#define MA_DEFAULT_SPEED_OF_SOUND 343.3f
47512#endif
47513
47514/*
47515These vectors represent the direction that speakers are facing from the center point. They're used
47516for panning in the spatializer. Must be normalized.
47517*/
47518static ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = {
47519 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_NONE */
47520 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_MONO */
47521 {-0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_LEFT */
47522 {+0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_RIGHT */
47523 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_FRONT_CENTER */
47524 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_LFE */
47525 {-0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_LEFT */
47526 {+0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_RIGHT */
47527 {-0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_LEFT_CENTER */
47528 {+0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_RIGHT_CENTER */
47529 { 0.0f, 0.0f, +1.0f }, /* MA_CHANNEL_BACK_CENTER */
47530 {-1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_LEFT */
47531 {+1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_RIGHT */
47532 { 0.0f, +1.0f, 0.0f }, /* MA_CHANNEL_TOP_CENTER */
47533 {-0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_LEFT */
47534 { 0.0f, +0.7071f, -0.7071f }, /* MA_CHANNEL_TOP_FRONT_CENTER */
47535 {+0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_RIGHT */
47536 {-0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_LEFT */
47537 { 0.0f, +0.7071f, +0.7071f }, /* MA_CHANNEL_TOP_BACK_CENTER */
47538 {+0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_RIGHT */
47539 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_0 */
47540 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_1 */
47541 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_2 */
47542 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_3 */
47543 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_4 */
47544 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_5 */
47545 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_6 */
47546 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_7 */
47547 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_8 */
47548 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_9 */
47549 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_10 */
47550 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_11 */
47551 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_12 */
47552 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_13 */
47553 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_14 */
47554 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_15 */
47555 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_16 */
47556 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_17 */
47557 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_18 */
47558 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_19 */
47559 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_20 */
47560 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_21 */
47561 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_22 */
47562 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_23 */
47563 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_24 */
47564 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_25 */
47565 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_26 */
47566 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_27 */
47567 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_28 */
47568 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_29 */
47569 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_30 */
47570 { 0.0f, 0.0f, -1.0f } /* MA_CHANNEL_AUX_31 */
47571};
47572
47573static ma_vec3f ma_get_channel_direction(ma_channel channel)
47574{
47576 return ma_vec3f_init_3f(0, 0, -1);
47577 } else {
47578 return g_maChannelDirections[channel];
47579 }
47580}
47581
47582
47583
47584static float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff)
47585{
47586 if (minDistance >= maxDistance) {
47587 return 1; /* To avoid division by zero. Do not attenuate. */
47588 }
47589
47590 return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance));
47591}
47592
47593static float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff)
47594{
47595 if (minDistance >= maxDistance) {
47596 return 1; /* To avoid division by zero. Do not attenuate. */
47597 }
47598
47599 return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance);
47600}
47601
47602static float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff)
47603{
47604 if (minDistance >= maxDistance) {
47605 return 1; /* To avoid division by zero. Do not attenuate. */
47606 }
47607
47608 return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff);
47609}
47610
47611
47612/*
47613Dopper Effect calculation taken from the OpenAL spec, with two main differences:
47614
47615 1) The source to listener vector will have already been calcualted at an earlier step so we can
47616 just use that directly. We need only the position of the source relative to the origin.
47617
47618 2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight
47619 into the resampler directly.
47620*/
47621static float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor)
47622{
47623 float len;
47624 float vls;
47625 float vss;
47626
47627 len = ma_vec3f_len(relativePosition);
47628
47629 /*
47630 There's a case where the position of the source will be right on top of the listener in which
47631 case the length will be 0 and we'll end up with a division by zero. We can just return a ratio
47632 of 1.0 in this case. This is not considered in the OpenAL spec, but is necessary.
47633 */
47634 if (len == 0) {
47635 return 1.0;
47636 }
47637
47638 vls = ma_vec3f_dot(relativePosition, listenVelocity) / len;
47639 vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len;
47640
47641 vls = ma_min(vls, speedOfSound / dopplerFactor);
47642 vss = ma_min(vss, speedOfSound / dopplerFactor);
47643
47644 return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss);
47645}
47646
47647
47648static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channelCount)
47649{
47650 /*
47651 Special case for stereo. Want to default the left and right speakers to side left and side
47652 right so that they're facing directly down the X axis rather than slightly forward. Not
47653 doing this will result in sounds being quieter when behind the listener. This might
47654 actually be good for some scenerios, but I don't think it's an appropriate default because
47655 it can be a bit unexpected.
47656 */
47657 if (channelCount == 2) {
47658 pChannelMap[0] = MA_CHANNEL_SIDE_LEFT;
47659 pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT;
47660 } else {
47661 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
47662 }
47663}
47664
47665
47667{
47669
47670 MA_ZERO_OBJECT(&config);
47671 config.channelsOut = channelsOut;
47672 config.pChannelMapOut = NULL;
47674 config.worldUp = ma_vec3f_init_3f(0, 1, 0);
47675 config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */
47676 config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */
47677 config.coneOuterGain = 0;
47678 config.speedOfSound = 343.3f; /* Same as OpenAL. Used for doppler effect. */
47679
47680 return config;
47681}
47682
47683
47684typedef struct
47685{
47686 size_t sizeInBytes;
47687 size_t channelMapOutOffset;
47688} ma_spatializer_listener_heap_layout;
47689
47690static ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout)
47691{
47692 MA_ASSERT(pHeapLayout != NULL);
47693
47694 MA_ZERO_OBJECT(pHeapLayout);
47695
47696 if (pConfig == NULL) {
47697 return MA_INVALID_ARGS;
47698 }
47699
47700 if (pConfig->channelsOut == 0) {
47701 return MA_INVALID_ARGS;
47702 }
47703
47704 pHeapLayout->sizeInBytes = 0;
47705
47706 /* Channel map. We always need this, even for passthroughs. */
47707 pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes;
47708 pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut);
47709
47710 return MA_SUCCESS;
47711}
47712
47713
47715{
47716 ma_result result;
47717 ma_spatializer_listener_heap_layout heapLayout;
47718
47719 if (pHeapSizeInBytes == NULL) {
47720 return MA_INVALID_ARGS;
47721 }
47722
47723 *pHeapSizeInBytes = 0;
47724
47725 result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout);
47726 if (result != MA_SUCCESS) {
47727 return result;
47728 }
47729
47730 *pHeapSizeInBytes = heapLayout.sizeInBytes;
47731
47732 return MA_SUCCESS;
47733}
47734
47736{
47737 ma_result result;
47738 ma_spatializer_listener_heap_layout heapLayout;
47739
47740 if (pListener == NULL) {
47741 return MA_INVALID_ARGS;
47742 }
47743
47744 MA_ZERO_OBJECT(pListener);
47745
47746 result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout);
47747 if (result != MA_SUCCESS) {
47748 return result;
47749 }
47750
47751 pListener->_pHeap = pHeap;
47752 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
47753
47754 pListener->config = *pConfig;
47755 pListener->position = ma_vec3f_init_3f(0, 0, 0);
47756 pListener->direction = ma_vec3f_init_3f(0, 0, -1);
47757 pListener->velocity = ma_vec3f_init_3f(0, 0, 0);
47758 pListener->isEnabled = MA_TRUE;
47759
47760 /* Swap the forward direction if we're left handed (it was initialized based on right handed). */
47761 if (pListener->config.handedness == ma_handedness_left) {
47762 pListener->direction = ma_vec3f_neg(pListener->direction);
47763 }
47764
47765
47766 /* We must always have a valid channel map. */
47767 pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset);
47768
47769 /* Use a slightly different default channel map for stereo. */
47770 if (pConfig->pChannelMapOut == NULL) {
47771 ma_get_default_channel_map_for_spatializer(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->channelsOut);
47772 } else {
47774 }
47775
47776 return MA_SUCCESS;
47777}
47778
47780{
47781 ma_result result;
47782 size_t heapSizeInBytes;
47783 void* pHeap;
47784
47785 result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes);
47786 if (result != MA_SUCCESS) {
47787 return result;
47788 }
47789
47790 if (heapSizeInBytes > 0) {
47791 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
47792 if (pHeap == NULL) {
47793 return MA_OUT_OF_MEMORY;
47794 }
47795 } else {
47796 pHeap = NULL;
47797 }
47798
47799 result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener);
47800 if (result != MA_SUCCESS) {
47801 ma_free(pHeap, pAllocationCallbacks);
47802 return result;
47803 }
47804
47805 pListener->_ownsHeap = MA_TRUE;
47806 return MA_SUCCESS;
47807}
47808
47810{
47811 if (pListener == NULL) {
47812 return;
47813 }
47814
47815 if (pListener->_ownsHeap) {
47816 ma_free(pListener->_pHeap, pAllocationCallbacks);
47817 }
47818}
47819
47821{
47822 if (pListener == NULL) {
47823 return NULL;
47824 }
47825
47826 return pListener->config.pChannelMapOut;
47827}
47828
47829MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
47830{
47831 if (pListener == NULL) {
47832 return;
47833 }
47834
47835 pListener->config.coneInnerAngleInRadians = innerAngleInRadians;
47836 pListener->config.coneOuterAngleInRadians = outerAngleInRadians;
47837 pListener->config.coneOuterGain = outerGain;
47838}
47839
47840MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
47841{
47842 if (pListener == NULL) {
47843 return;
47844 }
47845
47846 if (pInnerAngleInRadians != NULL) {
47847 *pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians;
47848 }
47849
47850 if (pOuterAngleInRadians != NULL) {
47851 *pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians;
47852 }
47853
47854 if (pOuterGain != NULL) {
47855 *pOuterGain = pListener->config.coneOuterGain;
47856 }
47857}
47858
47859MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z)
47860{
47861 if (pListener == NULL) {
47862 return;
47863 }
47864
47865 pListener->position = ma_vec3f_init_3f(x, y, z);
47866}
47867
47869{
47870 if (pListener == NULL) {
47871 return ma_vec3f_init_3f(0, 0, 0);
47872 }
47873
47874 return pListener->position;
47875}
47876
47877MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z)
47878{
47879 if (pListener == NULL) {
47880 return;
47881 }
47882
47883 pListener->direction = ma_vec3f_init_3f(x, y, z);
47884}
47885
47887{
47888 if (pListener == NULL) {
47889 return ma_vec3f_init_3f(0, 0, -1);
47890 }
47891
47892 return pListener->direction;
47893}
47894
47895MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z)
47896{
47897 if (pListener == NULL) {
47898 return;
47899 }
47900
47901 pListener->velocity = ma_vec3f_init_3f(x, y, z);
47902}
47903
47905{
47906 if (pListener == NULL) {
47907 return ma_vec3f_init_3f(0, 0, 0);
47908 }
47909
47910 return pListener->velocity;
47911}
47912
47914{
47915 if (pListener == NULL) {
47916 return;
47917 }
47918
47919 pListener->config.speedOfSound = speedOfSound;
47920}
47921
47923{
47924 if (pListener == NULL) {
47925 return 0;
47926 }
47927
47928 return pListener->config.speedOfSound;
47929}
47930
47931MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z)
47932{
47933 if (pListener == NULL) {
47934 return;
47935 }
47936
47937 pListener->config.worldUp = ma_vec3f_init_3f(x, y, z);
47938}
47939
47941{
47942 if (pListener == NULL) {
47943 return ma_vec3f_init_3f(0, 1, 0);
47944 }
47945
47946 return pListener->config.worldUp;
47947}
47948
47950{
47951 if (pListener == NULL) {
47952 return;
47953 }
47954
47955 pListener->isEnabled = isEnabled;
47956}
47957
47959{
47960 if (pListener == NULL) {
47961 return MA_FALSE;
47962 }
47963
47964 return pListener->isEnabled;
47965}
47966
47967
47968
47969
47971{
47972 ma_spatializer_config config;
47973
47974 MA_ZERO_OBJECT(&config);
47975 config.channelsIn = channelsIn;
47976 config.channelsOut = channelsOut;
47977 config.pChannelMapIn = NULL;
47981 config.minGain = 0;
47982 config.maxGain = 1;
47983 config.minDistance = 1;
47984 config.maxDistance = MA_FLT_MAX;
47985 config.rolloff = 1;
47986 config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */
47987 config.coneOuterAngleInRadians = 6.283185f; /* 360 degress. */
47988 config.coneOuterGain = 0.0f;
47989 config.dopplerFactor = 1;
47991 config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */
47992
47993 return config;
47994}
47995
47996
47997static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig)
47998{
47999 MA_ASSERT(pConfig != NULL);
48000 return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames);
48001}
48002
48003static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig)
48004{
48005 MA_ASSERT(pConfig != NULL);
48006
48007 if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
48008 return MA_INVALID_ARGS;
48009 }
48010
48011 return MA_SUCCESS;
48012}
48013
48014typedef struct
48015{
48016 size_t sizeInBytes;
48017 size_t channelMapInOffset;
48018 size_t newChannelGainsOffset;
48019 size_t gainerOffset;
48020} ma_spatializer_heap_layout;
48021
48022static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout)
48023{
48024 ma_result result;
48025
48026 MA_ASSERT(pHeapLayout != NULL);
48027
48028 MA_ZERO_OBJECT(pHeapLayout);
48029
48030 if (pConfig == NULL) {
48031 return MA_INVALID_ARGS;
48032 }
48033
48034 result = ma_spatializer_validate_config(pConfig);
48035 if (result != MA_SUCCESS) {
48036 return result;
48037 }
48038
48039 pHeapLayout->sizeInBytes = 0;
48040
48041 /* Channel map. */
48042 pHeapLayout->channelMapInOffset = MA_SIZE_MAX; /* <-- MA_SIZE_MAX indicates no allocation necessary. */
48043 if (pConfig->pChannelMapIn != NULL) {
48044 pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes;
48045 pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn);
48046 }
48047
48048 /* New channel gains for output. */
48049 pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes;
48050 pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut);
48051
48052 /* Gainer. */
48053 {
48054 size_t gainerHeapSizeInBytes;
48055 ma_gainer_config gainerConfig;
48056
48057 gainerConfig = ma_spatializer_gainer_config_init(pConfig);
48058
48059 result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes);
48060 if (result != MA_SUCCESS) {
48061 return result;
48062 }
48063
48064 pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes;
48065 pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes);
48066 }
48067
48068 return MA_SUCCESS;
48069}
48070
48071MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes)
48072{
48073 ma_result result;
48074 ma_spatializer_heap_layout heapLayout;
48075
48076 if (pHeapSizeInBytes == NULL) {
48077 return MA_INVALID_ARGS;
48078 }
48079
48080 *pHeapSizeInBytes = 0; /* Safety. */
48081
48082 result = ma_spatializer_get_heap_layout(pConfig, &heapLayout);
48083 if (result != MA_SUCCESS) {
48084 return result;
48085 }
48086
48087 *pHeapSizeInBytes = heapLayout.sizeInBytes;
48088
48089 return MA_SUCCESS;
48090}
48091
48092
48094{
48095 ma_result result;
48096 ma_spatializer_heap_layout heapLayout;
48097 ma_gainer_config gainerConfig;
48098
48099 if (pSpatializer == NULL) {
48100 return MA_INVALID_ARGS;
48101 }
48102
48103 MA_ZERO_OBJECT(pSpatializer);
48104
48105 if (pConfig == NULL || pHeap == NULL) {
48106 return MA_INVALID_ARGS;
48107 }
48108
48109 result = ma_spatializer_get_heap_layout(pConfig, &heapLayout);
48110 if (result != MA_SUCCESS) {
48111 return result;
48112 }
48113
48114 pSpatializer->_pHeap = pHeap;
48115 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
48116
48117 pSpatializer->channelsIn = pConfig->channelsIn;
48118 pSpatializer->channelsOut = pConfig->channelsOut;
48119 pSpatializer->attenuationModel = pConfig->attenuationModel;
48120 pSpatializer->positioning = pConfig->positioning;
48121 pSpatializer->handedness = pConfig->handedness;
48122 pSpatializer->minGain = pConfig->minGain;
48123 pSpatializer->maxGain = pConfig->maxGain;
48124 pSpatializer->minDistance = pConfig->minDistance;
48125 pSpatializer->maxDistance = pConfig->maxDistance;
48126 pSpatializer->rolloff = pConfig->rolloff;
48127 pSpatializer->coneInnerAngleInRadians = pConfig->coneInnerAngleInRadians;
48128 pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians;
48129 pSpatializer->coneOuterGain = pConfig->coneOuterGain;
48130 pSpatializer->dopplerFactor = pConfig->dopplerFactor;
48132 pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames;
48133 pSpatializer->position = ma_vec3f_init_3f(0, 0, 0);
48134 pSpatializer->direction = ma_vec3f_init_3f(0, 0, -1);
48135 pSpatializer->velocity = ma_vec3f_init_3f(0, 0, 0);
48136 pSpatializer->dopplerPitch = 1;
48137
48138 /* Swap the forward direction if we're left handed (it was initialized based on right handed). */
48139 if (pSpatializer->handedness == ma_handedness_left) {
48140 pSpatializer->direction = ma_vec3f_neg(pSpatializer->direction);
48141 }
48142
48143 /* Channel map. This will be on the heap. */
48144 if (pConfig->pChannelMapIn != NULL) {
48145 pSpatializer->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset);
48146 ma_channel_map_copy_or_default(pSpatializer->pChannelMapIn, pSpatializer->channelsIn, pConfig->pChannelMapIn, pSpatializer->channelsIn);
48147 }
48148
48149 /* New channel gains for output channels. */
48150 pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset);
48151
48152 /* Gainer. */
48153 gainerConfig = ma_spatializer_gainer_config_init(pConfig);
48154
48155 result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer);
48156 if (result != MA_SUCCESS) {
48157 return result; /* Failed to initialize the gainer. */
48158 }
48159
48160 return MA_SUCCESS;
48161}
48162
48163MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer)
48164{
48165 ma_result result;
48166 size_t heapSizeInBytes;
48167 void* pHeap;
48168
48169 /* We'll need a heap allocation to retrieve the size. */
48170 result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes);
48171 if (result != MA_SUCCESS) {
48172 return result;
48173 }
48174
48175 if (heapSizeInBytes > 0) {
48176 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
48177 if (pHeap == NULL) {
48178 return MA_OUT_OF_MEMORY;
48179 }
48180 } else {
48181 pHeap = NULL;
48182 }
48183
48184 result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer);
48185 if (result != MA_SUCCESS) {
48186 ma_free(pHeap, pAllocationCallbacks);
48187 return result;
48188 }
48189
48190 pSpatializer->_ownsHeap = MA_TRUE;
48191 return MA_SUCCESS;
48192}
48193
48194MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks)
48195{
48196 if (pSpatializer == NULL) {
48197 return;
48198 }
48199
48200 ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks);
48201
48202 if (pSpatializer->_ownsHeap) {
48203 ma_free(pSpatializer->_pHeap, pAllocationCallbacks);
48204 }
48205}
48206
48207static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain)
48208{
48209 /*
48210 Angular attenuation.
48211
48212 Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure
48213 this out for ourselves at the expense of possibly being inconsistent with other implementations.
48214
48215 To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We
48216 just need to get the direction from the source to the listener and then do a dot product against that and the
48217 direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer
48218 angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than
48219 the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain.
48220 */
48221 if (coneInnerAngleInRadians < 6.283185f) {
48222 float angularGain = 1;
48223 float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f);
48224 float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f);
48225 float d;
48226
48227 d = ma_vec3f_dot(dirA, dirB);
48228
48229 if (d > cutoffInner) {
48230 /* It's inside the inner angle. */
48231 angularGain = 1;
48232 } else {
48233 /* It's outside the inner angle. */
48234 if (d > cutoffOuter) {
48235 /* It's between the inner and outer angle. We need to linearly interpolate between 1 and coneOuterGain. */
48236 angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter));
48237 } else {
48238 /* It's outside the outer angle. */
48239 angularGain = coneOuterGain;
48240 }
48241 }
48242
48243 /*printf("d = %f; cutoffInner = %f; cutoffOuter = %f; angularGain = %f\n", d, cutoffInner, cutoffOuter, angularGain);*/
48244 return angularGain;
48245 } else {
48246 /* Inner angle is 360 degrees so no need to do any attenuation. */
48247 return 1;
48248 }
48249}
48250
48251MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
48252{
48253 ma_channel* pChannelMapIn = pSpatializer->pChannelMapIn;
48254 ma_channel* pChannelMapOut = pListener->config.pChannelMapOut;
48255
48256 if (pSpatializer == NULL) {
48257 return MA_INVALID_ARGS;
48258 }
48259
48260 /* If we're not spatializing we need to run an optimized path. */
48261 if (c89atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) {
48262 if (ma_spatializer_listener_is_enabled(pListener)) {
48263 /* No attenuation is required, but we'll need to do some channel conversion. */
48264 if (pSpatializer->channelsIn == pSpatializer->channelsOut) {
48265 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->channelsIn);
48266 } else {
48267 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. */
48268 }
48269 } else {
48270 /* The listener is disabled. Output silence. */
48271 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut);
48272 }
48273
48274 /*
48275 We're not doing attenuation so don't bother with doppler for now. I'm not sure if this is
48276 the correct thinking so might need to review this later.
48277 */
48278 pSpatializer->dopplerPitch = 1;
48279 } else {
48280 /*
48281 Let's first determine which listener the sound is closest to. Need to keep in mind that we
48282 might not have a world or any listeners, in which case we just spatializer based on the
48283 listener being positioned at the origin (0, 0, 0).
48284 */
48285 ma_vec3f relativePosNormalized;
48286 ma_vec3f relativePos; /* The position relative to the listener. */
48287 ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */
48288 ma_vec3f listenerVel; /* The volocity of the listener. For doppler pitch calculation. */
48289 float speedOfSound;
48290 float distance = 0;
48291 float gain = 1;
48292 ma_uint32 iChannel;
48293 const ma_uint32 channelsOut = pSpatializer->channelsOut;
48294 const ma_uint32 channelsIn = pSpatializer->channelsIn;
48295 float minDistance = ma_spatializer_get_min_distance(pSpatializer);
48296 float maxDistance = ma_spatializer_get_max_distance(pSpatializer);
48297 float rolloff = ma_spatializer_get_rolloff(pSpatializer);
48298 float dopplerFactor = ma_spatializer_get_doppler_factor(pSpatializer);
48299
48300 /*
48301 We'll need the listener velocity for doppler pitch calculations. The speed of sound is
48302 defined by the listener, so we'll grab that here too.
48303 */
48304 if (pListener != NULL) {
48305 listenerVel = pListener->velocity;
48306 speedOfSound = pListener->config.speedOfSound;
48307 } else {
48308 listenerVel = ma_vec3f_init_3f(0, 0, 0);
48309 speedOfSound = MA_DEFAULT_SPEED_OF_SOUND;
48310 }
48311
48312 if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) {
48313 /* There's no listener or we're using relative positioning. */
48314 relativePos = pSpatializer->position;
48315 relativeDir = pSpatializer->direction;
48316 } else {
48317 /*
48318 We've found a listener and we're using absolute positioning. We need to transform the
48319 sound's position and direction so that it's relative to listener. Later on we'll use
48320 this for determining the factors to apply to each channel to apply the panning effect.
48321 */
48322 ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir);
48323 }
48324
48325 distance = ma_vec3f_len(relativePos);
48326
48327 /* We've gathered the data, so now we can apply some spatialization. */
48328 switch (ma_spatializer_get_attenuation_model(pSpatializer)) {
48330 {
48331 gain = ma_attenuation_inverse(distance, minDistance, maxDistance, rolloff);
48332 } break;
48334 {
48335 gain = ma_attenuation_linear(distance, minDistance, maxDistance, rolloff);
48336 } break;
48338 {
48339 gain = ma_attenuation_exponential(distance, minDistance, maxDistance, rolloff);
48340 } break;
48342 default:
48343 {
48344 gain = 1;
48345 } break;
48346 }
48347
48348 /* Normalize the position. */
48349 if (distance > 0.001f) {
48350 float distanceInv = 1/distance;
48351 relativePosNormalized = relativePos;
48352 relativePosNormalized.x *= distanceInv;
48353 relativePosNormalized.y *= distanceInv;
48354 relativePosNormalized.z *= distanceInv;
48355 } else {
48356 distance = 0;
48357 relativePosNormalized = ma_vec3f_init_3f(0, 0, 0);
48358 }
48359
48360 /*
48361 Angular attenuation.
48362
48363 Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure
48364 this out for ourselves at the expense of possibly being inconsistent with other implementations.
48365
48366 To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We
48367 just need to get the direction from the source to the listener and then do a dot product against that and the
48368 direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer
48369 angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than
48370 the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain.
48371 */
48372 if (distance > 0) {
48373 /* Source anglular gain. */
48374 float spatializerConeInnerAngle;
48375 float spatializerConeOuterAngle;
48376 float spatializerConeOuterGain;
48377 ma_spatializer_get_cone(pSpatializer, &spatializerConeInnerAngle, &spatializerConeOuterAngle, &spatializerConeOuterGain);
48378
48379 gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), spatializerConeInnerAngle, spatializerConeOuterAngle, spatializerConeOuterGain);
48380
48381 /*
48382 We're supporting angular gain on the listener as well for those who want to reduce the volume of sounds that
48383 are positioned behind the listener. On default settings, this will have no effect.
48384 */
48385 if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) {
48386 ma_vec3f listenerDirection;
48387 float listenerInnerAngle;
48388 float listenerOuterAngle;
48389 float listenerOuterGain;
48390
48391 if (pListener->config.handedness == ma_handedness_right) {
48392 listenerDirection = ma_vec3f_init_3f(0, 0, -1);
48393 } else {
48394 listenerDirection = ma_vec3f_init_3f(0, 0, +1);
48395 }
48396
48397 listenerInnerAngle = pListener->config.coneInnerAngleInRadians;
48398 listenerOuterAngle = pListener->config.coneOuterAngleInRadians;
48399 listenerOuterGain = pListener->config.coneOuterGain;
48400
48401 gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain);
48402 }
48403 } else {
48404 /* The sound is right on top of the listener. Don't do any angular attenuation. */
48405 }
48406
48407
48408 /* Clamp the gain. */
48409 gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer));
48410
48411 /*
48412 Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for
48413 when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the
48414 gain to the final output.
48415 */
48416 /*printf("distance=%f; gain=%f\n", distance, gain);*/
48417
48418 /* We must have a valid channel map here to ensure we spatialize properly. */
48419 MA_ASSERT(pChannelMapOut != NULL);
48420
48421 /*
48422 We're not converting to mono so we'll want to apply some panning. This is where the feeling of something being
48423 to the left, right, infront or behind the listener is calculated. I'm just using a basic model here. Note that
48424 the code below is not based on any specific algorithm. I'm just implementing this off the top of my head and
48425 seeing how it goes. There might be better ways to do this.
48426
48427 To determine the direction of the sound relative to a speaker I'm using dot products. Each speaker is given a
48428 direction. For example, the left channel in a stereo system will be -1 on the X axis and the right channel will
48429 be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized
48430 position of the sound.
48431 */
48432 for (iChannel = 0; iChannel < channelsOut; iChannel += 1) {
48433 pSpatializer->pNewChannelGainsOut[iChannel] = gain;
48434 }
48435
48436 /*
48437 Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore
48438 the whole section of code here because we need to update some internal spatialization state.
48439 */
48440 if (ma_spatializer_listener_is_enabled(pListener)) {
48441 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);
48442 } else {
48443 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut);
48444 }
48445
48446 /*
48447 Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's
48448 relation to the direction of the channel.
48449 */
48450 if (distance > 0) {
48451 ma_vec3f unitPos = relativePos;
48452 float distanceInv = 1/distance;
48453 unitPos.x *= distanceInv;
48454 unitPos.y *= distanceInv;
48455 unitPos.z *= distanceInv;
48456
48457 for (iChannel = 0; iChannel < channelsOut; iChannel += 1) {
48458 ma_channel channelOut;
48459 float d;
48460 float dMin;
48461
48462 channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel);
48463 if (ma_is_spatial_channel_position(channelOut)) {
48464 d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), ma_spatializer_get_directional_attenuation_factor(pSpatializer));
48465 } else {
48466 d = 1; /* It's not a spatial channel so there's no real notion of direction. */
48467 }
48468
48469 /*
48470 In my testing, if the panning effect is too aggressive it makes spatialization feel uncomfortable.
48471 The "dMin" variable below is used to control the aggressiveness of the panning effect. When set to
48472 0, panning will be most extreme and any sounds that are positioned on the opposite side of the
48473 speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it
48474 doesn't even remotely represent the real world at all because sounds that come from your right side
48475 are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at
48476 all, which is also not ideal. By setting it to something greater than 0, the spatialization effect
48477 becomes much less dramatic and a lot more bearable.
48478
48479 Summary: 0 = more extreme panning; 1 = no panning.
48480 */
48481 dMin = 0.2f; /* TODO: Consider making this configurable. */
48482
48483 /*
48484 At this point, "d" will be positive if the sound is on the same side as the channel and negative if
48485 it's on the opposite side. It will be in the range of -1..1. There's two ways I can think of to
48486 calculate a panning value. The first is to simply convert it to 0..1, however this has a problem
48487 which I'm not entirely happy with. Considering a stereo system, when a sound is positioned right
48488 in front of the listener it'll result in each speaker getting a gain of 0.5. I don't know if I like
48489 the idea of having a scaling factor of 0.5 being applied to a sound when it's sitting right in front
48490 of the listener. I would intuitively expect that to be played at full volume, or close to it.
48491
48492 The second idea I think of is to only apply a reduction in gain when the sound is on the opposite
48493 side of the speaker. That is, reduce the gain only when the dot product is negative. The problem
48494 with this is that there will not be any attenuation as the sound sweeps around the 180 degrees
48495 where the dot product is positive. The idea with this option is that you leave the gain at 1 when
48496 the sound is being played on the same side as the speaker and then you just reduce the volume when
48497 the sound is on the other side.
48498
48499 The summarize, I think the first option should give a better sense of spatialization, but the second
48500 option is better for preserving the sound's power.
48501
48502 UPDATE: In my testing, I find the first option to sound better. You can feel the sense of space a
48503 bit better, but you can also hear the reduction in volume when it's right in front.
48504 */
48505 #if 1
48506 {
48507 /*
48508 Scale the dot product from -1..1 to 0..1. Will result in a sound directly in front losing power
48509 by being played at 0.5 gain.
48510 */
48511 d = (d + 1) * 0.5f; /* -1..1 to 0..1 */
48512 d = ma_max(d, dMin);
48513 pSpatializer->pNewChannelGainsOut[iChannel] *= d;
48514 }
48515 #else
48516 {
48517 /*
48518 Only reduce the volume of the sound if it's on the opposite side. This path keeps the volume more
48519 consistent, but comes at the expense of a worse sense of space and positioning.
48520 */
48521 if (d < 0) {
48522 d += 1; /* Move into the positive range. */
48523 d = ma_max(d, dMin);
48524 channelGainsOut[iChannel] *= d;
48525 }
48526 }
48527 #endif
48528 }
48529 } else {
48530 /* Assume the sound is right on top of us. Don't do any panning. */
48531 }
48532
48533 /* 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. */
48534 ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut);
48535 ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount);
48536
48537 /*
48538 Before leaving we'll want to update our doppler pitch so that the caller can apply some
48539 pitch shifting if they desire. Note that we need to negate the relative position here
48540 because the doppler calculation needs to be source-to-listener, but ours is listener-to-
48541 source.
48542 */
48543 if (dopplerFactor > 0) {
48544 pSpatializer->dopplerPitch = ma_doppler_pitch(ma_vec3f_sub(pListener->position, pSpatializer->position), pSpatializer->velocity, listenerVel, speedOfSound, dopplerFactor);
48545 } else {
48546 pSpatializer->dopplerPitch = 1;
48547 }
48548 }
48549
48550 return MA_SUCCESS;
48551}
48552
48554{
48555 if (pSpatializer == NULL) {
48556 return 0;
48557 }
48558
48559 return pSpatializer->channelsIn;
48560}
48561
48563{
48564 if (pSpatializer == NULL) {
48565 return 0;
48566 }
48567
48568 return pSpatializer->channelsOut;
48569}
48570
48572{
48573 if (pSpatializer == NULL) {
48574 return;
48575 }
48576
48577 c89atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel);
48578}
48579
48581{
48582 if (pSpatializer == NULL) {
48584 }
48585
48586 return (ma_attenuation_model)c89atomic_load_i32(&pSpatializer->attenuationModel);
48587}
48588
48590{
48591 if (pSpatializer == NULL) {
48592 return;
48593 }
48594
48595 c89atomic_exchange_i32(&pSpatializer->positioning, positioning);
48596}
48597
48599{
48600 if (pSpatializer == NULL) {
48602 }
48603
48604 return (ma_positioning)c89atomic_load_i32(&pSpatializer->positioning);
48605}
48606
48607MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff)
48608{
48609 if (pSpatializer == NULL) {
48610 return;
48611 }
48612
48613 c89atomic_exchange_f32(&pSpatializer->rolloff, rolloff);
48614}
48615
48616MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer)
48617{
48618 if (pSpatializer == NULL) {
48619 return 0;
48620 }
48621
48622 return c89atomic_load_f32(&pSpatializer->rolloff);
48623}
48624
48625MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain)
48626{
48627 if (pSpatializer == NULL) {
48628 return;
48629 }
48630
48631 c89atomic_exchange_f32(&pSpatializer->minGain, minGain);
48632}
48633
48634MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer)
48635{
48636 if (pSpatializer == NULL) {
48637 return 0;
48638 }
48639
48640 return c89atomic_load_f32(&pSpatializer->minGain);
48641}
48642
48643MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain)
48644{
48645 if (pSpatializer == NULL) {
48646 return;
48647 }
48648
48649 c89atomic_exchange_f32(&pSpatializer->maxGain, maxGain);
48650}
48651
48652MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer)
48653{
48654 if (pSpatializer == NULL) {
48655 return 0;
48656 }
48657
48658 return c89atomic_load_f32(&pSpatializer->maxGain);
48659}
48660
48661MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance)
48662{
48663 if (pSpatializer == NULL) {
48664 return;
48665 }
48666
48667 c89atomic_exchange_f32(&pSpatializer->minDistance, minDistance);
48668}
48669
48670MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer)
48671{
48672 if (pSpatializer == NULL) {
48673 return 0;
48674 }
48675
48676 return c89atomic_load_f32(&pSpatializer->minDistance);
48677}
48678
48679MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance)
48680{
48681 if (pSpatializer == NULL) {
48682 return;
48683 }
48684
48685 c89atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance);
48686}
48687
48688MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer)
48689{
48690 if (pSpatializer == NULL) {
48691 return 0;
48692 }
48693
48694 return c89atomic_load_f32(&pSpatializer->maxDistance);
48695}
48696
48697MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
48698{
48699 if (pSpatializer == NULL) {
48700 return;
48701 }
48702
48703 c89atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians);
48704 c89atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians);
48705 c89atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain);
48706}
48707
48708MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
48709{
48710 if (pSpatializer == NULL) {
48711 return;
48712 }
48713
48714 if (pInnerAngleInRadians != NULL) {
48715 *pInnerAngleInRadians = c89atomic_load_f32(&pSpatializer->coneInnerAngleInRadians);
48716 }
48717
48718 if (pOuterAngleInRadians != NULL) {
48719 *pOuterAngleInRadians = c89atomic_load_f32(&pSpatializer->coneOuterAngleInRadians);
48720 }
48721
48722 if (pOuterGain != NULL) {
48723 *pOuterGain = c89atomic_load_f32(&pSpatializer->coneOuterGain);
48724 }
48725}
48726
48727MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor)
48728{
48729 if (pSpatializer == NULL) {
48730 return;
48731 }
48732
48733 c89atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor);
48734}
48735
48737{
48738 if (pSpatializer == NULL) {
48739 return 1;
48740 }
48741
48742 return c89atomic_load_f32(&pSpatializer->dopplerFactor);
48743}
48744
48745MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor)
48746{
48747 if (pSpatializer == NULL) {
48748 return;
48749 }
48750
48751 c89atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor);
48752}
48753
48755{
48756 if (pSpatializer == NULL) {
48757 return 1;
48758 }
48759
48760 return c89atomic_load_f32(&pSpatializer->directionalAttenuationFactor);
48761}
48762
48763MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z)
48764{
48765 if (pSpatializer == NULL) {
48766 return;
48767 }
48768
48769 pSpatializer->position = ma_vec3f_init_3f(x, y, z);
48770}
48771
48773{
48774 if (pSpatializer == NULL) {
48775 return ma_vec3f_init_3f(0, 0, 0);
48776 }
48777
48778 return pSpatializer->position;
48779}
48780
48781MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z)
48782{
48783 if (pSpatializer == NULL) {
48784 return;
48785 }
48786
48787 pSpatializer->direction = ma_vec3f_init_3f(x, y, z);
48788}
48789
48791{
48792 if (pSpatializer == NULL) {
48793 return ma_vec3f_init_3f(0, 0, -1);
48794 }
48795
48796 return pSpatializer->direction;
48797}
48798
48799MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z)
48800{
48801 if (pSpatializer == NULL) {
48802 return;
48803 }
48804
48805 pSpatializer->velocity = ma_vec3f_init_3f(x, y, z);
48806}
48807
48809{
48810 if (pSpatializer == NULL) {
48811 return ma_vec3f_init_3f(0, 0, 0);
48812 }
48813
48814 return pSpatializer->velocity;
48815}
48816
48817MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir)
48818{
48819 if (pRelativePos != NULL) {
48820 pRelativePos->x = 0;
48821 pRelativePos->y = 0;
48822 pRelativePos->z = 0;
48823 }
48824
48825 if (pRelativeDir != NULL) {
48826 pRelativeDir->x = 0;
48827 pRelativeDir->y = 0;
48828 pRelativeDir->z = -1;
48829 }
48830
48831 if (pSpatializer == NULL) {
48832 return;
48833 }
48834
48835 if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) {
48836 /* There's no listener or we're using relative positioning. */
48837 if (pRelativePos != NULL) {
48838 *pRelativePos = pSpatializer->position;
48839 }
48840 if (pRelativeDir != NULL) {
48841 *pRelativeDir = pSpatializer->direction;
48842 }
48843 } else {
48844 ma_vec3f v;
48845 ma_vec3f axisX;
48846 ma_vec3f axisY;
48847 ma_vec3f axisZ;
48848 float m[4][4];
48849
48850 /*
48851 We need to calcualte the right vector from our forward and up vectors. This is done with
48852 a cross product.
48853 */
48854 axisZ = ma_vec3f_normalize(pListener->direction); /* Normalization required here because we can't trust the caller. */
48855 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. */
48856
48857 /*
48858 The calculation of axisX above can result in a zero-length vector if the listener is
48859 looking straight up on the Y axis. We'll need to fall back to a +X in this case so that
48860 the calculations below don't fall apart. This is where a quaternion based listener and
48861 sound orientation would come in handy.
48862 */
48863 if (ma_vec3f_len2(axisX) == 0) {
48864 axisX = ma_vec3f_init_3f(1, 0, 0);
48865 }
48866
48867 axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */
48868
48869 /*
48870 We need to swap the X axis if we're left handed because otherwise the cross product above
48871 will have resulted in it pointing in the wrong direction (right handed was assumed in the
48872 cross products above).
48873 */
48874 if (pListener->config.handedness == ma_handedness_left) {
48875 axisX = ma_vec3f_neg(axisX);
48876 }
48877
48878 /* Lookat. */
48879 m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, pListener->position);
48880 m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, pListener->position);
48881 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), pListener->position);
48882 m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1;
48883
48884 /*
48885 Multiply the lookat matrix by the spatializer position to transform it to listener
48886 space. This allows calculations to work based on the sound being relative to the
48887 origin which makes things simpler.
48888 */
48889 if (pRelativePos != NULL) {
48890 v = pSpatializer->position;
48891 pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1;
48892 pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1;
48893 pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1;
48894 }
48895
48896 /*
48897 The direction of the sound needs to also be transformed so that it's relative to the
48898 rotation of the listener.
48899 */
48900 if (pRelativeDir != NULL) {
48901 v = pSpatializer->direction;
48902 pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z;
48903 pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z;
48904 pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z;
48905 }
48906 }
48907}
48908
48909
48910
48911
48912
48918{
48920 MA_ZERO_OBJECT(&config);
48921 config.format = format;
48922 config.channels = channels;
48923 config.sampleRateIn = sampleRateIn;
48924 config.sampleRateOut = sampleRateOut;
48925 config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
48926 config.lpfNyquistFactor = 1;
48927
48928 return config;
48929}
48930
48931
48932typedef struct
48933{
48934 size_t sizeInBytes;
48935 size_t x0Offset;
48936 size_t x1Offset;
48937 size_t lpfOffset;
48938} ma_linear_resampler_heap_layout;
48939
48940
48941static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut)
48942{
48943 /*
48944 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
48945 be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate.
48946 */
48947 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. */
48948 ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut;
48949
48950 pResampler->inTimeFrac =
48951 (oldRateTimeWhole * newSampleRateOut) +
48952 ((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut);
48953
48954 /* Make sure the fractional part is less than the output sample rate. */
48955 pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut;
48956 pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut;
48957}
48958
48959static 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)
48960{
48961 ma_result result;
48962 ma_uint32 gcf;
48963 ma_uint32 lpfSampleRate;
48964 double lpfCutoffFrequency;
48965 ma_lpf_config lpfConfig;
48966 ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */
48967
48968 if (pResampler == NULL) {
48969 return MA_INVALID_ARGS;
48970 }
48971
48972 if (sampleRateIn == 0 || sampleRateOut == 0) {
48973 return MA_INVALID_ARGS;
48974 }
48975
48976 oldSampleRateOut = pResampler->config.sampleRateOut;
48977
48978 pResampler->config.sampleRateIn = sampleRateIn;
48979 pResampler->config.sampleRateOut = sampleRateOut;
48980
48981 /* Simplify the sample rate. */
48982 gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut);
48983 pResampler->config.sampleRateIn /= gcf;
48984 pResampler->config.sampleRateOut /= gcf;
48985
48986 /* Always initialize the low-pass filter, even when the order is 0. */
48987 if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) {
48988 return MA_INVALID_ARGS;
48989 }
48990
48991 lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut));
48992 lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor);
48993
48994 lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder);
48995
48996 /*
48997 If the resampler is alreay initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames
48998 getting cleared. Instead we re-initialize the filter which will maintain any cached frames.
48999 */
49000 if (isResamplerAlreadyInitialized) {
49001 result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf);
49002 } else {
49003 result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf);
49004 }
49005
49006 if (result != MA_SUCCESS) {
49007 return result;
49008 }
49009
49010
49011 pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut;
49012 pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut;
49013
49014 /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */
49015 ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut);
49016
49017 return MA_SUCCESS;
49018}
49019
49020static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout)
49021{
49022 MA_ASSERT(pHeapLayout != NULL);
49023
49024 MA_ZERO_OBJECT(pHeapLayout);
49025
49026 if (pConfig == NULL) {
49027 return MA_INVALID_ARGS;
49028 }
49029
49030 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
49031 return MA_INVALID_ARGS;
49032 }
49033
49034 if (pConfig->channels == 0) {
49035 return MA_INVALID_ARGS;
49036 }
49037
49038 pHeapLayout->sizeInBytes = 0;
49039
49040 /* x0 */
49041 pHeapLayout->x0Offset = pHeapLayout->sizeInBytes;
49042 if (pConfig->format == ma_format_f32) {
49043 pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
49044 } else {
49045 pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels;
49046 }
49047
49048 /* x1 */
49049 pHeapLayout->x1Offset = pHeapLayout->sizeInBytes;
49050 if (pConfig->format == ma_format_f32) {
49051 pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
49052 } else {
49053 pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels;
49054 }
49055
49056 /* LPF */
49057 pHeapLayout->lpfOffset = pHeapLayout->sizeInBytes;
49058 {
49059 ma_result result;
49060 size_t lpfHeapSizeInBytes;
49061 ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder); /* Sample rate and cutoff frequency do not matter. */
49062
49063 result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes);
49064 if (result != MA_SUCCESS) {
49065 return result;
49066 }
49067
49068 pHeapLayout->sizeInBytes += lpfHeapSizeInBytes;
49069 }
49070
49071 /* Make sure allocation size is aligned. */
49072 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
49073
49074 return MA_SUCCESS;
49075}
49076
49077MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes)
49078{
49079 ma_result result;
49080 ma_linear_resampler_heap_layout heapLayout;
49081
49082 if (pHeapSizeInBytes == NULL) {
49083 return MA_INVALID_ARGS;
49084 }
49085
49086 *pHeapSizeInBytes = 0;
49087
49088 result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout);
49089 if (result != MA_SUCCESS) {
49090 return result;
49091 }
49092
49093 *pHeapSizeInBytes = heapLayout.sizeInBytes;
49094
49095 return MA_SUCCESS;
49096}
49097
49099{
49100 ma_result result;
49101 ma_linear_resampler_heap_layout heapLayout;
49102
49103 if (pResampler == NULL) {
49104 return MA_INVALID_ARGS;
49105 }
49106
49107 MA_ZERO_OBJECT(pResampler);
49108
49109 result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout);
49110 if (result != MA_SUCCESS) {
49111 return result;
49112 }
49113
49114 pResampler->config = *pConfig;
49115
49116 pResampler->_pHeap = pHeap;
49117 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
49118
49119 if (pConfig->format == ma_format_f32) {
49120 pResampler->x0.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x0Offset);
49121 pResampler->x1.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x1Offset);
49122 } else {
49123 pResampler->x0.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x0Offset);
49124 pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset);
49125 }
49126
49127 /* Setting the rate will set up the filter and time advances for us. */
49128 result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE);
49129 if (result != MA_SUCCESS) {
49130 return result;
49131 }
49132
49133 pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */
49134 pResampler->inTimeFrac = 0;
49135
49136 return MA_SUCCESS;
49137}
49138
49140{
49141 ma_result result;
49142 size_t heapSizeInBytes;
49143 void* pHeap;
49144
49145 result = ma_linear_resampler_get_heap_size(pConfig, &heapSizeInBytes);
49146 if (result != MA_SUCCESS) {
49147 return result;
49148 }
49149
49150 if (heapSizeInBytes > 0) {
49151 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
49152 if (pHeap == NULL) {
49153 return MA_OUT_OF_MEMORY;
49154 }
49155 } else {
49156 pHeap = NULL;
49157 }
49158
49159 result = ma_linear_resampler_init_preallocated(pConfig, pHeap, pResampler);
49160 if (result != MA_SUCCESS) {
49161 ma_free(pHeap, pAllocationCallbacks);
49162 return result;
49163 }
49164
49165 pResampler->_ownsHeap = MA_TRUE;
49166 return MA_SUCCESS;
49167}
49168
49169MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks)
49170{
49171 if (pResampler == NULL) {
49172 return;
49173 }
49174
49175 ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks);
49176
49177 if (pResampler->_ownsHeap) {
49178 ma_free(pResampler->_pHeap, pAllocationCallbacks);
49179 }
49180}
49181
49182static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift)
49183{
49184 ma_int32 b;
49185 ma_int32 c;
49186 ma_int32 r;
49187
49188 MA_ASSERT(a <= (1<<shift));
49189
49190 b = x * ((1<<shift) - a);
49191 c = y * a;
49192 r = b + c;
49193
49194 return (ma_int16)(r >> shift);
49195}
49196
49197static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut)
49198{
49199 ma_uint32 c;
49200 ma_uint32 a;
49201 const ma_uint32 channels = pResampler->config.channels;
49202 const ma_uint32 shift = 12;
49203
49204 MA_ASSERT(pResampler != NULL);
49205 MA_ASSERT(pFrameOut != NULL);
49206
49207 a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut;
49208
49209 MA_ASSUME(channels > 0);
49210 for (c = 0; c < channels; c += 1) {
49211 ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift);
49212 pFrameOut[c] = s;
49213 }
49214}
49215
49216
49217static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut)
49218{
49219 ma_uint32 c;
49220 float a;
49221 const ma_uint32 channels = pResampler->config.channels;
49222
49223 MA_ASSERT(pResampler != NULL);
49224 MA_ASSERT(pFrameOut != NULL);
49225
49226 a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut;
49227
49228 MA_ASSUME(channels > 0);
49229 for (c = 0; c < channels; c += 1) {
49230 float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a);
49231 pFrameOut[c] = s;
49232 }
49233}
49234
49235static 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)
49236{
49237 const ma_int16* pFramesInS16;
49238 /* */ ma_int16* pFramesOutS16;
49239 ma_uint64 frameCountIn;
49240 ma_uint64 frameCountOut;
49241 ma_uint64 framesProcessedIn;
49242 ma_uint64 framesProcessedOut;
49243
49244 MA_ASSERT(pResampler != NULL);
49245 MA_ASSERT(pFrameCountIn != NULL);
49246 MA_ASSERT(pFrameCountOut != NULL);
49247
49248 pFramesInS16 = (const ma_int16*)pFramesIn;
49249 pFramesOutS16 = ( ma_int16*)pFramesOut;
49250 frameCountIn = *pFrameCountIn;
49251 frameCountOut = *pFrameCountOut;
49252 framesProcessedIn = 0;
49253 framesProcessedOut = 0;
49254
49255 while (framesProcessedOut < frameCountOut) {
49256 /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
49257 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
49258 ma_uint32 iChannel;
49259
49260 if (pFramesInS16 != NULL) {
49261 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
49262 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
49263 pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
49264 }
49265 pFramesInS16 += pResampler->config.channels;
49266 } else {
49267 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
49268 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
49269 pResampler->x1.s16[iChannel] = 0;
49270 }
49271 }
49272
49273 /* Filter. */
49274 ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16);
49275
49276 framesProcessedIn += 1;
49277 pResampler->inTimeInt -= 1;
49278 }
49279
49280 if (pResampler->inTimeInt > 0) {
49281 break; /* Ran out of input data. */
49282 }
49283
49284 /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
49285 if (pFramesOutS16 != NULL) {
49286 MA_ASSERT(pResampler->inTimeInt == 0);
49287 ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
49288
49289 pFramesOutS16 += pResampler->config.channels;
49290 }
49291
49292 framesProcessedOut += 1;
49293
49294 /* Advance time forward. */
49295 pResampler->inTimeInt += pResampler->inAdvanceInt;
49296 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
49297 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
49298 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
49299 pResampler->inTimeInt += 1;
49300 }
49301 }
49302
49303 *pFrameCountIn = framesProcessedIn;
49304 *pFrameCountOut = framesProcessedOut;
49305
49306 return MA_SUCCESS;
49307}
49308
49309static 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)
49310{
49311 const ma_int16* pFramesInS16;
49312 /* */ ma_int16* pFramesOutS16;
49313 ma_uint64 frameCountIn;
49314 ma_uint64 frameCountOut;
49315 ma_uint64 framesProcessedIn;
49316 ma_uint64 framesProcessedOut;
49317
49318 MA_ASSERT(pResampler != NULL);
49319 MA_ASSERT(pFrameCountIn != NULL);
49320 MA_ASSERT(pFrameCountOut != NULL);
49321
49322 pFramesInS16 = (const ma_int16*)pFramesIn;
49323 pFramesOutS16 = ( ma_int16*)pFramesOut;
49324 frameCountIn = *pFrameCountIn;
49325 frameCountOut = *pFrameCountOut;
49326 framesProcessedIn = 0;
49327 framesProcessedOut = 0;
49328
49329 while (framesProcessedOut < frameCountOut) {
49330 /* Before interpolating we need to load the buffers. */
49331 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
49332 ma_uint32 iChannel;
49333
49334 if (pFramesInS16 != NULL) {
49335 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
49336 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
49337 pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
49338 }
49339 pFramesInS16 += pResampler->config.channels;
49340 } else {
49341 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
49342 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
49343 pResampler->x1.s16[iChannel] = 0;
49344 }
49345 }
49346
49347 framesProcessedIn += 1;
49348 pResampler->inTimeInt -= 1;
49349 }
49350
49351 if (pResampler->inTimeInt > 0) {
49352 break; /* Ran out of input data. */
49353 }
49354
49355 /* Getting here means the frames have been loaded and we can generate the next output frame. */
49356 if (pFramesOutS16 != NULL) {
49357 MA_ASSERT(pResampler->inTimeInt == 0);
49358 ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
49359
49360 /* Filter. */
49361 ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16);
49362
49363 pFramesOutS16 += pResampler->config.channels;
49364 }
49365
49366 framesProcessedOut += 1;
49367
49368 /* Advance time forward. */
49369 pResampler->inTimeInt += pResampler->inAdvanceInt;
49370 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
49371 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
49372 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
49373 pResampler->inTimeInt += 1;
49374 }
49375 }
49376
49377 *pFrameCountIn = framesProcessedIn;
49378 *pFrameCountOut = framesProcessedOut;
49379
49380 return MA_SUCCESS;
49381}
49382
49383static ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
49384{
49385 MA_ASSERT(pResampler != NULL);
49386
49387 if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
49388 return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
49389 } else {
49390 return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
49391 }
49392}
49393
49394
49395static 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)
49396{
49397 const float* pFramesInF32;
49398 /* */ float* pFramesOutF32;
49399 ma_uint64 frameCountIn;
49400 ma_uint64 frameCountOut;
49401 ma_uint64 framesProcessedIn;
49402 ma_uint64 framesProcessedOut;
49403
49404 MA_ASSERT(pResampler != NULL);
49405 MA_ASSERT(pFrameCountIn != NULL);
49406 MA_ASSERT(pFrameCountOut != NULL);
49407
49408 pFramesInF32 = (const float*)pFramesIn;
49409 pFramesOutF32 = ( float*)pFramesOut;
49410 frameCountIn = *pFrameCountIn;
49411 frameCountOut = *pFrameCountOut;
49412 framesProcessedIn = 0;
49413 framesProcessedOut = 0;
49414
49415 while (framesProcessedOut < frameCountOut) {
49416 /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
49417 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
49418 ma_uint32 iChannel;
49419
49420 if (pFramesInF32 != NULL) {
49421 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
49422 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
49423 pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
49424 }
49425 pFramesInF32 += pResampler->config.channels;
49426 } else {
49427 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
49428 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
49429 pResampler->x1.f32[iChannel] = 0;
49430 }
49431 }
49432
49433 /* Filter. */
49434 ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32);
49435
49436 framesProcessedIn += 1;
49437 pResampler->inTimeInt -= 1;
49438 }
49439
49440 if (pResampler->inTimeInt > 0) {
49441 break; /* Ran out of input data. */
49442 }
49443
49444 /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
49445 if (pFramesOutF32 != NULL) {
49446 MA_ASSERT(pResampler->inTimeInt == 0);
49447 ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
49448
49449 pFramesOutF32 += pResampler->config.channels;
49450 }
49451
49452 framesProcessedOut += 1;
49453
49454 /* Advance time forward. */
49455 pResampler->inTimeInt += pResampler->inAdvanceInt;
49456 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
49457 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
49458 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
49459 pResampler->inTimeInt += 1;
49460 }
49461 }
49462
49463 *pFrameCountIn = framesProcessedIn;
49464 *pFrameCountOut = framesProcessedOut;
49465
49466 return MA_SUCCESS;
49467}
49468
49469static 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)
49470{
49471 const float* pFramesInF32;
49472 /* */ float* pFramesOutF32;
49473 ma_uint64 frameCountIn;
49474 ma_uint64 frameCountOut;
49475 ma_uint64 framesProcessedIn;
49476 ma_uint64 framesProcessedOut;
49477
49478 MA_ASSERT(pResampler != NULL);
49479 MA_ASSERT(pFrameCountIn != NULL);
49480 MA_ASSERT(pFrameCountOut != NULL);
49481
49482 pFramesInF32 = (const float*)pFramesIn;
49483 pFramesOutF32 = ( float*)pFramesOut;
49484 frameCountIn = *pFrameCountIn;
49485 frameCountOut = *pFrameCountOut;
49486 framesProcessedIn = 0;
49487 framesProcessedOut = 0;
49488
49489 while (framesProcessedOut < frameCountOut) {
49490 /* Before interpolating we need to load the buffers. */
49491 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
49492 ma_uint32 iChannel;
49493
49494 if (pFramesInF32 != NULL) {
49495 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
49496 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
49497 pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
49498 }
49499 pFramesInF32 += pResampler->config.channels;
49500 } else {
49501 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
49502 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
49503 pResampler->x1.f32[iChannel] = 0;
49504 }
49505 }
49506
49507 framesProcessedIn += 1;
49508 pResampler->inTimeInt -= 1;
49509 }
49510
49511 if (pResampler->inTimeInt > 0) {
49512 break; /* Ran out of input data. */
49513 }
49514
49515 /* Getting here means the frames have been loaded and we can generate the next output frame. */
49516 if (pFramesOutF32 != NULL) {
49517 MA_ASSERT(pResampler->inTimeInt == 0);
49518 ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
49519
49520 /* Filter. */
49521 ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32);
49522
49523 pFramesOutF32 += pResampler->config.channels;
49524 }
49525
49526 framesProcessedOut += 1;
49527
49528 /* Advance time forward. */
49529 pResampler->inTimeInt += pResampler->inAdvanceInt;
49530 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
49531 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
49532 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
49533 pResampler->inTimeInt += 1;
49534 }
49535 }
49536
49537 *pFrameCountIn = framesProcessedIn;
49538 *pFrameCountOut = framesProcessedOut;
49539
49540 return MA_SUCCESS;
49541}
49542
49543static ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
49544{
49545 MA_ASSERT(pResampler != NULL);
49546
49547 if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
49548 return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
49549 } else {
49550 return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
49551 }
49552}
49553
49554
49555MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
49556{
49557 if (pResampler == NULL) {
49558 return MA_INVALID_ARGS;
49559 }
49560
49561 /* */ if (pResampler->config.format == ma_format_s16) {
49562 return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
49563 } else if (pResampler->config.format == ma_format_f32) {
49564 return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
49565 } else {
49566 /* 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(). */
49567 MA_ASSERT(MA_FALSE);
49568 return MA_INVALID_ARGS;
49569 }
49570}
49571
49572
49574{
49575 return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE);
49576}
49577
49579{
49580 ma_uint32 n;
49581 ma_uint32 d;
49582
49583 if (pResampler == NULL) {
49584 return MA_INVALID_ARGS;
49585 }
49586
49587 if (ratioInOut <= 0) {
49588 return MA_INVALID_ARGS;
49589 }
49590
49591 d = 1000;
49592 n = (ma_uint32)(ratioInOut * d);
49593
49594 if (n == 0) {
49595 return MA_INVALID_ARGS; /* Ratio too small. */
49596 }
49597
49598 MA_ASSERT(n != 0);
49599
49600 return ma_linear_resampler_set_rate(pResampler, n, d);
49601}
49602
49604{
49605 if (pResampler == NULL) {
49606 return 0;
49607 }
49608
49609 return 1 + ma_lpf_get_latency(&pResampler->lpf);
49610}
49611
49613{
49614 if (pResampler == NULL) {
49615 return 0;
49616 }
49617
49618 return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn;
49619}
49620
49622{
49623 ma_uint64 inputFrameCount;
49624
49625 if (pInputFrameCount == NULL) {
49626 return MA_INVALID_ARGS;
49627 }
49628
49629 *pInputFrameCount = 0;
49630
49631 if (pResampler == NULL) {
49632 return MA_INVALID_ARGS;
49633 }
49634
49635 if (outputFrameCount == 0) {
49636 return MA_SUCCESS;
49637 }
49638
49639 /* Any whole input frames are consumed before the first output frame is generated. */
49640 inputFrameCount = pResampler->inTimeInt;
49641 outputFrameCount -= 1;
49642
49643 /* The rest of the output frames can be calculated in constant time. */
49644 inputFrameCount += outputFrameCount * pResampler->inAdvanceInt;
49645 inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut;
49646
49647 *pInputFrameCount = inputFrameCount;
49648
49649 return MA_SUCCESS;
49650}
49651
49653{
49654 ma_uint64 outputFrameCount;
49655 ma_uint64 preliminaryInputFrameCountFromFrac;
49656 ma_uint64 preliminaryInputFrameCount;
49657
49658 if (pOutputFrameCount == NULL) {
49659 return MA_INVALID_ARGS;
49660 }
49661
49662 *pOutputFrameCount = 0;
49663
49664 if (pResampler == NULL) {
49665 return MA_INVALID_ARGS;
49666 }
49667
49668 /*
49669 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
49670 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
49671 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
49672 of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames.
49673 */
49674 outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn;
49675
49676 /*
49677 We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is
49678 used in the logic below to determine whether or not we need to add an extra output frame.
49679 */
49680 preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut;
49681 preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac;
49682
49683 /*
49684 If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greather than
49685 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
49686 to actually process. Otherwise we need to add the extra output frame.
49687 */
49688 if (preliminaryInputFrameCount <= inputFrameCount) {
49689 outputFrameCount += 1;
49690 }
49691
49692 *pOutputFrameCount = outputFrameCount;
49693
49694 return MA_SUCCESS;
49695}
49696
49697
49698/* Linear resampler backend vtable. */
49699static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig)
49700{
49701 ma_linear_resampler_config linearConfig;
49702
49703 linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut);
49704 linearConfig.lpfOrder = pConfig->linear.lpfOrder;
49705
49706 return linearConfig;
49707}
49708
49709static ma_result ma_resampling_backend_get_heap_size__linear(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes)
49710{
49711 ma_linear_resampler_config linearConfig;
49712
49713 (void)pUserData;
49714
49715 linearConfig = ma_resampling_backend_get_config__linear(pConfig);
49716
49717 return ma_linear_resampler_get_heap_size(&linearConfig, pHeapSizeInBytes);
49718}
49719
49720static ma_result ma_resampling_backend_init__linear(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend)
49721{
49722 ma_resampler* pResampler = (ma_resampler*)pUserData;
49723 ma_result result;
49724 ma_linear_resampler_config linearConfig;
49725
49726 (void)pUserData;
49727
49728 linearConfig = ma_resampling_backend_get_config__linear(pConfig);
49729
49730 result = ma_linear_resampler_init_preallocated(&linearConfig, pHeap, &pResampler->state.linear);
49731 if (result != MA_SUCCESS) {
49732 return result;
49733 }
49734
49735 *ppBackend = &pResampler->state.linear;
49736
49737 return MA_SUCCESS;
49738}
49739
49740static void ma_resampling_backend_uninit__linear(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
49741{
49742 (void)pUserData;
49743
49744 ma_linear_resampler_uninit((ma_linear_resampler*)pBackend, pAllocationCallbacks);
49745}
49746
49747static ma_result ma_resampling_backend_process__linear(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
49748{
49749 (void)pUserData;
49750
49751 return ma_linear_resampler_process_pcm_frames((ma_linear_resampler*)pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
49752}
49753
49754static ma_result ma_resampling_backend_set_rate__linear(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
49755{
49756 (void)pUserData;
49757
49758 return ma_linear_resampler_set_rate((ma_linear_resampler*)pBackend, sampleRateIn, sampleRateOut);
49759}
49760
49761static ma_uint64 ma_resampling_backend_get_input_latency__linear(void* pUserData, const ma_resampling_backend* pBackend)
49762{
49763 (void)pUserData;
49764
49766}
49767
49768static ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserData, const ma_resampling_backend* pBackend)
49769{
49770 (void)pUserData;
49771
49773}
49774
49775static ma_result ma_resampling_backend_get_required_input_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
49776{
49777 (void)pUserData;
49778
49779 return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount);
49780}
49781
49782static ma_result ma_resampling_backend_get_expected_output_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
49783{
49784 (void)pUserData;
49785
49786 return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount);
49787}
49788
49789static ma_resampling_backend_vtable g_ma_linear_resampler_vtable =
49790{
49791 ma_resampling_backend_get_heap_size__linear,
49792 ma_resampling_backend_init__linear,
49793 ma_resampling_backend_uninit__linear,
49794 ma_resampling_backend_process__linear,
49795 ma_resampling_backend_set_rate__linear,
49796 ma_resampling_backend_get_input_latency__linear,
49797 ma_resampling_backend_get_output_latency__linear,
49798 ma_resampling_backend_get_required_input_frame_count__linear,
49799 ma_resampling_backend_get_expected_output_frame_count__linear
49800};
49801
49802
49803
49805{
49806 ma_resampler_config config;
49807
49808 MA_ZERO_OBJECT(&config);
49809 config.format = format;
49810 config.channels = channels;
49811 config.sampleRateIn = sampleRateIn;
49812 config.sampleRateOut = sampleRateOut;
49813 config.algorithm = algorithm;
49814
49815 /* Linear. */
49816 config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
49817
49818 return config;
49819}
49820
49821static ma_result ma_resampler_get_vtable(const ma_resampler_config* pConfig, ma_resampler* pResampler, ma_resampling_backend_vtable** ppVTable, void** ppUserData)
49822{
49823 MA_ASSERT(pConfig != NULL);
49824 MA_ASSERT(ppVTable != NULL);
49825 MA_ASSERT(ppUserData != NULL);
49826
49827 /* Safety. */
49828 *ppVTable = NULL;
49829 *ppUserData = NULL;
49830
49831 switch (pConfig->algorithm)
49832 {
49834 {
49835 *ppVTable = &g_ma_linear_resampler_vtable;
49836 *ppUserData = pResampler;
49837 } break;
49838
49840 {
49841 *ppVTable = pConfig->pBackendVTable;
49842 *ppUserData = pConfig->pBackendUserData;
49843 } break;
49844
49845 default: return MA_INVALID_ARGS;
49846 }
49847
49848 return MA_SUCCESS;
49849}
49850
49851MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes)
49852{
49853 ma_result result;
49855 void* pVTableUserData;
49856
49857 if (pHeapSizeInBytes == NULL) {
49858 return MA_INVALID_ARGS;
49859 }
49860
49861 *pHeapSizeInBytes = 0;
49862
49863 if (pConfig == NULL) {
49864 return MA_INVALID_ARGS;
49865 }
49866
49867 result = ma_resampler_get_vtable(pConfig, NULL, &pVTable, &pVTableUserData);
49868 if (result != MA_SUCCESS) {
49869 return result;
49870 }
49871
49872 if (pVTable == NULL || pVTable->onGetHeapSize == NULL) {
49873 return MA_NOT_IMPLEMENTED;
49874 }
49875
49876 result = pVTable->onGetHeapSize(pVTableUserData, pConfig, pHeapSizeInBytes);
49877 if (result != MA_SUCCESS) {
49878 return result;
49879 }
49880
49881 return MA_SUCCESS;
49882}
49883
49884MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler)
49885{
49886 ma_result result;
49887
49888 if (pResampler == NULL) {
49889 return MA_INVALID_ARGS;
49890 }
49891
49892 MA_ZERO_OBJECT(pResampler);
49893
49894 if (pConfig == NULL) {
49895 return MA_INVALID_ARGS;
49896 }
49897
49898 pResampler->_pHeap = pHeap;
49899 pResampler->format = pConfig->format;
49900 pResampler->channels = pConfig->channels;
49901 pResampler->sampleRateIn = pConfig->sampleRateIn;
49902 pResampler->sampleRateOut = pConfig->sampleRateOut;
49903
49904 result = ma_resampler_get_vtable(pConfig, pResampler, &pResampler->pBackendVTable, &pResampler->pBackendUserData);
49905 if (result != MA_SUCCESS) {
49906 return result;
49907 }
49908
49909 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onInit == NULL) {
49910 return MA_NOT_IMPLEMENTED; /* onInit not implemented. */
49911 }
49912
49913 result = pResampler->pBackendVTable->onInit(pResampler->pBackendUserData, pConfig, pHeap, &pResampler->pBackend);
49914 if (result != MA_SUCCESS) {
49915 return result;
49916 }
49917
49918 return MA_SUCCESS;
49919}
49920
49921MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler)
49922{
49923 ma_result result;
49924 size_t heapSizeInBytes;
49925 void* pHeap;
49926
49927 result = ma_resampler_get_heap_size(pConfig, &heapSizeInBytes);
49928 if (result != MA_SUCCESS) {
49929 return result;
49930 }
49931
49932 if (heapSizeInBytes > 0) {
49933 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
49934 if (pHeap == NULL) {
49935 return MA_OUT_OF_MEMORY;
49936 }
49937 } else {
49938 pHeap = NULL;
49939 }
49940
49941 result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler);
49942 if (result != MA_SUCCESS) {
49943 return result;
49944 }
49945
49946 pResampler->_ownsHeap = MA_TRUE;
49947 return MA_SUCCESS;
49948}
49949
49950MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks)
49951{
49952 if (pResampler == NULL) {
49953 return;
49954 }
49955
49956 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onUninit == NULL) {
49957 return;
49958 }
49959
49960 pResampler->pBackendVTable->onUninit(pResampler->pBackendUserData, pResampler->pBackend, pAllocationCallbacks);
49961
49962 if (pResampler->_ownsHeap) {
49963 ma_free(pResampler->_pHeap, pAllocationCallbacks);
49964 }
49965}
49966
49967MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
49968{
49969 if (pResampler == NULL) {
49970 return MA_INVALID_ARGS;
49971 }
49972
49973 if (pFrameCountOut == NULL && pFrameCountIn == NULL) {
49974 return MA_INVALID_ARGS;
49975 }
49976
49977 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onProcess == NULL) {
49978 return MA_NOT_IMPLEMENTED;
49979 }
49980
49981 return pResampler->pBackendVTable->onProcess(pResampler->pBackendUserData, pResampler->pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
49982}
49983
49984MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
49985{
49986 ma_result result;
49987
49988 if (pResampler == NULL) {
49989 return MA_INVALID_ARGS;
49990 }
49991
49992 if (sampleRateIn == 0 || sampleRateOut == 0) {
49993 return MA_INVALID_ARGS;
49994 }
49995
49996 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onSetRate == NULL) {
49997 return MA_NOT_IMPLEMENTED;
49998 }
49999
50000 result = pResampler->pBackendVTable->onSetRate(pResampler->pBackendUserData, pResampler->pBackend, sampleRateIn, sampleRateOut);
50001 if (result != MA_SUCCESS) {
50002 return result;
50003 }
50004
50005 pResampler->sampleRateIn = sampleRateIn;
50006 pResampler->sampleRateOut = sampleRateOut;
50007
50008 return MA_SUCCESS;
50009}
50010
50012{
50013 ma_uint32 n;
50014 ma_uint32 d;
50015
50016 if (pResampler == NULL) {
50017 return MA_INVALID_ARGS;
50018 }
50019
50020 if (ratio <= 0) {
50021 return MA_INVALID_ARGS;
50022 }
50023
50024 d = 1000;
50025 n = (ma_uint32)(ratio * d);
50026
50027 if (n == 0) {
50028 return MA_INVALID_ARGS; /* Ratio too small. */
50029 }
50030
50031 MA_ASSERT(n != 0);
50032
50033 return ma_resampler_set_rate(pResampler, n, d);
50034}
50035
50037{
50038 if (pResampler == NULL) {
50039 return 0;
50040 }
50041
50042 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetInputLatency == NULL) {
50043 return 0;
50044 }
50045
50046 return pResampler->pBackendVTable->onGetInputLatency(pResampler->pBackendUserData, pResampler->pBackend);
50047}
50048
50050{
50051 if (pResampler == NULL) {
50052 return 0;
50053 }
50054
50055 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetOutputLatency == NULL) {
50056 return 0;
50057 }
50058
50059 return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend);
50060}
50061
50062MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
50063{
50064 if (pInputFrameCount == NULL) {
50065 return MA_INVALID_ARGS;
50066 }
50067
50068 *pInputFrameCount = 0;
50069
50070 if (pResampler == NULL) {
50071 return MA_INVALID_ARGS;
50072 }
50073
50074 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) {
50075 return MA_NOT_IMPLEMENTED;
50076 }
50077
50078 return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount);
50079}
50080
50081MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
50082{
50083 if (pOutputFrameCount == NULL) {
50084 return MA_INVALID_ARGS;
50085 }
50086
50087 *pOutputFrameCount = 0;
50088
50089 if (pResampler == NULL) {
50090 return MA_INVALID_ARGS;
50091 }
50092
50093 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) {
50094 return MA_NOT_IMPLEMENTED;
50095 }
50096
50097 return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount);
50098}
50099
50100
50105#ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT
50106#define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12
50107#endif
50108
50109#define MA_PLANE_LEFT 0
50110#define MA_PLANE_RIGHT 1
50111#define MA_PLANE_FRONT 2
50112#define MA_PLANE_BACK 3
50113#define MA_PLANE_BOTTOM 4
50114#define MA_PLANE_TOP 5
50115
50116static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = {
50117 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */
50118 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */
50119 { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */
50120 { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */
50121 { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */
50122 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */
50123 { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */
50124 { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */
50125 { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */
50126 { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */
50127 { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */
50128 { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */
50129 { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */
50130 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */
50131 { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */
50132 { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */
50133 { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */
50134 { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */
50135 { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */
50136 { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */
50137 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */
50138 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */
50139 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */
50140 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */
50141 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */
50142 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */
50143 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */
50144 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */
50145 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */
50146 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */
50147 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */
50148 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */
50149 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */
50150 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */
50151 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */
50152 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */
50153 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */
50154 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */
50155 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */
50156 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */
50157 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */
50158 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */
50159 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */
50160 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */
50161 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */
50162 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */
50163 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */
50164 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */
50165 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */
50166 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */
50167 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */
50168 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */
50169};
50170
50171static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB)
50172{
50173 /*
50174 Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to
50175 the following output configuration:
50176
50177 - front/left
50178 - side/left
50179 - back/left
50180
50181 The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount
50182 of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated.
50183
50184 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
50185 speaker emitting half of it's total volume from the front, and the other half from the left. Since part of it's volume is being emitted
50186 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
50187 receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between
50188 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
50189 across 3 spatial dimensions.
50190
50191 The first thing to do is figure out how each speaker's volume is spread over each of plane:
50192 - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane
50193 - side/left: 1 plane (left only) = 1/1 = entire volume from left plane
50194 - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane
50195 - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane
50196
50197 The amount of volume each channel contributes to each of it's planes is what controls how much it is willing to given and take to other
50198 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
50199 taken by the other to produce the final contribution.
50200 */
50201
50202 /* Contribution = Sum(Volume to Give * Volume to Take) */
50203 float contribution =
50204 g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] +
50205 g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] +
50206 g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] +
50207 g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] +
50208 g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] +
50209 g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5];
50210
50211 return contribution;
50212}
50213
50214MA_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)
50215{
50217
50218 MA_ZERO_OBJECT(&config);
50219 config.format = format;
50220 config.channelsIn = channelsIn;
50221 config.channelsOut = channelsOut;
50222 config.pChannelMapIn = pChannelMapIn;
50223 config.pChannelMapOut = pChannelMapOut;
50224 config.mixingMode = mixingMode;
50225
50226 return config;
50227}
50228
50229static ma_int32 ma_channel_converter_float_to_fixed(float x)
50230{
50231 return (ma_int32)(x * (1<<MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT));
50232}
50233
50234static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition)
50235{
50236 int i;
50237
50238 if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) {
50239 return MA_FALSE;
50240 }
50241
50242 if (channelPosition >= MA_CHANNEL_AUX_0 && channelPosition <= MA_CHANNEL_AUX_31) {
50243 return MA_FALSE;
50244 }
50245
50246 for (i = 0; i < 6; ++i) { /* Each side of a cube. */
50247 if (g_maChannelPlaneRatios[channelPosition][i] != 0) {
50248 return MA_TRUE;
50249 }
50250 }
50251
50252 return MA_FALSE;
50253}
50254
50255
50256static ma_bool32 ma_channel_map_is_passthrough(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut)
50257{
50258 if (channelsOut == channelsIn) {
50259 return ma_channel_map_is_equal(pChannelMapOut, pChannelMapIn, channelsOut);
50260 } else {
50261 return MA_FALSE; /* Channel counts differ, so cannot be a passthrough. */
50262 }
50263}
50264
50265static 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)
50266{
50267 if (ma_channel_map_is_passthrough(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut)) {
50269 }
50270
50271 if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) {
50273 }
50274
50275 if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) {
50277 }
50278
50281 }
50282
50283 /*
50284 We can use a simple shuffle if both channel maps have the same channel count and all channel
50285 positions are present in both.
50286 */
50287 if (channelsIn == channelsOut) {
50288 ma_uint32 iChannelIn;
50289 ma_bool32 areAllChannelPositionsPresent = MA_TRUE;
50290 for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) {
50291 ma_bool32 isInputChannelPositionInOutput = MA_FALSE;
50292 if (ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn))) {
50293 isInputChannelPositionInOutput = MA_TRUE;
50294 break;
50295 }
50296
50297 if (!isInputChannelPositionInOutput) {
50298 areAllChannelPositionsPresent = MA_FALSE;
50299 break;
50300 }
50301 }
50302
50303 if (areAllChannelPositionsPresent) {
50305 }
50306 }
50307
50308 /* Getting here means we'll need to use weights. */
50310}
50311
50312
50313static 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)
50314{
50315 ma_uint32 iChannelIn;
50316 ma_uint32 iChannelOut;
50317
50318 if (pShuffleTable == NULL || channelCountIn == 0 || channelCountOut == 0) {
50319 return MA_INVALID_ARGS;
50320 }
50321
50322 /*
50323 When building the shuffle table we just do a 1:1 mapping based on the first occurance of a channel. If the
50324 input channel has more than one occurance of a channel position, the second one will be ignored.
50325 */
50326 for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) {
50327 ma_channel channelOut;
50328
50329 /* Default to MA_CHANNEL_INDEX_NULL so that if a mapping is not found it'll be set appropriately. */
50330 pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL;
50331
50332 channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut);
50333 for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) {
50334 ma_channel channelIn;
50335
50336 channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn);
50337 if (channelOut == channelIn) {
50338 pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
50339 break;
50340 }
50341
50342 /*
50343 Getting here means the channels don't exactly match, but we are going to support some
50344 relaxed matching for practicality. If, for example, there are two stereo channel maps,
50345 but one uses front left/right and the other uses side left/right, it makes logical
50346 sense to just map these. The way we'll do it is we'll check if there is a logical
50347 corresponding mapping, and if so, apply it, but we will *not* break from the loop,
50348 thereby giving the loop a chance to find an exact match later which will take priority.
50349 */
50350 switch (channelOut)
50351 {
50352 /* Left channels. */
50355 {
50356 switch (channelIn) {
50359 {
50360 pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
50361 } break;
50362 }
50363 } break;
50364
50365 /* Right channels. */
50368 {
50369 switch (channelIn) {
50372 {
50373 pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
50374 } break;
50375 }
50376 } break;
50377
50378 default: break;
50379 }
50380 }
50381 }
50382
50383 return MA_SUCCESS;
50384}
50385
50386
50387static 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)
50388{
50389 ma_uint64 iFrame;
50390 ma_uint32 iChannelOut;
50391
50392 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50393 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
50394 ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
50395 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
50396 pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
50397 } else {
50398 pFramesOut[iChannelOut] = 0;
50399 }
50400 }
50401
50402 pFramesOut += channelsOut;
50403 pFramesIn += channelsIn;
50404 }
50405}
50406
50407static 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)
50408{
50409 ma_uint64 iFrame;
50410 ma_uint32 iChannelOut;
50411
50412 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50413 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
50414 ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
50415 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
50416 pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
50417 } else {
50418 pFramesOut[iChannelOut] = 0;
50419 }
50420 }
50421
50422 pFramesOut += channelsOut;
50423 pFramesIn += channelsIn;
50424 }
50425}
50426
50427static 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)
50428{
50429 ma_uint64 iFrame;
50430 ma_uint32 iChannelOut;
50431
50432 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50433 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
50434 ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
50435 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
50436 pFramesOut[iChannelOut*3 + 0] = pFramesIn[iChannelIn*3 + 0];
50437 pFramesOut[iChannelOut*3 + 1] = pFramesIn[iChannelIn*3 + 1];
50438 pFramesOut[iChannelOut*3 + 2] = pFramesIn[iChannelIn*3 + 2];
50439 } else {
50440 pFramesOut[iChannelOut*3 + 0] = 0;
50441 } pFramesOut[iChannelOut*3 + 1] = 0;
50442 } pFramesOut[iChannelOut*3 + 2] = 0;
50443
50444 pFramesOut += channelsOut*3;
50445 pFramesIn += channelsIn*3;
50446 }
50447}
50448
50449static 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)
50450{
50451 ma_uint64 iFrame;
50452 ma_uint32 iChannelOut;
50453
50454 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50455 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
50456 ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
50457 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
50458 pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
50459 } else {
50460 pFramesOut[iChannelOut] = 0;
50461 }
50462 }
50463
50464 pFramesOut += channelsOut;
50465 pFramesIn += channelsIn;
50466 }
50467}
50468
50469static 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)
50470{
50471 ma_uint64 iFrame;
50472 ma_uint32 iChannelOut;
50473
50474 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50475 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
50476 ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
50477 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
50478 pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
50479 } else {
50480 pFramesOut[iChannelOut] = 0;
50481 }
50482 }
50483
50484 pFramesOut += channelsOut;
50485 pFramesIn += channelsIn;
50486 }
50487}
50488
50489static 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)
50490{
50491 if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || pShuffleTable == NULL) {
50492 return MA_INVALID_ARGS;
50493 }
50494
50495 switch (format)
50496 {
50497 case ma_format_u8:
50498 {
50499 ma_channel_map_apply_shuffle_table_u8((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable);
50500 } break;
50501
50502 case ma_format_s16:
50503 {
50504 ma_channel_map_apply_shuffle_table_s16((ma_int16*)pFramesOut, channelsOut, (const ma_int16*)pFramesIn, channelsIn, frameCount, pShuffleTable);
50505 } break;
50506
50507 case ma_format_s24:
50508 {
50509 ma_channel_map_apply_shuffle_table_s24((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable);
50510 } break;
50511
50512 case ma_format_s32:
50513 {
50514 ma_channel_map_apply_shuffle_table_s32((ma_int32*)pFramesOut, channelsOut, (const ma_int32*)pFramesIn, channelsIn, frameCount, pShuffleTable);
50515 } break;
50516
50517 case ma_format_f32:
50518 {
50519 ma_channel_map_apply_shuffle_table_f32((float*)pFramesOut, channelsOut, (const float*)pFramesIn, channelsIn, frameCount, pShuffleTable);
50520 } break;
50521
50522 default: return MA_INVALID_ARGS; /* Unknown format. */
50523 }
50524
50525 return MA_SUCCESS;
50526}
50527
50528static ma_result ma_channel_map_apply_mono_out_f32(float* pFramesOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount)
50529{
50530 ma_uint64 iFrame;
50531 ma_uint32 iChannelIn;
50532 ma_uint32 accumulationCount;
50533
50534 if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) {
50535 return MA_INVALID_ARGS;
50536 }
50537
50538 /* In this case the output stream needs to be the average of all channels, ignoring NONE. */
50539
50540 /* A quick pre-processing step to get the accumulation counter since we're ignoring NONE channels. */
50541 accumulationCount = 0;
50542 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
50543 if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) {
50544 accumulationCount += 1;
50545 }
50546 }
50547
50548 if (accumulationCount > 0) { /* <-- Prevent a division by zero. */
50549 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50550 float accumulation = 0;
50551
50552 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
50553 ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
50554 if (channelIn != MA_CHANNEL_NONE) {
50555 accumulation += pFramesIn[iChannelIn];
50556 }
50557 }
50558
50559 pFramesOut[0] = accumulation / accumulationCount;
50560 pFramesOut += 1;
50561 pFramesIn += channelsIn;
50562 }
50563 } else {
50564 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1);
50565 }
50566
50567 return MA_SUCCESS;
50568}
50569
50570static ma_result ma_channel_map_apply_mono_in_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint64 frameCount, ma_mono_expansion_mode monoExpansionMode)
50571{
50572 ma_uint64 iFrame;
50573 ma_uint32 iChannelOut;
50574
50575 if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) {
50576 return MA_INVALID_ARGS;
50577 }
50578
50579 /* Note that the MA_CHANNEL_NONE channel must be ignored in all cases. */
50580 switch (monoExpansionMode)
50581 {
50583 {
50584 float weight;
50585 ma_uint32 validChannelCount = 0;
50586
50587 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
50588 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
50589 if (channelOut != MA_CHANNEL_NONE) {
50590 validChannelCount += 1;
50591 }
50592 }
50593
50594 weight = 1.0f / validChannelCount;
50595
50596 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50597 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
50598 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
50599 if (channelOut != MA_CHANNEL_NONE) {
50600 pFramesOut[iChannelOut] = pFramesIn[0] * weight;
50601 }
50602 }
50603
50604 pFramesOut += channelsOut;
50605 pFramesIn += 1;
50606 }
50607 } break;
50608
50610 {
50611 if (channelsOut >= 2) {
50612 ma_uint32 iChannelLeft = (ma_uint32)-1;
50613 ma_uint32 iChannelRight = (ma_uint32)-1;
50614
50615 /*
50616 We first need to find our stereo channels. We prefer front-left and front-right, but
50617 if they're not available, we'll also try side-left and side-right. If neither are
50618 available we'll fall through to the default case below.
50619 */
50620 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
50621 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
50622 if (channelOut == MA_CHANNEL_SIDE_LEFT) {
50623 iChannelLeft = iChannelOut;
50624 }
50625 if (channelOut == MA_CHANNEL_SIDE_RIGHT) {
50626 iChannelRight = iChannelOut;
50627 }
50628 }
50629
50630 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
50631 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
50632 if (channelOut == MA_CHANNEL_FRONT_LEFT) {
50633 iChannelLeft = iChannelOut;
50634 }
50635 if (channelOut == MA_CHANNEL_FRONT_RIGHT) {
50636 iChannelRight = iChannelOut;
50637 }
50638 }
50639
50640
50641 if (iChannelLeft != (ma_uint32)-1 && iChannelRight != (ma_uint32)-1) {
50642 /* We found our stereo channels so we can duplicate the signal across those channels. */
50643 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50644 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
50645 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
50646 if (channelOut != MA_CHANNEL_NONE) {
50647 if (iChannelOut == iChannelLeft || iChannelOut == iChannelRight) {
50648 pFramesOut[iChannelOut] = pFramesIn[0];
50649 } else {
50650 pFramesOut[iChannelOut] = 0.0f;
50651 }
50652 }
50653 }
50654
50655 pFramesOut += channelsOut;
50656 pFramesIn += 1;
50657 }
50658
50659 break; /* Get out of the switch. */
50660 } else {
50661 /* Fallthrough. Does not have left and right channels. */
50662 goto default_handler;
50663 }
50664 } else {
50665 /* Fallthrough. Does not have stereo channels. */
50666 goto default_handler;
50667 }
50668 }; /* Fallthrough. See comments above. */
50669
50671 default:
50672 {
50673 default_handler:
50674 {
50675 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50676 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
50677 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
50678 if (channelOut != MA_CHANNEL_NONE) {
50679 pFramesOut[iChannelOut] = pFramesIn[0];
50680 }
50681 }
50682
50683 pFramesOut += channelsOut;
50684 pFramesIn += 1;
50685 }
50686 }
50687 } break;
50688 }
50689
50690 return MA_SUCCESS;
50691}
50692
50693static 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)
50694{
50695 ma_channel_conversion_path conversionPath = ma_channel_map_get_conversion_path(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, mode);
50696
50697 /* Optimized Path: Passthrough */
50698 if (conversionPath == ma_channel_conversion_path_passthrough) {
50699 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut);
50700 return;
50701 }
50702
50703 /* Special Path: Mono Output. */
50704 if (conversionPath == ma_channel_conversion_path_mono_out) {
50705 ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount);
50706 return;
50707 }
50708
50709 /* Special Path: Mono Input. */
50710 if (conversionPath == ma_channel_conversion_path_mono_in) {
50711 ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount, monoExpansionMode);
50712 return;
50713 }
50714
50715 /* Getting here means we aren't running on an optimized conversion path. */
50716 if (channelsOut <= MA_MAX_CHANNELS) {
50717 ma_result result;
50718
50719 if (mode == ma_channel_mix_mode_simple) {
50720 ma_channel shuffleTable[MA_MAX_CHANNELS];
50721
50722 result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable);
50723 if (result != MA_SUCCESS) {
50724 return;
50725 }
50726
50727 result = ma_channel_map_apply_shuffle_table(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable, ma_format_f32);
50728 if (result != MA_SUCCESS) {
50729 return;
50730 }
50731 } else {
50732 ma_uint32 iFrame;
50733 ma_uint32 iChannelOut;
50734 ma_uint32 iChannelIn;
50735 float weights[32][32]; /* Do not use MA_MAX_CHANNELS here! */
50736
50737 /*
50738 If we have a small enough number of channels, pre-compute the weights. Otherwise we'll just need to
50739 fall back to a slower path because otherwise we'll run out of stack space.
50740 */
50741 if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) {
50742 /* Pre-compute weights. */
50743 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
50744 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
50745 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
50746 ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
50747 weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn);
50748 }
50749 }
50750
50751 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50752 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
50753 float accumulation = 0;
50754
50755 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
50756 accumulation += pFramesIn[iChannelIn] * weights[iChannelOut][iChannelIn];
50757 }
50758
50759 pFramesOut[iChannelOut] = accumulation;
50760 }
50761
50762 pFramesOut += channelsOut;
50763 pFramesIn += channelsIn;
50764 }
50765 } else {
50766 /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */
50767 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50768 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
50769 float accumulation = 0;
50770 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
50771
50772 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
50773 ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
50774 accumulation += pFramesIn[iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn);
50775 }
50776
50777 pFramesOut[iChannelOut] = accumulation;
50778 }
50779
50780 pFramesOut += channelsOut;
50781 pFramesIn += channelsIn;
50782 }
50783 }
50784 }
50785 } else {
50786 /* Fall back to silence. If you hit this, what are you doing with so many channels?! */
50787 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut);
50788 }
50789}
50790
50791
50792typedef struct
50793{
50794 size_t sizeInBytes;
50795 size_t channelMapInOffset;
50796 size_t channelMapOutOffset;
50797 size_t shuffleTableOffset;
50798 size_t weightsOffset;
50799} ma_channel_converter_heap_layout;
50800
50801static ma_channel_conversion_path ma_channel_converter_config_get_conversion_path(const ma_channel_converter_config* pConfig)
50802{
50803 return ma_channel_map_get_conversion_path(pConfig->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapOut, pConfig->channelsOut, pConfig->mixingMode);
50804}
50805
50806static ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter_config* pConfig, ma_channel_converter_heap_layout* pHeapLayout)
50807{
50808 ma_channel_conversion_path conversionPath;
50809
50810 MA_ASSERT(pHeapLayout != NULL);
50811
50812 if (pConfig == NULL) {
50813 return MA_INVALID_ARGS;
50814 }
50815
50816 if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
50817 return MA_INVALID_ARGS;
50818 }
50819
50820 if (!ma_channel_map_is_valid(pConfig->pChannelMapIn, pConfig->channelsIn)) {
50821 return MA_INVALID_ARGS;
50822 }
50823
50824 if (!ma_channel_map_is_valid(pConfig->pChannelMapOut, pConfig->channelsOut)) {
50825 return MA_INVALID_ARGS;
50826 }
50827
50828 pHeapLayout->sizeInBytes = 0;
50829
50830 /* Input channel map. Only need to allocate this if we have an input channel map (otherwise default channel map is assumed). */
50831 pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes;
50832 if (pConfig->pChannelMapIn != NULL) {
50833 pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn;
50834 }
50835
50836 /* Output channel map. Only need to allocate this if we have an output channel map (otherwise default channel map is assumed). */
50837 pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes;
50838 if (pConfig->pChannelMapOut != NULL) {
50839 pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut;
50840 }
50841
50842 /* Alignment for the next section. */
50843 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
50844
50845 /* Whether or not we use weights of a shuffle table depends on the channel map themselves and the algorithm we've chosen. */
50846 conversionPath = ma_channel_converter_config_get_conversion_path(pConfig);
50847
50848 /* Shuffle table */
50849 pHeapLayout->shuffleTableOffset = pHeapLayout->sizeInBytes;
50850 if (conversionPath == ma_channel_conversion_path_shuffle) {
50851 pHeapLayout->sizeInBytes += sizeof(ma_uint8) * pConfig->channelsOut;
50852 }
50853
50854 /* Weights */
50855 pHeapLayout->weightsOffset = pHeapLayout->sizeInBytes;
50856 if (conversionPath == ma_channel_conversion_path_weights) {
50857 pHeapLayout->sizeInBytes += sizeof(float*) * pConfig->channelsIn;
50858 pHeapLayout->sizeInBytes += sizeof(float ) * pConfig->channelsIn * pConfig->channelsOut;
50859 }
50860
50861 /* Make sure allocation size is aligned. */
50862 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
50863
50864 return MA_SUCCESS;
50865}
50866
50868{
50869 ma_result result;
50870 ma_channel_converter_heap_layout heapLayout;
50871
50872 if (pHeapSizeInBytes == NULL) {
50873 return MA_INVALID_ARGS;
50874 }
50875
50876 *pHeapSizeInBytes = 0;
50877
50878 result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout);
50879 if (result != MA_SUCCESS) {
50880 return result;
50881 }
50882
50883 *pHeapSizeInBytes = heapLayout.sizeInBytes;
50884
50885 return MA_SUCCESS;
50886}
50887
50889{
50890 ma_result result;
50891 ma_channel_converter_heap_layout heapLayout;
50892
50893 if (pConverter == NULL) {
50894 return MA_INVALID_ARGS;
50895 }
50896
50897 MA_ZERO_OBJECT(pConverter);
50898
50899 result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout);
50900 if (result != MA_SUCCESS) {
50901 return result;
50902 }
50903
50904 pConverter->_pHeap = pHeap;
50905 MA_ZERO_MEMORY(pConverter->_pHeap, heapLayout.sizeInBytes);
50906
50907 pConverter->format = pConfig->format;
50908 pConverter->channelsIn = pConfig->channelsIn;
50909 pConverter->channelsOut = pConfig->channelsOut;
50910 pConverter->mixingMode = pConfig->mixingMode;
50911
50912 if (pConfig->pChannelMapIn != NULL) {
50913 pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset);
50914 ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn);
50915 } else {
50916 pConverter->pChannelMapIn = NULL; /* Use default channel map. */
50917 }
50918
50919 if (pConfig->pChannelMapOut != NULL) {
50920 pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset);
50921 ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut);
50922 } else {
50923 pConverter->pChannelMapOut = NULL; /* Use default channel map. */
50924 }
50925
50926 pConverter->conversionPath = ma_channel_converter_config_get_conversion_path(pConfig);
50927
50929 pConverter->pShuffleTable = (ma_uint8*)ma_offset_ptr(pHeap, heapLayout.shuffleTableOffset);
50930 ma_channel_map_build_shuffle_table(pConverter->pChannelMapIn, pConverter->channelsIn, pConverter->pChannelMapOut, pConverter->channelsOut, pConverter->pShuffleTable);
50931 }
50932
50934 ma_uint32 iChannelIn;
50935 ma_uint32 iChannelOut;
50936
50937 if (pConverter->format == ma_format_f32) {
50938 pConverter->weights.f32 = (float** )ma_offset_ptr(pHeap, heapLayout.weightsOffset);
50939 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
50940 pConverter->weights.f32[iChannelIn] = (float*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(float*) * pConverter->channelsIn) + (sizeof(float) * pConverter->channelsOut * iChannelIn)));
50941 }
50942 } else {
50943 pConverter->weights.s16 = (ma_int32**)ma_offset_ptr(pHeap, heapLayout.weightsOffset);
50944 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
50945 pConverter->weights.s16[iChannelIn] = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(ma_int32*) * pConverter->channelsIn) + (sizeof(ma_int32) * pConverter->channelsOut * iChannelIn)));
50946 }
50947 }
50948
50949 /* Silence our weights by default. */
50950 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
50951 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) {
50952 if (pConverter->format == ma_format_f32) {
50953 pConverter->weights.f32[iChannelIn][iChannelOut] = 0.0f;
50954 } else {
50955 pConverter->weights.s16[iChannelIn][iChannelOut] = 0;
50956 }
50957 }
50958 }
50959
50960 /*
50961 We now need to fill out our weights table. This is determined by the mixing mode.
50962 */
50963 switch (pConverter->mixingMode)
50964 {
50965 case ma_channel_mix_mode_custom_weights:
50966 {
50967 if (pConfig->ppWeights == NULL) {
50968 return MA_INVALID_ARGS; /* Config specified a custom weights mixing mode, but no custom weights have been specified. */
50969 }
50970
50971 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
50972 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) {
50973 float weight = pConfig->ppWeights[iChannelIn][iChannelOut];
50974
50975 if (pConverter->format == ma_format_f32) {
50976 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
50977 } else {
50978 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
50979 }
50980 }
50981 }
50982 } break;
50983
50985 {
50986 /* In simple mode, excess channels need to be silenced or dropped. */
50987 ma_uint32 iChannel;
50988 for (iChannel = 0; iChannel < ma_min(pConverter->channelsIn, pConverter->channelsOut); iChannel += 1) {
50989 if (pConverter->format == ma_format_f32) {
50990 if (pConverter->weights.f32[iChannel][iChannel] == 0) {
50991 pConverter->weights.f32[iChannel][iChannel] = 1;
50992 }
50993 } else {
50994 if (pConverter->weights.s16[iChannel][iChannel] == 0) {
50995 pConverter->weights.s16[iChannel][iChannel] = ma_channel_converter_float_to_fixed(1);
50996 }
50997 }
50998 }
50999 } break;
51000
51002 default:
51003 {
51004 /* Unmapped input channels. */
51005 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
51006 ma_channel channelPosIn = pConverter->pChannelMapIn[iChannelIn];
51007
51008 if (ma_is_spatial_channel_position(channelPosIn)) {
51009 if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) {
51010 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
51011 ma_channel channelPosOut = pConverter->pChannelMapOut[iChannelOut];
51012
51013 if (ma_is_spatial_channel_position(channelPosOut)) {
51014 float weight = 0;
51015 if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
51016 weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
51017 }
51018
51019 /* Only apply the weight if we haven't already got some contribution from the respective channels. */
51020 if (pConverter->format == ma_format_f32) {
51021 if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
51022 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
51023 }
51024 } else {
51025 if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
51026 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
51027 }
51028 }
51029 }
51030 }
51031 }
51032 }
51033 }
51034
51035 /* Unmapped output channels. */
51036 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
51037 ma_channel channelPosOut = pConverter->pChannelMapOut[iChannelOut];
51038
51039 if (ma_is_spatial_channel_position(channelPosOut)) {
51040 if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) {
51041 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
51042 ma_channel channelPosIn = pConverter->pChannelMapIn[iChannelIn];
51043
51044 if (ma_is_spatial_channel_position(channelPosIn)) {
51045 float weight = 0;
51046 if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
51047 weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
51048 }
51049
51050 /* Only apply the weight if we haven't already got some contribution from the respective channels. */
51051 if (pConverter->format == ma_format_f32) {
51052 if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
51053 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
51054 }
51055 } else {
51056 if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
51057 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
51058 }
51059 }
51060 }
51061 }
51062 }
51063 }
51064 }
51065 } break;
51066 }
51067 }
51068
51069 return MA_SUCCESS;
51070}
51071
51073{
51074 ma_result result;
51075 size_t heapSizeInBytes;
51076 void* pHeap;
51077
51078 result = ma_channel_converter_get_heap_size(pConfig, &heapSizeInBytes);
51079 if (result != MA_SUCCESS) {
51080 return result;
51081 }
51082
51083 if (heapSizeInBytes > 0) {
51084 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
51085 if (pHeap == NULL) {
51086 return MA_OUT_OF_MEMORY;
51087 }
51088 } else {
51089 pHeap = NULL;
51090 }
51091
51092 result = ma_channel_converter_init_preallocated(pConfig, pHeap, pConverter);
51093 if (result != MA_SUCCESS) {
51094 ma_free(pHeap, pAllocationCallbacks);
51095 return result;
51096 }
51097
51098 pConverter->_ownsHeap = MA_TRUE;
51099 return MA_SUCCESS;
51100}
51101
51102MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks)
51103{
51104 if (pConverter == NULL) {
51105 return;
51106 }
51107
51108 if (pConverter->_ownsHeap) {
51109 ma_free(pConverter->_pHeap, pAllocationCallbacks);
51110 }
51111}
51112
51113static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
51114{
51115 MA_ASSERT(pConverter != NULL);
51116 MA_ASSERT(pFramesOut != NULL);
51117 MA_ASSERT(pFramesIn != NULL);
51118
51119 ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
51120 return MA_SUCCESS;
51121}
51122
51123static ma_result ma_channel_converter_process_pcm_frames__shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
51124{
51125 MA_ASSERT(pConverter != NULL);
51126 MA_ASSERT(pFramesOut != NULL);
51127 MA_ASSERT(pFramesIn != NULL);
51128 MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut);
51129
51130 return ma_channel_map_apply_shuffle_table(pFramesOut, pConverter->channelsOut, pFramesIn, pConverter->channelsIn, frameCount, pConverter->pShuffleTable, pConverter->format);
51131}
51132
51133static ma_result ma_channel_converter_process_pcm_frames__mono_in(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
51134{
51135 ma_uint64 iFrame;
51136
51137 MA_ASSERT(pConverter != NULL);
51138 MA_ASSERT(pFramesOut != NULL);
51139 MA_ASSERT(pFramesIn != NULL);
51140 MA_ASSERT(pConverter->channelsIn == 1);
51141
51142 switch (pConverter->format)
51143 {
51144 case ma_format_u8:
51145 {
51146 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
51147 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
51148
51149 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
51150 ma_uint32 iChannel;
51151 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
51152 pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame];
51153 }
51154 }
51155 } break;
51156
51157 case ma_format_s16:
51158 {
51159 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
51160 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
51161
51162 if (pConverter->channelsOut == 2) {
51163 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
51164 pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame];
51165 pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame];
51166 }
51167 } else {
51168 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
51169 ma_uint32 iChannel;
51170 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
51171 pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame];
51172 }
51173 }
51174 }
51175 } break;
51176
51177 case ma_format_s24:
51178 {
51179 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
51180 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
51181
51182 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
51183 ma_uint32 iChannel;
51184 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
51185 ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel;
51186 ma_uint64 iSampleIn = iFrame;
51187 pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0];
51188 pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1];
51189 pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2];
51190 }
51191 }
51192 } break;
51193
51194 case ma_format_s32:
51195 {
51196 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
51197 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
51198
51199 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
51200 ma_uint32 iChannel;
51201 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
51202 pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame];
51203 }
51204 }
51205 } break;
51206
51207 case ma_format_f32:
51208 {
51209 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
51210 const float* pFramesInF32 = (const float*)pFramesIn;
51211
51212 if (pConverter->channelsOut == 2) {
51213 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
51214 pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame];
51215 pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame];
51216 }
51217 } else {
51218 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
51219 ma_uint32 iChannel;
51220 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
51221 pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame];
51222 }
51223 }
51224 }
51225 } break;
51226
51227 default: return MA_INVALID_OPERATION; /* Unknown format. */
51228 }
51229
51230 return MA_SUCCESS;
51231}
51232
51233static ma_result ma_channel_converter_process_pcm_frames__mono_out(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
51234{
51235 ma_uint64 iFrame;
51236 ma_uint32 iChannel;
51237
51238 MA_ASSERT(pConverter != NULL);
51239 MA_ASSERT(pFramesOut != NULL);
51240 MA_ASSERT(pFramesIn != NULL);
51241 MA_ASSERT(pConverter->channelsOut == 1);
51242
51243 switch (pConverter->format)
51244 {
51245 case ma_format_u8:
51246 {
51247 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
51248 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
51249
51250 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
51251 ma_int32 t = 0;
51252 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
51253 t += ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*pConverter->channelsIn + iChannel]);
51254 }
51255
51256 pFramesOutU8[iFrame] = ma_clip_u8(t / pConverter->channelsOut);
51257 }
51258 } break;
51259
51260 case ma_format_s16:
51261 {
51262 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
51263 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
51264
51265 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
51266 ma_int32 t = 0;
51267 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
51268 t += pFramesInS16[iFrame*pConverter->channelsIn + iChannel];
51269 }
51270
51271 pFramesOutS16[iFrame] = (ma_int16)(t / pConverter->channelsIn);
51272 }
51273 } break;
51274
51275 case ma_format_s24:
51276 {
51277 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
51278 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
51279
51280 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
51281 ma_int64 t = 0;
51282 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
51283 t += ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*pConverter->channelsIn + iChannel)*3]);
51284 }
51285
51286 ma_pcm_sample_s32_to_s24_no_scale(t / pConverter->channelsIn, &pFramesOutS24[iFrame*3]);
51287 }
51288 } break;
51289
51290 case ma_format_s32:
51291 {
51292 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
51293 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
51294
51295 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
51296 ma_int64 t = 0;
51297 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
51298 t += pFramesInS32[iFrame*pConverter->channelsIn + iChannel];
51299 }
51300
51301 pFramesOutS32[iFrame] = (ma_int32)(t / pConverter->channelsIn);
51302 }
51303 } break;
51304
51305 case ma_format_f32:
51306 {
51307 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
51308 const float* pFramesInF32 = (const float*)pFramesIn;
51309
51310 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
51311 float t = 0;
51312 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
51313 t += pFramesInF32[iFrame*pConverter->channelsIn + iChannel];
51314 }
51315
51316 pFramesOutF32[iFrame] = t / pConverter->channelsIn;
51317 }
51318 } break;
51319
51320 default: return MA_INVALID_OPERATION; /* Unknown format. */
51321 }
51322
51323 return MA_SUCCESS;
51324}
51325
51326static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
51327{
51328 ma_uint32 iFrame;
51329 ma_uint32 iChannelIn;
51330 ma_uint32 iChannelOut;
51331
51332 MA_ASSERT(pConverter != NULL);
51333 MA_ASSERT(pFramesOut != NULL);
51334 MA_ASSERT(pFramesIn != NULL);
51335
51336 /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */
51337
51338 /* Clear. */
51339 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
51340
51341 /* Accumulate. */
51342 switch (pConverter->format)
51343 {
51344 case ma_format_u8:
51345 {
51346 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
51347 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
51348
51349 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
51350 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
51351 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
51352 ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]);
51353 ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn + iChannelIn ]);
51354 ma_int32 s = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127);
51355 pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s);
51356 }
51357 }
51358 }
51359 } break;
51360
51361 case ma_format_s16:
51362 {
51363 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
51364 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
51365
51366 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
51367 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
51368 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
51369 ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut];
51370 s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
51371
51372 pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767);
51373 }
51374 }
51375 }
51376 } break;
51377
51378 case ma_format_s24:
51379 {
51380 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
51381 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
51382
51383 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
51384 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
51385 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
51386 ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]);
51387 ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn + iChannelIn )*3]);
51388 ma_int64 s24 = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607);
51389 ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]);
51390 }
51391 }
51392 }
51393 } break;
51394
51395 case ma_format_s32:
51396 {
51397 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
51398 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
51399
51400 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
51401 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
51402 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
51403 ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut];
51404 s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
51405
51406 pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s);
51407 }
51408 }
51409 }
51410 } break;
51411
51412 case ma_format_f32:
51413 {
51414 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
51415 const float* pFramesInF32 = (const float*)pFramesIn;
51416
51417 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
51418 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
51419 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
51420 pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut];
51421 }
51422 }
51423 }
51424 } break;
51425
51426 default: return MA_INVALID_OPERATION; /* Unknown format. */
51427 }
51428
51429 return MA_SUCCESS;
51430}
51431
51432MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
51433{
51434 if (pConverter == NULL) {
51435 return MA_INVALID_ARGS;
51436 }
51437
51438 if (pFramesOut == NULL) {
51439 return MA_INVALID_ARGS;
51440 }
51441
51442 if (pFramesIn == NULL) {
51443 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
51444 return MA_SUCCESS;
51445 }
51446
51447 switch (pConverter->conversionPath)
51448 {
51449 case ma_channel_conversion_path_passthrough: return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount);
51450 case ma_channel_conversion_path_mono_out: return ma_channel_converter_process_pcm_frames__mono_out(pConverter, pFramesOut, pFramesIn, frameCount);
51451 case ma_channel_conversion_path_mono_in: return ma_channel_converter_process_pcm_frames__mono_in(pConverter, pFramesOut, pFramesIn, frameCount);
51452 case ma_channel_conversion_path_shuffle: return ma_channel_converter_process_pcm_frames__shuffle(pConverter, pFramesOut, pFramesIn, frameCount);
51454 default:
51455 {
51456 return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount);
51457 }
51458 }
51459}
51460
51461MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
51462{
51463 if (pConverter == NULL || pChannelMap == NULL) {
51464 return MA_INVALID_ARGS;
51465 }
51466
51467 ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapIn, pConverter->channelsIn);
51468
51469 return MA_SUCCESS;
51470}
51471
51472MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
51473{
51474 if (pConverter == NULL || pChannelMap == NULL) {
51475 return MA_INVALID_ARGS;
51476 }
51477
51478 ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapOut, pConverter->channelsOut);
51479
51480 return MA_SUCCESS;
51481}
51482
51483
51484
51490{
51492 MA_ZERO_OBJECT(&config);
51493
51496 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. */
51497
51498 /* Linear resampling defaults. */
51499 config.resampling.linear.lpfOrder = 1;
51500
51501 return config;
51502}
51503
51504MA_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)
51505{
51507 config.formatIn = formatIn;
51508 config.formatOut = formatOut;
51509 config.channelsIn = channelsIn;
51510 config.channelsOut = channelsOut;
51511 config.sampleRateIn = sampleRateIn;
51512 config.sampleRateOut = sampleRateOut;
51513
51514 return config;
51515}
51516
51517
51518typedef struct
51519{
51520 size_t sizeInBytes;
51521 size_t channelConverterOffset;
51522 size_t resamplerOffset;
51523} ma_data_converter_heap_layout;
51524
51525static ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig)
51526{
51527 MA_ASSERT(pConfig != NULL);
51528
51529 return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut;
51530}
51531
51532static ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig)
51533{
51534 MA_ASSERT(pConfig != NULL);
51535
51536 /*
51537 We want to avoid as much data conversion as possible. The channel converter and linear
51538 resampler both support s16 and f32 natively. We need to decide on the format to use for this
51539 stage. We call this the mid format because it's used in the middle stage of the conversion
51540 pipeline. If the output format is either s16 or f32 we use that one. If that is not the case it
51541 will do the same thing for the input format. If it's neither we just use f32. If we are using a
51542 custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced
51543 to use that if resampling is required.
51544 */
51545 if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) {
51546 return ma_format_f32; /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */
51547 } else {
51548 /* */ if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) {
51549 return pConfig->formatOut;
51550 } else if (pConfig->formatIn == ma_format_s16 || pConfig->formatIn == ma_format_f32) {
51551 return pConfig->formatIn;
51552 } else {
51553 return ma_format_f32;
51554 }
51555 }
51556}
51557
51558static ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig)
51559{
51560 ma_channel_converter_config channelConverterConfig;
51561
51562 MA_ASSERT(pConfig != NULL);
51563
51564 channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode);
51565 channelConverterConfig.ppWeights = pConfig->ppChannelWeights;
51566
51567 return channelConverterConfig;
51568}
51569
51570static ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig)
51571{
51572 ma_resampler_config resamplerConfig;
51573 ma_uint32 resamplerChannels;
51574
51575 MA_ASSERT(pConfig != NULL);
51576
51577 /* 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. */
51578 if (pConfig->channelsIn < pConfig->channelsOut) {
51579 resamplerChannels = pConfig->channelsIn;
51580 } else {
51581 resamplerChannels = pConfig->channelsOut;
51582 }
51583
51584 resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm);
51585 resamplerConfig.linear = pConfig->resampling.linear;
51586 resamplerConfig.pBackendVTable = pConfig->resampling.pBackendVTable;
51587 resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData;
51588
51589 return resamplerConfig;
51590}
51591
51592static ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout)
51593{
51594 ma_result result;
51595
51596 MA_ASSERT(pHeapLayout != NULL);
51597
51598 MA_ZERO_OBJECT(pHeapLayout);
51599
51600 if (pConfig == NULL) {
51601 return MA_INVALID_ARGS;
51602 }
51603
51604 if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
51605 return MA_INVALID_ARGS;
51606 }
51607
51608 pHeapLayout->sizeInBytes = 0;
51609
51610 /* Channel converter. */
51611 pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes;
51612 {
51613 size_t heapSizeInBytes;
51614 ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig);
51615
51616 result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes);
51617 if (result != MA_SUCCESS) {
51618 return result;
51619 }
51620
51621 pHeapLayout->sizeInBytes += heapSizeInBytes;
51622 }
51623
51624 /* Resampler. */
51625 pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes;
51626 if (ma_data_converter_config_is_resampler_required(pConfig)) {
51627 size_t heapSizeInBytes;
51628 ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig);
51629
51630 result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes);
51631 if (result != MA_SUCCESS) {
51632 return result;
51633 }
51634
51635 pHeapLayout->sizeInBytes += heapSizeInBytes;
51636 }
51637
51638 /* Make sure allocation size is aligned. */
51639 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
51640
51641 return MA_SUCCESS;
51642}
51643
51644MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes)
51645{
51646 ma_result result;
51647 ma_data_converter_heap_layout heapLayout;
51648
51649 if (pHeapSizeInBytes == NULL) {
51650 return MA_INVALID_ARGS;
51651 }
51652
51653 *pHeapSizeInBytes = 0;
51654
51655 result = ma_data_converter_get_heap_layout(pConfig, &heapLayout);
51656 if (result != MA_SUCCESS) {
51657 return result;
51658 }
51659
51660 *pHeapSizeInBytes = heapLayout.sizeInBytes;
51661
51662 return MA_SUCCESS;
51663}
51664
51666{
51667 ma_result result;
51668 ma_data_converter_heap_layout heapLayout;
51669 ma_format midFormat;
51670 ma_bool32 isResamplingRequired;
51671
51672 if (pConverter == NULL) {
51673 return MA_INVALID_ARGS;
51674 }
51675
51676 MA_ZERO_OBJECT(pConverter);
51677
51678 result = ma_data_converter_get_heap_layout(pConfig, &heapLayout);
51679 if (result != MA_SUCCESS) {
51680 return result;
51681 }
51682
51683 pConverter->_pHeap = pHeap;
51684 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
51685
51686 pConverter->formatIn = pConfig->formatIn;
51687 pConverter->formatOut = pConfig->formatOut;
51688 pConverter->channelsIn = pConfig->channelsIn;
51689 pConverter->channelsOut = pConfig->channelsOut;
51690 pConverter->sampleRateIn = pConfig->sampleRateIn;
51691 pConverter->sampleRateOut = pConfig->sampleRateOut;
51692 pConverter->ditherMode = pConfig->ditherMode;
51693
51694 /*
51695 Determine if resampling is required. We need to do this so we can determine an appropriate
51696 mid format to use. If resampling is required, the mid format must be ma_format_f32 since
51697 that is the only one that is guaranteed to supported by custom resampling backends.
51698 */
51699 isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig);
51700 midFormat = ma_data_converter_config_get_mid_format(pConfig);
51701
51702
51703 /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */
51704 {
51705 ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig);
51706
51707 result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter);
51708 if (result != MA_SUCCESS) {
51709 return result;
51710 }
51711
51712 /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */
51714 pConverter->hasChannelConverter = MA_TRUE;
51715 }
51716 }
51717
51718
51719 /* Resampler. */
51720 if (isResamplingRequired) {
51721 ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig);
51722
51723 result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler);
51724 if (result != MA_SUCCESS) {
51725 return result;
51726 }
51727
51728 pConverter->hasResampler = MA_TRUE;
51729 }
51730
51731
51732 /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */
51733 if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) {
51734 /* 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. */
51735 if (pConverter->formatIn == pConverter->formatOut) {
51736 /* The formats are the same so we can just pass through. */
51737 pConverter->hasPreFormatConversion = MA_FALSE;
51738 pConverter->hasPostFormatConversion = MA_FALSE;
51739 } else {
51740 /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */
51741 pConverter->hasPreFormatConversion = MA_FALSE;
51742 pConverter->hasPostFormatConversion = MA_TRUE;
51743 }
51744 } else {
51745 /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */
51746 if (pConverter->formatIn != midFormat) {
51747 pConverter->hasPreFormatConversion = MA_TRUE;
51748 }
51749 if (pConverter->formatOut != midFormat) {
51750 pConverter->hasPostFormatConversion = MA_TRUE;
51751 }
51752 }
51753
51754 /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */
51755 if (pConverter->hasPreFormatConversion == MA_FALSE &&
51756 pConverter->hasPostFormatConversion == MA_FALSE &&
51757 pConverter->hasChannelConverter == MA_FALSE &&
51758 pConverter->hasResampler == MA_FALSE) {
51759 pConverter->isPassthrough = MA_TRUE;
51760 }
51761
51762
51763 /* We now need to determine our execution path. */
51764 if (pConverter->isPassthrough) {
51766 } else {
51767 if (pConverter->channelsIn < pConverter->channelsOut) {
51768 /* Do resampling first, if necessary. */
51769 MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE);
51770
51771 if (pConverter->hasResampler) {
51773 } else {
51775 }
51776 } else {
51777 /* Do channel conversion first, if necessary. */
51778 if (pConverter->hasChannelConverter) {
51779 if (pConverter->hasResampler) {
51781 } else {
51783 }
51784 } else {
51785 /* Channel routing not required. */
51786 if (pConverter->hasResampler) {
51788 } else {
51790 }
51791 }
51792 }
51793 }
51794
51795 return MA_SUCCESS;
51796}
51797
51798MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter)
51799{
51800 ma_result result;
51801 size_t heapSizeInBytes;
51802 void* pHeap;
51803
51804 result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes);
51805 if (result != MA_SUCCESS) {
51806 return result;
51807 }
51808
51809 if (heapSizeInBytes > 0) {
51810 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
51811 if (pHeap == NULL) {
51812 return MA_OUT_OF_MEMORY;
51813 }
51814 } else {
51815 pHeap = NULL;
51816 }
51817
51818 result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter);
51819 if (result != MA_SUCCESS) {
51820 ma_free(pHeap, pAllocationCallbacks);
51821 return result;
51822 }
51823
51824 pConverter->_ownsHeap = MA_TRUE;
51825 return MA_SUCCESS;
51826}
51827
51828MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks)
51829{
51830 if (pConverter == NULL) {
51831 return;
51832 }
51833
51834 if (pConverter->hasResampler) {
51835 ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks);
51836 }
51837
51838 ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks);
51839
51840 if (pConverter->_ownsHeap) {
51841 ma_free(pConverter->_pHeap, pAllocationCallbacks);
51842 }
51843}
51844
51845static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
51846{
51847 ma_uint64 frameCountIn;
51848 ma_uint64 frameCountOut;
51849 ma_uint64 frameCount;
51850
51851 MA_ASSERT(pConverter != NULL);
51852
51853 frameCountIn = 0;
51854 if (pFrameCountIn != NULL) {
51855 frameCountIn = *pFrameCountIn;
51856 }
51857
51858 frameCountOut = 0;
51859 if (pFrameCountOut != NULL) {
51860 frameCountOut = *pFrameCountOut;
51861 }
51862
51863 frameCount = ma_min(frameCountIn, frameCountOut);
51864
51865 if (pFramesOut != NULL) {
51866 if (pFramesIn != NULL) {
51867 ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
51868 } else {
51869 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
51870 }
51871 }
51872
51873 if (pFrameCountIn != NULL) {
51874 *pFrameCountIn = frameCount;
51875 }
51876 if (pFrameCountOut != NULL) {
51877 *pFrameCountOut = frameCount;
51878 }
51879
51880 return MA_SUCCESS;
51881}
51882
51883static 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)
51884{
51885 ma_uint64 frameCountIn;
51886 ma_uint64 frameCountOut;
51887 ma_uint64 frameCount;
51888
51889 MA_ASSERT(pConverter != NULL);
51890
51891 frameCountIn = 0;
51892 if (pFrameCountIn != NULL) {
51893 frameCountIn = *pFrameCountIn;
51894 }
51895
51896 frameCountOut = 0;
51897 if (pFrameCountOut != NULL) {
51898 frameCountOut = *pFrameCountOut;
51899 }
51900
51901 frameCount = ma_min(frameCountIn, frameCountOut);
51902
51903 if (pFramesOut != NULL) {
51904 if (pFramesIn != NULL) {
51905 ma_convert_pcm_frames_format(pFramesOut, pConverter->formatOut, pFramesIn, pConverter->formatIn, frameCount, pConverter->channelsIn, pConverter->ditherMode);
51906 } else {
51907 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
51908 }
51909 }
51910
51911 if (pFrameCountIn != NULL) {
51912 *pFrameCountIn = frameCount;
51913 }
51914 if (pFrameCountOut != NULL) {
51915 *pFrameCountOut = frameCount;
51916 }
51917
51918 return MA_SUCCESS;
51919}
51920
51921
51922static 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)
51923{
51924 ma_result result = MA_SUCCESS;
51925 ma_uint64 frameCountIn;
51926 ma_uint64 frameCountOut;
51927 ma_uint64 framesProcessedIn;
51928 ma_uint64 framesProcessedOut;
51929
51930 MA_ASSERT(pConverter != NULL);
51931
51932 frameCountIn = 0;
51933 if (pFrameCountIn != NULL) {
51934 frameCountIn = *pFrameCountIn;
51935 }
51936
51937 frameCountOut = 0;
51938 if (pFrameCountOut != NULL) {
51939 frameCountOut = *pFrameCountOut;
51940 }
51941
51942 framesProcessedIn = 0;
51943 framesProcessedOut = 0;
51944
51945 while (framesProcessedOut < frameCountOut) {
51946 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
51947 const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
51948 const void* pFramesInThisIteration;
51949 /* */ void* pFramesOutThisIteration;
51950 ma_uint64 frameCountInThisIteration;
51951 ma_uint64 frameCountOutThisIteration;
51952
51953 if (pFramesIn != NULL) {
51954 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
51955 } else {
51956 pFramesInThisIteration = NULL;
51957 }
51958
51959 if (pFramesOut != NULL) {
51960 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
51961 } else {
51962 pFramesOutThisIteration = NULL;
51963 }
51964
51965 /* Do a pre format conversion if necessary. */
51966 if (pConverter->hasPreFormatConversion) {
51967 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
51968 const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
51969
51970 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
51971 if (frameCountInThisIteration > tempBufferInCap) {
51972 frameCountInThisIteration = tempBufferInCap;
51973 }
51974
51975 if (pConverter->hasPostFormatConversion) {
51976 if (frameCountInThisIteration > tempBufferOutCap) {
51977 frameCountInThisIteration = tempBufferOutCap;
51978 }
51979 }
51980
51981 if (pFramesInThisIteration != NULL) {
51982 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pFramesInThisIteration, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
51983 } else {
51984 MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
51985 }
51986
51987 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
51988
51989 if (pConverter->hasPostFormatConversion) {
51990 /* Both input and output conversion required. Output to the temp buffer. */
51991 if (frameCountOutThisIteration > tempBufferOutCap) {
51992 frameCountOutThisIteration = tempBufferOutCap;
51993 }
51994
51995 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
51996 } else {
51997 /* Only pre-format required. Output straight to the output buffer. */
51998 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration);
51999 }
52000
52001 if (result != MA_SUCCESS) {
52002 break;
52003 }
52004 } else {
52005 /* No pre-format required. Just read straight from the input buffer. */
52006 MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
52007
52008 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
52009 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
52010 if (frameCountOutThisIteration > tempBufferOutCap) {
52011 frameCountOutThisIteration = tempBufferOutCap;
52012 }
52013
52014 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
52015 if (result != MA_SUCCESS) {
52016 break;
52017 }
52018 }
52019
52020 /* If we are doing a post format conversion we need to do that now. */
52021 if (pConverter->hasPostFormatConversion) {
52022 if (pFramesOutThisIteration != NULL) {
52023 ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->resampler.channels, pConverter->ditherMode);
52024 }
52025 }
52026
52027 framesProcessedIn += frameCountInThisIteration;
52028 framesProcessedOut += frameCountOutThisIteration;
52029
52030 MA_ASSERT(framesProcessedIn <= frameCountIn);
52031 MA_ASSERT(framesProcessedOut <= frameCountOut);
52032
52033 if (frameCountOutThisIteration == 0) {
52034 break; /* Consumed all of our input data. */
52035 }
52036 }
52037
52038 if (pFrameCountIn != NULL) {
52039 *pFrameCountIn = framesProcessedIn;
52040 }
52041 if (pFrameCountOut != NULL) {
52042 *pFrameCountOut = framesProcessedOut;
52043 }
52044
52045 return result;
52046}
52047
52048static 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)
52049{
52050 MA_ASSERT(pConverter != NULL);
52051
52052 if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
52053 /* Neither pre- nor post-format required. This is simple case where only resampling is required. */
52054 return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
52055 } else {
52056 /* Format conversion required. */
52057 return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
52058 }
52059}
52060
52061static 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)
52062{
52063 ma_result result;
52064 ma_uint64 frameCountIn;
52065 ma_uint64 frameCountOut;
52066 ma_uint64 frameCount;
52067
52068 MA_ASSERT(pConverter != NULL);
52069
52070 frameCountIn = 0;
52071 if (pFrameCountIn != NULL) {
52072 frameCountIn = *pFrameCountIn;
52073 }
52074
52075 frameCountOut = 0;
52076 if (pFrameCountOut != NULL) {
52077 frameCountOut = *pFrameCountOut;
52078 }
52079
52080 frameCount = ma_min(frameCountIn, frameCountOut);
52081
52082 if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
52083 /* No format conversion required. */
52084 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount);
52085 if (result != MA_SUCCESS) {
52086 return result;
52087 }
52088 } else {
52089 /* Format conversion required. */
52090 ma_uint64 framesProcessed = 0;
52091
52092 while (framesProcessed < frameCount) {
52093 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
52094 const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
52095 const void* pFramesInThisIteration;
52096 /* */ void* pFramesOutThisIteration;
52097 ma_uint64 frameCountThisIteration;
52098
52099 if (pFramesIn != NULL) {
52100 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
52101 } else {
52102 pFramesInThisIteration = NULL;
52103 }
52104
52105 if (pFramesOut != NULL) {
52106 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
52107 } else {
52108 pFramesOutThisIteration = NULL;
52109 }
52110
52111 /* Do a pre format conversion if necessary. */
52112 if (pConverter->hasPreFormatConversion) {
52113 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
52114 const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
52115
52116 frameCountThisIteration = (frameCount - framesProcessed);
52117 if (frameCountThisIteration > tempBufferInCap) {
52118 frameCountThisIteration = tempBufferInCap;
52119 }
52120
52121 if (pConverter->hasPostFormatConversion) {
52122 if (frameCountThisIteration > tempBufferOutCap) {
52123 frameCountThisIteration = tempBufferOutCap;
52124 }
52125 }
52126
52127 if (pFramesInThisIteration != NULL) {
52128 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->formatIn, frameCountThisIteration, pConverter->channelsIn, pConverter->ditherMode);
52129 } else {
52130 MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
52131 }
52132
52133 if (pConverter->hasPostFormatConversion) {
52134 /* Both input and output conversion required. Output to the temp buffer. */
52135 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration);
52136 } else {
52137 /* Only pre-format required. Output straight to the output buffer. */
52138 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration);
52139 }
52140
52141 if (result != MA_SUCCESS) {
52142 break;
52143 }
52144 } else {
52145 /* No pre-format required. Just read straight from the input buffer. */
52146 MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
52147
52148 frameCountThisIteration = (frameCount - framesProcessed);
52149 if (frameCountThisIteration > tempBufferOutCap) {
52150 frameCountThisIteration = tempBufferOutCap;
52151 }
52152
52153 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration);
52154 if (result != MA_SUCCESS) {
52155 break;
52156 }
52157 }
52158
52159 /* If we are doing a post format conversion we need to do that now. */
52160 if (pConverter->hasPostFormatConversion) {
52161 if (pFramesOutThisIteration != NULL) {
52162 ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode);
52163 }
52164 }
52165
52166 framesProcessed += frameCountThisIteration;
52167 }
52168 }
52169
52170 if (pFrameCountIn != NULL) {
52171 *pFrameCountIn = frameCount;
52172 }
52173 if (pFrameCountOut != NULL) {
52174 *pFrameCountOut = frameCount;
52175 }
52176
52177 return MA_SUCCESS;
52178}
52179
52180static 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)
52181{
52182 ma_result result;
52183 ma_uint64 frameCountIn;
52184 ma_uint64 frameCountOut;
52185 ma_uint64 framesProcessedIn;
52186 ma_uint64 framesProcessedOut;
52187 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
52188 ma_uint64 tempBufferInCap;
52189 ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
52190 ma_uint64 tempBufferMidCap;
52191 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
52192 ma_uint64 tempBufferOutCap;
52193
52194 MA_ASSERT(pConverter != NULL);
52195 MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format);
52196 MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn);
52197 MA_ASSERT(pConverter->resampler.channels < pConverter->channelConverter.channelsOut);
52198
52199 frameCountIn = 0;
52200 if (pFrameCountIn != NULL) {
52201 frameCountIn = *pFrameCountIn;
52202 }
52203
52204 frameCountOut = 0;
52205 if (pFrameCountOut != NULL) {
52206 frameCountOut = *pFrameCountOut;
52207 }
52208
52209 framesProcessedIn = 0;
52210 framesProcessedOut = 0;
52211
52212 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
52213 tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
52214 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
52215
52216 while (framesProcessedOut < frameCountOut) {
52217 ma_uint64 frameCountInThisIteration;
52218 ma_uint64 frameCountOutThisIteration;
52219 const void* pRunningFramesIn = NULL;
52220 void* pRunningFramesOut = NULL;
52221 const void* pResampleBufferIn;
52222 void* pChannelsBufferOut;
52223
52224 if (pFramesIn != NULL) {
52225 pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
52226 }
52227 if (pFramesOut != NULL) {
52228 pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
52229 }
52230
52231 /* Run input data through the resampler and output it to the temporary buffer. */
52232 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
52233
52234 if (pConverter->hasPreFormatConversion) {
52235 if (frameCountInThisIteration > tempBufferInCap) {
52236 frameCountInThisIteration = tempBufferInCap;
52237 }
52238 }
52239
52240 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
52241 if (frameCountOutThisIteration > tempBufferMidCap) {
52242 frameCountOutThisIteration = tempBufferMidCap;
52243 }
52244
52245 /* We can't read more frames than can fit in the output buffer. */
52246 if (pConverter->hasPostFormatConversion) {
52247 if (frameCountOutThisIteration > tempBufferOutCap) {
52248 frameCountOutThisIteration = tempBufferOutCap;
52249 }
52250 }
52251
52252 /* 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. */
52253
52254 /*
52255 We need to try to predict how many input frames will be required for the resampler. If the
52256 resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further
52257 off we are from this, the more wasted format conversions we'll end up doing.
52258 */
52259 #if 1
52260 {
52261 ma_uint64 requiredInputFrameCount;
52262
52263 result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount);
52264 if (result != MA_SUCCESS) {
52265 /* Fall back to a best guess. */
52266 requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;
52267 }
52268
52269 if (frameCountInThisIteration > requiredInputFrameCount) {
52270 frameCountInThisIteration = requiredInputFrameCount;
52271 }
52272 }
52273 #endif
52274
52275 if (pConverter->hasPreFormatConversion) {
52276 if (pFramesIn != NULL) {
52277 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
52278 pResampleBufferIn = pTempBufferIn;
52279 } else {
52280 pResampleBufferIn = NULL;
52281 }
52282 } else {
52283 pResampleBufferIn = pRunningFramesIn;
52284 }
52285
52286 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration);
52287 if (result != MA_SUCCESS) {
52288 return result;
52289 }
52290
52291
52292 /*
52293 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
52294 this part if we have an output buffer.
52295 */
52296 if (pFramesOut != NULL) {
52297 if (pConverter->hasPostFormatConversion) {
52298 pChannelsBufferOut = pTempBufferOut;
52299 } else {
52300 pChannelsBufferOut = pRunningFramesOut;
52301 }
52302
52303 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration);
52304 if (result != MA_SUCCESS) {
52305 return result;
52306 }
52307
52308 /* Finally we do post format conversion. */
52309 if (pConverter->hasPostFormatConversion) {
52310 ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode);
52311 }
52312 }
52313
52314
52315 framesProcessedIn += frameCountInThisIteration;
52316 framesProcessedOut += frameCountOutThisIteration;
52317
52318 MA_ASSERT(framesProcessedIn <= frameCountIn);
52319 MA_ASSERT(framesProcessedOut <= frameCountOut);
52320
52321 if (frameCountOutThisIteration == 0) {
52322 break; /* Consumed all of our input data. */
52323 }
52324 }
52325
52326 if (pFrameCountIn != NULL) {
52327 *pFrameCountIn = framesProcessedIn;
52328 }
52329 if (pFrameCountOut != NULL) {
52330 *pFrameCountOut = framesProcessedOut;
52331 }
52332
52333 return MA_SUCCESS;
52334}
52335
52336static 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)
52337{
52338 ma_result result;
52339 ma_uint64 frameCountIn;
52340 ma_uint64 frameCountOut;
52341 ma_uint64 framesProcessedIn;
52342 ma_uint64 framesProcessedOut;
52343 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
52344 ma_uint64 tempBufferInCap;
52345 ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
52346 ma_uint64 tempBufferMidCap;
52347 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
52348 ma_uint64 tempBufferOutCap;
52349
52350 MA_ASSERT(pConverter != NULL);
52351 MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format);
52352 MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut);
52353 MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn);
52354
52355 frameCountIn = 0;
52356 if (pFrameCountIn != NULL) {
52357 frameCountIn = *pFrameCountIn;
52358 }
52359
52360 frameCountOut = 0;
52361 if (pFrameCountOut != NULL) {
52362 frameCountOut = *pFrameCountOut;
52363 }
52364
52365 framesProcessedIn = 0;
52366 framesProcessedOut = 0;
52367
52368 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
52369 tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
52370 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
52371
52372 while (framesProcessedOut < frameCountOut) {
52373 ma_uint64 frameCountInThisIteration;
52374 ma_uint64 frameCountOutThisIteration;
52375 const void* pRunningFramesIn = NULL;
52376 void* pRunningFramesOut = NULL;
52377 const void* pChannelsBufferIn;
52378 void* pResampleBufferOut;
52379
52380 if (pFramesIn != NULL) {
52381 pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
52382 }
52383 if (pFramesOut != NULL) {
52384 pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
52385 }
52386
52387 /*
52388 Before doing any processing we need to determine how many frames we should try processing
52389 this iteration, for both input and output. The resampler requires us to perform format and
52390 channel conversion before passing any data into it. If we get our input count wrong, we'll
52391 end up peforming redundant pre-processing. This isn't the end of the world, but it does
52392 result in some inefficiencies proportionate to how far our estimates are off.
52393
52394 If the resampler has a means to calculate exactly how much we'll need, we'll use that.
52395 Otherwise we'll make a best guess. In order to do this, we'll need to calculate the output
52396 frame count first.
52397 */
52398 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
52399 if (frameCountOutThisIteration > tempBufferMidCap) {
52400 frameCountOutThisIteration = tempBufferMidCap;
52401 }
52402
52403 if (pConverter->hasPostFormatConversion) {
52404 if (frameCountOutThisIteration > tempBufferOutCap) {
52405 frameCountOutThisIteration = tempBufferOutCap;
52406 }
52407 }
52408
52409 /* Now that we have the output frame count we can determine the input frame count. */
52410 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
52411 if (pConverter->hasPreFormatConversion) {
52412 if (frameCountInThisIteration > tempBufferInCap) {
52413 frameCountInThisIteration = tempBufferInCap;
52414 }
52415 }
52416
52417 if (frameCountInThisIteration > tempBufferMidCap) {
52418 frameCountInThisIteration = tempBufferMidCap;
52419 }
52420
52421 #if 1
52422 {
52423 ma_uint64 requiredInputFrameCount;
52424
52425 result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount);
52426 if (result != MA_SUCCESS) {
52427 /* Fall back to a best guess. */
52428 requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;
52429 }
52430
52431 if (frameCountInThisIteration > requiredInputFrameCount) {
52432 frameCountInThisIteration = requiredInputFrameCount;
52433 }
52434 }
52435 #endif
52436
52437
52438 /* Pre format conversion. */
52439 if (pConverter->hasPreFormatConversion) {
52440 if (pRunningFramesIn != NULL) {
52441 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
52442 pChannelsBufferIn = pTempBufferIn;
52443 } else {
52444 pChannelsBufferIn = NULL;
52445 }
52446 } else {
52447 pChannelsBufferIn = pRunningFramesIn;
52448 }
52449
52450
52451 /* Channel conversion. */
52452 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration);
52453 if (result != MA_SUCCESS) {
52454 return result;
52455 }
52456
52457
52458 /* Resampling. */
52459 if (pConverter->hasPostFormatConversion) {
52460 pResampleBufferOut = pTempBufferOut;
52461 } else {
52462 pResampleBufferOut = pRunningFramesOut;
52463 }
52464
52465 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration);
52466 if (result != MA_SUCCESS) {
52467 return result;
52468 }
52469
52470
52471 /* Post format conversion. */
52472 if (pConverter->hasPostFormatConversion) {
52473 if (pRunningFramesOut != NULL) {
52474 ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode);
52475 }
52476 }
52477
52478
52479 framesProcessedIn += frameCountInThisIteration;
52480 framesProcessedOut += frameCountOutThisIteration;
52481
52482 MA_ASSERT(framesProcessedIn <= frameCountIn);
52483 MA_ASSERT(framesProcessedOut <= frameCountOut);
52484
52485 if (frameCountOutThisIteration == 0) {
52486 break; /* Consumed all of our input data. */
52487 }
52488 }
52489
52490 if (pFrameCountIn != NULL) {
52491 *pFrameCountIn = framesProcessedIn;
52492 }
52493 if (pFrameCountOut != NULL) {
52494 *pFrameCountOut = framesProcessedOut;
52495 }
52496
52497 return MA_SUCCESS;
52498}
52499
52500MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
52501{
52502 if (pConverter == NULL) {
52503 return MA_INVALID_ARGS;
52504 }
52505
52506 switch (pConverter->executionPath)
52507 {
52508 case ma_data_converter_execution_path_passthrough: return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
52509 case ma_data_converter_execution_path_format_only: return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
52510 case ma_data_converter_execution_path_channels_only: return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
52511 case ma_data_converter_execution_path_resample_only: return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
52512 case ma_data_converter_execution_path_resample_first: return ma_data_converter_process_pcm_frames__resample_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
52513 case ma_data_converter_execution_path_channels_first: return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
52514 default: return MA_INVALID_OPERATION; /* Should never hit this. */
52515 }
52516}
52517
52518MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
52519{
52520 if (pConverter == NULL) {
52521 return MA_INVALID_ARGS;
52522 }
52523
52524 if (pConverter->hasResampler == MA_FALSE) {
52525 return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */
52526 }
52527
52528 return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut);
52529}
52530
52532{
52533 if (pConverter == NULL) {
52534 return MA_INVALID_ARGS;
52535 }
52536
52537 if (pConverter->hasResampler == MA_FALSE) {
52538 return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */
52539 }
52540
52541 return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut);
52542}
52543
52545{
52546 if (pConverter == NULL) {
52547 return 0;
52548 }
52549
52550 if (pConverter->hasResampler) {
52551 return ma_resampler_get_input_latency(&pConverter->resampler);
52552 }
52553
52554 return 0; /* No latency without a resampler. */
52555}
52556
52558{
52559 if (pConverter == NULL) {
52560 return 0;
52561 }
52562
52563 if (pConverter->hasResampler) {
52564 return ma_resampler_get_output_latency(&pConverter->resampler);
52565 }
52566
52567 return 0; /* No latency without a resampler. */
52568}
52569
52570MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
52571{
52572 if (pInputFrameCount == NULL) {
52573 return MA_INVALID_ARGS;
52574 }
52575
52576 *pInputFrameCount = 0;
52577
52578 if (pConverter == NULL) {
52579 return MA_INVALID_ARGS;
52580 }
52581
52582 if (pConverter->hasResampler) {
52583 return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount);
52584 } else {
52585 *pInputFrameCount = outputFrameCount; /* 1:1 */
52586 return MA_SUCCESS;
52587 }
52588}
52589
52590MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
52591{
52592 if (pOutputFrameCount == NULL) {
52593 return MA_INVALID_ARGS;
52594 }
52595
52596 *pOutputFrameCount = 0;
52597
52598 if (pConverter == NULL) {
52599 return MA_INVALID_ARGS;
52600 }
52601
52602 if (pConverter->hasResampler) {
52603 return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount);
52604 } else {
52605 *pOutputFrameCount = inputFrameCount; /* 1:1 */
52606 return MA_SUCCESS;
52607 }
52608}
52609
52610MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
52611{
52612 if (pConverter == NULL || pChannelMap == NULL) {
52613 return MA_INVALID_ARGS;
52614 }
52615
52616 if (pConverter->hasChannelConverter) {
52617 ma_channel_converter_get_output_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap);
52618 } else {
52619 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsOut);
52620 }
52621
52622 return MA_SUCCESS;
52623}
52624
52625MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
52626{
52627 if (pConverter == NULL || pChannelMap == NULL) {
52628 return MA_INVALID_ARGS;
52629 }
52630
52631 if (pConverter->hasChannelConverter) {
52632 ma_channel_converter_get_input_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap);
52633 } else {
52634 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsIn);
52635 }
52636
52637 return MA_SUCCESS;
52638}
52639
52640
52641
52642
52647static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex);
52648
52649MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)
52650{
52651 if (pChannelMap == NULL) {
52652 return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex);
52653 } else {
52654 if (channelIndex >= channelCount) {
52655 return MA_CHANNEL_NONE;
52656 }
52657
52658 return pChannelMap[channelIndex];
52659 }
52660}
52661
52662MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels)
52663{
52664 if (pChannelMap == NULL) {
52665 return;
52666 }
52667
52668 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels);
52669}
52670
52671
52672static ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex)
52673{
52674 if (channelCount == 0 || channelIndex >= channelCount) {
52675 return MA_CHANNEL_NONE;
52676 }
52677
52678 /* 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 */
52679 switch (channelCount)
52680 {
52681 case 0: return MA_CHANNEL_NONE;
52682
52683 case 1:
52684 {
52685 return MA_CHANNEL_MONO;
52686 } break;
52687
52688 case 2:
52689 {
52690 switch (channelIndex) {
52691 case 0: return MA_CHANNEL_FRONT_LEFT;
52692 case 1: return MA_CHANNEL_FRONT_RIGHT;
52693 }
52694 } break;
52695
52696 case 3: /* No defined, but best guess. */
52697 {
52698 switch (channelIndex) {
52699 case 0: return MA_CHANNEL_FRONT_LEFT;
52700 case 1: return MA_CHANNEL_FRONT_RIGHT;
52701 case 2: return MA_CHANNEL_FRONT_CENTER;
52702 }
52703 } break;
52704
52705 case 4:
52706 {
52707 switch (channelIndex) {
52708 #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP
52709 /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */
52710 case 0: return MA_CHANNEL_FRONT_LEFT;
52711 case 1: return MA_CHANNEL_FRONT_RIGHT;
52712 case 2: return MA_CHANNEL_FRONT_CENTER;
52713 case 3: return MA_CHANNEL_BACK_CENTER;
52714 #else
52715 /* Quad. */
52716 case 0: return MA_CHANNEL_FRONT_LEFT;
52717 case 1: return MA_CHANNEL_FRONT_RIGHT;
52718 case 2: return MA_CHANNEL_BACK_LEFT;
52719 case 3: return MA_CHANNEL_BACK_RIGHT;
52720 #endif
52721 }
52722 } break;
52723
52724 case 5: /* Not defined, but best guess. */
52725 {
52726 switch (channelIndex) {
52727 case 0: return MA_CHANNEL_FRONT_LEFT;
52728 case 1: return MA_CHANNEL_FRONT_RIGHT;
52729 case 2: return MA_CHANNEL_FRONT_CENTER;
52730 case 3: return MA_CHANNEL_BACK_LEFT;
52731 case 4: return MA_CHANNEL_BACK_RIGHT;
52732 }
52733 } break;
52734
52735 case 6:
52736 {
52737 switch (channelIndex) {
52738 case 0: return MA_CHANNEL_FRONT_LEFT;
52739 case 1: return MA_CHANNEL_FRONT_RIGHT;
52740 case 2: return MA_CHANNEL_FRONT_CENTER;
52741 case 3: return MA_CHANNEL_LFE;
52742 case 4: return MA_CHANNEL_SIDE_LEFT;
52743 case 5: return MA_CHANNEL_SIDE_RIGHT;
52744 }
52745 } break;
52746
52747 case 7: /* Not defined, but best guess. */
52748 {
52749 switch (channelIndex) {
52750 case 0: return MA_CHANNEL_FRONT_LEFT;
52751 case 1: return MA_CHANNEL_FRONT_RIGHT;
52752 case 2: return MA_CHANNEL_FRONT_CENTER;
52753 case 3: return MA_CHANNEL_LFE;
52754 case 4: return MA_CHANNEL_BACK_CENTER;
52755 case 5: return MA_CHANNEL_SIDE_LEFT;
52756 case 6: return MA_CHANNEL_SIDE_RIGHT;
52757 }
52758 } break;
52759
52760 case 8:
52761 default:
52762 {
52763 switch (channelIndex) {
52764 case 0: return MA_CHANNEL_FRONT_LEFT;
52765 case 1: return MA_CHANNEL_FRONT_RIGHT;
52766 case 2: return MA_CHANNEL_FRONT_CENTER;
52767 case 3: return MA_CHANNEL_LFE;
52768 case 4: return MA_CHANNEL_BACK_LEFT;
52769 case 5: return MA_CHANNEL_BACK_RIGHT;
52770 case 6: return MA_CHANNEL_SIDE_LEFT;
52771 case 7: return MA_CHANNEL_SIDE_RIGHT;
52772 }
52773 } break;
52774 }
52775
52776 if (channelCount > 8) {
52777 if (channelIndex < 32) { /* We have 32 AUX channels. */
52778 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
52779 }
52780 }
52781
52782 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
52783 return MA_CHANNEL_NONE;
52784}
52785
52786static ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex)
52787{
52788 switch (channelCount)
52789 {
52790 case 0: return MA_CHANNEL_NONE;
52791
52792 case 1:
52793 {
52794 return MA_CHANNEL_MONO;
52795 } break;
52796
52797 case 2:
52798 {
52799 switch (channelIndex) {
52800 case 0: return MA_CHANNEL_FRONT_LEFT;
52801 case 1: return MA_CHANNEL_FRONT_RIGHT;
52802 }
52803 } break;
52804
52805 case 3:
52806 {
52807 switch (channelIndex) {
52808 case 0: return MA_CHANNEL_FRONT_LEFT;
52809 case 1: return MA_CHANNEL_FRONT_RIGHT;
52810 case 2: return MA_CHANNEL_FRONT_CENTER;
52811 }
52812 } break;
52813
52814 case 4:
52815 {
52816 switch (channelIndex) {
52817 case 0: return MA_CHANNEL_FRONT_LEFT;
52818 case 1: return MA_CHANNEL_FRONT_RIGHT;
52819 case 2: return MA_CHANNEL_BACK_LEFT;
52820 case 3: return MA_CHANNEL_BACK_RIGHT;
52821 }
52822 } break;
52823
52824 case 5:
52825 {
52826 switch (channelIndex) {
52827 case 0: return MA_CHANNEL_FRONT_LEFT;
52828 case 1: return MA_CHANNEL_FRONT_RIGHT;
52829 case 2: return MA_CHANNEL_BACK_LEFT;
52830 case 3: return MA_CHANNEL_BACK_RIGHT;
52831 case 4: return MA_CHANNEL_FRONT_CENTER;
52832 }
52833 } break;
52834
52835 case 6:
52836 {
52837 switch (channelIndex) {
52838 case 0: return MA_CHANNEL_FRONT_LEFT;
52839 case 1: return MA_CHANNEL_FRONT_RIGHT;
52840 case 2: return MA_CHANNEL_BACK_LEFT;
52841 case 3: return MA_CHANNEL_BACK_RIGHT;
52842 case 4: return MA_CHANNEL_FRONT_CENTER;
52843 case 5: return MA_CHANNEL_LFE;
52844 }
52845 } break;
52846
52847 case 7:
52848 {
52849 switch (channelIndex) {
52850 case 0: return MA_CHANNEL_FRONT_LEFT;
52851 case 1: return MA_CHANNEL_FRONT_RIGHT;
52852 case 2: return MA_CHANNEL_BACK_LEFT;
52853 case 3: return MA_CHANNEL_BACK_RIGHT;
52854 case 4: return MA_CHANNEL_FRONT_CENTER;
52855 case 5: return MA_CHANNEL_LFE;
52856 case 6: return MA_CHANNEL_BACK_CENTER;
52857 }
52858 } break;
52859
52860 case 8:
52861 default:
52862 {
52863 switch (channelIndex) {
52864 case 0: return MA_CHANNEL_FRONT_LEFT;
52865 case 1: return MA_CHANNEL_FRONT_RIGHT;
52866 case 2: return MA_CHANNEL_BACK_LEFT;
52867 case 3: return MA_CHANNEL_BACK_RIGHT;
52868 case 4: return MA_CHANNEL_FRONT_CENTER;
52869 case 5: return MA_CHANNEL_LFE;
52870 case 6: return MA_CHANNEL_SIDE_LEFT;
52871 case 7: return MA_CHANNEL_SIDE_RIGHT;
52872 }
52873 } break;
52874 }
52875
52876 if (channelCount > 8) {
52877 if (channelIndex < 32) { /* We have 32 AUX channels. */
52878 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
52879 }
52880 }
52881
52882 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
52883 return MA_CHANNEL_NONE;
52884}
52885
52886static ma_channel ma_channel_map_init_standard_channel_rfc3551(ma_uint32 channelCount, ma_uint32 channelIndex)
52887{
52888 switch (channelCount)
52889 {
52890 case 0: return MA_CHANNEL_NONE;
52891
52892 case 1:
52893 {
52894 return MA_CHANNEL_MONO;
52895 } break;
52896
52897 case 2:
52898 {
52899 switch (channelIndex) {
52900 case 0: return MA_CHANNEL_FRONT_LEFT;
52901 case 1: return MA_CHANNEL_FRONT_RIGHT;
52902 }
52903 } break;
52904
52905 case 3:
52906 {
52907 switch (channelIndex) {
52908 case 0: return MA_CHANNEL_FRONT_LEFT;
52909 case 1: return MA_CHANNEL_FRONT_RIGHT;
52910 case 2: return MA_CHANNEL_FRONT_CENTER;
52911 }
52912 } break;
52913
52914 case 4:
52915 {
52916 switch (channelIndex) {
52917 case 0: return MA_CHANNEL_FRONT_LEFT;
52918 case 2: return MA_CHANNEL_FRONT_CENTER;
52919 case 1: return MA_CHANNEL_FRONT_RIGHT;
52920 case 3: return MA_CHANNEL_BACK_CENTER;
52921 }
52922 } break;
52923
52924 case 5:
52925 {
52926 switch (channelIndex) {
52927 case 0: return MA_CHANNEL_FRONT_LEFT;
52928 case 1: return MA_CHANNEL_FRONT_RIGHT;
52929 case 2: return MA_CHANNEL_FRONT_CENTER;
52930 case 3: return MA_CHANNEL_BACK_LEFT;
52931 case 4: return MA_CHANNEL_BACK_RIGHT;
52932 }
52933 } break;
52934
52935 case 6:
52936 default:
52937 {
52938 switch (channelIndex) {
52939 case 0: return MA_CHANNEL_FRONT_LEFT;
52940 case 1: return MA_CHANNEL_SIDE_LEFT;
52941 case 2: return MA_CHANNEL_FRONT_CENTER;
52942 case 3: return MA_CHANNEL_FRONT_RIGHT;
52943 case 4: return MA_CHANNEL_SIDE_RIGHT;
52944 case 5: return MA_CHANNEL_BACK_CENTER;
52945 }
52946 } break;
52947 }
52948
52949 if (channelCount > 6) {
52950 if (channelIndex < 32) { /* We have 32 AUX channels. */
52951 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6));
52952 }
52953 }
52954
52955 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
52956 return MA_CHANNEL_NONE;
52957}
52958
52959static ma_channel ma_channel_map_init_standard_channel_flac(ma_uint32 channelCount, ma_uint32 channelIndex)
52960{
52961 switch (channelCount)
52962 {
52963 case 0: return MA_CHANNEL_NONE;
52964
52965 case 1:
52966 {
52967 return MA_CHANNEL_MONO;
52968 } break;
52969
52970 case 2:
52971 {
52972 switch (channelIndex) {
52973 case 0: return MA_CHANNEL_FRONT_LEFT;
52974 case 1: return MA_CHANNEL_FRONT_RIGHT;
52975 }
52976 } break;
52977
52978 case 3:
52979 {
52980 switch (channelIndex) {
52981 case 0: return MA_CHANNEL_FRONT_LEFT;
52982 case 1: return MA_CHANNEL_FRONT_RIGHT;
52983 case 2: return MA_CHANNEL_FRONT_CENTER;
52984 }
52985 } break;
52986
52987 case 4:
52988 {
52989 switch (channelIndex) {
52990 case 0: return MA_CHANNEL_FRONT_LEFT;
52991 case 1: return MA_CHANNEL_FRONT_RIGHT;
52992 case 2: return MA_CHANNEL_BACK_LEFT;
52993 case 3: return MA_CHANNEL_BACK_RIGHT;
52994 }
52995 } break;
52996
52997 case 5:
52998 {
52999 switch (channelIndex) {
53000 case 0: return MA_CHANNEL_FRONT_LEFT;
53001 case 1: return MA_CHANNEL_FRONT_RIGHT;
53002 case 2: return MA_CHANNEL_FRONT_CENTER;
53003 case 3: return MA_CHANNEL_BACK_LEFT;
53004 case 4: return MA_CHANNEL_BACK_RIGHT;
53005 }
53006 } break;
53007
53008 case 6:
53009 {
53010 switch (channelIndex) {
53011 case 0: return MA_CHANNEL_FRONT_LEFT;
53012 case 1: return MA_CHANNEL_FRONT_RIGHT;
53013 case 2: return MA_CHANNEL_FRONT_CENTER;
53014 case 3: return MA_CHANNEL_LFE;
53015 case 4: return MA_CHANNEL_BACK_LEFT;
53016 case 5: return MA_CHANNEL_BACK_RIGHT;
53017 }
53018 } break;
53019
53020 case 7:
53021 {
53022 switch (channelIndex) {
53023 case 0: return MA_CHANNEL_FRONT_LEFT;
53024 case 1: return MA_CHANNEL_FRONT_RIGHT;
53025 case 2: return MA_CHANNEL_FRONT_CENTER;
53026 case 3: return MA_CHANNEL_LFE;
53027 case 4: return MA_CHANNEL_BACK_CENTER;
53028 case 5: return MA_CHANNEL_SIDE_LEFT;
53029 case 6: return MA_CHANNEL_SIDE_RIGHT;
53030 }
53031 } break;
53032
53033 case 8:
53034 default:
53035 {
53036 switch (channelIndex) {
53037 case 0: return MA_CHANNEL_FRONT_LEFT;
53038 case 1: return MA_CHANNEL_FRONT_RIGHT;
53039 case 2: return MA_CHANNEL_FRONT_CENTER;
53040 case 3: return MA_CHANNEL_LFE;
53041 case 4: return MA_CHANNEL_BACK_LEFT;
53042 case 5: return MA_CHANNEL_BACK_RIGHT;
53043 case 6: return MA_CHANNEL_SIDE_LEFT;
53044 case 7: return MA_CHANNEL_SIDE_RIGHT;
53045 }
53046 } break;
53047 }
53048
53049 if (channelCount > 8) {
53050 if (channelIndex < 32) { /* We have 32 AUX channels. */
53051 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
53052 }
53053 }
53054
53055 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
53056 return MA_CHANNEL_NONE;
53057}
53058
53059static ma_channel ma_channel_map_init_standard_channel_vorbis(ma_uint32 channelCount, ma_uint32 channelIndex)
53060{
53061 switch (channelCount)
53062 {
53063 case 0: return MA_CHANNEL_NONE;
53064
53065 case 1:
53066 {
53067 return MA_CHANNEL_MONO;
53068 } break;
53069
53070 case 2:
53071 {
53072 switch (channelIndex) {
53073 case 0: return MA_CHANNEL_FRONT_LEFT;
53074 case 1: return MA_CHANNEL_FRONT_RIGHT;
53075 }
53076 } break;
53077
53078 case 3:
53079 {
53080 switch (channelIndex) {
53081 case 0: return MA_CHANNEL_FRONT_LEFT;
53082 case 1: return MA_CHANNEL_FRONT_CENTER;
53083 case 2: return MA_CHANNEL_FRONT_RIGHT;
53084 }
53085 } break;
53086
53087 case 4:
53088 {
53089 switch (channelIndex) {
53090 case 0: return MA_CHANNEL_FRONT_LEFT;
53091 case 1: return MA_CHANNEL_FRONT_RIGHT;
53092 case 2: return MA_CHANNEL_BACK_LEFT;
53093 case 3: return MA_CHANNEL_BACK_RIGHT;
53094 }
53095 } break;
53096
53097 case 5:
53098 {
53099 switch (channelIndex) {
53100 case 0: return MA_CHANNEL_FRONT_LEFT;
53101 case 1: return MA_CHANNEL_FRONT_CENTER;
53102 case 2: return MA_CHANNEL_FRONT_RIGHT;
53103 case 3: return MA_CHANNEL_BACK_LEFT;
53104 case 4: return MA_CHANNEL_BACK_RIGHT;
53105 }
53106 } break;
53107
53108 case 6:
53109 {
53110 switch (channelIndex) {
53111 case 0: return MA_CHANNEL_FRONT_LEFT;
53112 case 1: return MA_CHANNEL_FRONT_CENTER;
53113 case 2: return MA_CHANNEL_FRONT_RIGHT;
53114 case 3: return MA_CHANNEL_BACK_LEFT;
53115 case 4: return MA_CHANNEL_BACK_RIGHT;
53116 case 5: return MA_CHANNEL_LFE;
53117 }
53118 } break;
53119
53120 case 7:
53121 {
53122 switch (channelIndex) {
53123 case 0: return MA_CHANNEL_FRONT_LEFT;
53124 case 1: return MA_CHANNEL_FRONT_CENTER;
53125 case 2: return MA_CHANNEL_FRONT_RIGHT;
53126 case 3: return MA_CHANNEL_SIDE_LEFT;
53127 case 4: return MA_CHANNEL_SIDE_RIGHT;
53128 case 5: return MA_CHANNEL_BACK_CENTER;
53129 case 6: return MA_CHANNEL_LFE;
53130 }
53131 } break;
53132
53133 case 8:
53134 default:
53135 {
53136 switch (channelIndex) {
53137 case 0: return MA_CHANNEL_FRONT_LEFT;
53138 case 1: return MA_CHANNEL_FRONT_CENTER;
53139 case 2: return MA_CHANNEL_FRONT_RIGHT;
53140 case 3: return MA_CHANNEL_SIDE_LEFT;
53141 case 4: return MA_CHANNEL_SIDE_RIGHT;
53142 case 5: return MA_CHANNEL_BACK_LEFT;
53143 case 6: return MA_CHANNEL_BACK_RIGHT;
53144 case 7: return MA_CHANNEL_LFE;
53145 }
53146 } break;
53147 }
53148
53149 if (channelCount > 8) {
53150 if (channelIndex < 32) { /* We have 32 AUX channels. */
53151 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
53152 }
53153 }
53154
53155 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
53156 return MA_CHANNEL_NONE;
53157}
53158
53159static ma_channel ma_channel_map_init_standard_channel_sound4(ma_uint32 channelCount, ma_uint32 channelIndex)
53160{
53161 switch (channelCount)
53162 {
53163 case 0: return MA_CHANNEL_NONE;
53164
53165 case 1:
53166 {
53167 return MA_CHANNEL_MONO;
53168 } break;
53169
53170 case 2:
53171 {
53172 switch (channelIndex) {
53173 case 0: return MA_CHANNEL_FRONT_LEFT;
53174 case 1: return MA_CHANNEL_FRONT_RIGHT;
53175 }
53176 } break;
53177
53178 case 3:
53179 {
53180 switch (channelIndex) {
53181 case 0: return MA_CHANNEL_FRONT_LEFT;
53182 case 1: return MA_CHANNEL_FRONT_RIGHT;
53183 case 2: return MA_CHANNEL_FRONT_CENTER;
53184 }
53185 } break;
53186
53187 case 4:
53188 {
53189 switch (channelIndex) {
53190 case 0: return MA_CHANNEL_FRONT_LEFT;
53191 case 1: return MA_CHANNEL_FRONT_RIGHT;
53192 case 2: return MA_CHANNEL_BACK_LEFT;
53193 case 3: return MA_CHANNEL_BACK_RIGHT;
53194 }
53195 } break;
53196
53197 case 5:
53198 {
53199 switch (channelIndex) {
53200 case 0: return MA_CHANNEL_FRONT_LEFT;
53201 case 1: return MA_CHANNEL_FRONT_RIGHT;
53202 case 2: return MA_CHANNEL_FRONT_CENTER;
53203 case 3: return MA_CHANNEL_BACK_LEFT;
53204 case 4: return MA_CHANNEL_BACK_RIGHT;
53205 }
53206 } break;
53207
53208 case 6:
53209 {
53210 switch (channelIndex) {
53211 case 0: return MA_CHANNEL_FRONT_LEFT;
53212 case 1: return MA_CHANNEL_FRONT_CENTER;
53213 case 2: return MA_CHANNEL_FRONT_RIGHT;
53214 case 3: return MA_CHANNEL_BACK_LEFT;
53215 case 4: return MA_CHANNEL_BACK_RIGHT;
53216 case 5: return MA_CHANNEL_LFE;
53217 }
53218 } break;
53219
53220 case 7:
53221 {
53222 switch (channelIndex) {
53223 case 0: return MA_CHANNEL_FRONT_LEFT;
53224 case 1: return MA_CHANNEL_FRONT_CENTER;
53225 case 2: return MA_CHANNEL_FRONT_RIGHT;
53226 case 3: return MA_CHANNEL_SIDE_LEFT;
53227 case 4: return MA_CHANNEL_SIDE_RIGHT;
53228 case 5: return MA_CHANNEL_BACK_CENTER;
53229 case 6: return MA_CHANNEL_LFE;
53230 }
53231 } break;
53232
53233 case 8:
53234 default:
53235 {
53236 switch (channelIndex) {
53237 case 0: return MA_CHANNEL_FRONT_LEFT;
53238 case 1: return MA_CHANNEL_FRONT_CENTER;
53239 case 2: return MA_CHANNEL_FRONT_RIGHT;
53240 case 3: return MA_CHANNEL_SIDE_LEFT;
53241 case 4: return MA_CHANNEL_SIDE_RIGHT;
53242 case 5: return MA_CHANNEL_BACK_LEFT;
53243 case 6: return MA_CHANNEL_BACK_RIGHT;
53244 case 7: return MA_CHANNEL_LFE;
53245 }
53246 } break;
53247 }
53248
53249 if (channelCount > 8) {
53250 if (channelIndex < 32) { /* We have 32 AUX channels. */
53251 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
53252 }
53253 }
53254
53255 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
53256 return MA_CHANNEL_NONE;
53257}
53258
53259static ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex)
53260{
53261 switch (channelCount)
53262 {
53263 case 0: return MA_CHANNEL_NONE;
53264
53265 case 1:
53266 {
53267 return MA_CHANNEL_MONO;
53268 } break;
53269
53270 case 2:
53271 {
53272 switch (channelIndex) {
53273 case 0: return MA_CHANNEL_FRONT_LEFT;
53274 case 1: return MA_CHANNEL_FRONT_RIGHT;
53275 }
53276 } break;
53277
53278 case 3: /* No defined, but best guess. */
53279 {
53280 switch (channelIndex) {
53281 case 0: return MA_CHANNEL_FRONT_LEFT;
53282 case 1: return MA_CHANNEL_FRONT_RIGHT;
53283 case 2: return MA_CHANNEL_FRONT_CENTER;
53284 }
53285 } break;
53286
53287 case 4:
53288 {
53289 switch (channelIndex) {
53290 case 0: return MA_CHANNEL_FRONT_LEFT;
53291 case 1: return MA_CHANNEL_FRONT_RIGHT;
53292 case 2: return MA_CHANNEL_BACK_LEFT;
53293 case 3: return MA_CHANNEL_BACK_RIGHT;
53294 }
53295 } break;
53296
53297 case 5: /* Not defined, but best guess. */
53298 {
53299 switch (channelIndex) {
53300 case 0: return MA_CHANNEL_FRONT_LEFT;
53301 case 1: return MA_CHANNEL_FRONT_RIGHT;
53302 case 2: return MA_CHANNEL_BACK_LEFT;
53303 case 3: return MA_CHANNEL_BACK_RIGHT;
53304 case 4: return MA_CHANNEL_FRONT_CENTER;
53305 }
53306 } break;
53307
53308 case 6:
53309 default:
53310 {
53311 switch (channelIndex) {
53312 case 0: return MA_CHANNEL_FRONT_LEFT;
53313 case 1: return MA_CHANNEL_FRONT_RIGHT;
53314 case 2: return MA_CHANNEL_BACK_LEFT;
53315 case 3: return MA_CHANNEL_BACK_RIGHT;
53316 case 4: return MA_CHANNEL_FRONT_CENTER;
53317 case 5: return MA_CHANNEL_LFE;
53318 }
53319 } break;
53320 }
53321
53322 if (channelCount > 6) {
53323 if (channelIndex < 32) { /* We have 32 AUX channels. */
53324 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6));
53325 }
53326 }
53327
53328 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
53329 return MA_CHANNEL_NONE;
53330}
53331
53332
53333static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)
53334{
53335 if (channelCount == 0 || channelIndex >= channelCount) {
53336 return MA_CHANNEL_NONE;
53337 }
53338
53339 switch (standardChannelMap)
53340 {
53342 {
53343 return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex);
53344 } break;
53345
53347 {
53348 return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex);
53349 } break;
53350
53352 {
53353 return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex);
53354 } break;
53355
53357 {
53358 return ma_channel_map_init_standard_channel_vorbis(channelCount, channelIndex);
53359 } break;
53360
53362 {
53363 return ma_channel_map_init_standard_channel_sound4(channelCount, channelIndex);
53364 } break;
53365
53367 {
53368 return ma_channel_map_init_standard_channel_sndio(channelCount, channelIndex);
53369 } break;
53370
53371 case ma_standard_channel_map_microsoft: /* Also default. */
53372 /*case ma_standard_channel_map_default;*/
53373 default:
53374 {
53375 return ma_channel_map_init_standard_channel_microsoft(channelCount, channelIndex);
53376 } break;
53377 }
53378}
53379
53380MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels)
53381{
53382 ma_uint32 iChannel;
53383
53384 if (pChannelMap == NULL || channelMapCap == 0 || channels == 0) {
53385 return;
53386 }
53387
53388 for (iChannel = 0; iChannel < channels; iChannel += 1) {
53389 if (channelMapCap == 0) {
53390 break; /* Ran out of room. */
53391 }
53392
53393 pChannelMap[0] = ma_channel_map_init_standard_channel(standardChannelMap, channels, iChannel);
53394 pChannelMap += 1;
53395 channelMapCap -= 1;
53396 }
53397}
53398
53399MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels)
53400{
53401 if (pOut != NULL && pIn != NULL && channels > 0) {
53402 MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels);
53403 }
53404}
53405
53406MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels)
53407{
53408 if (pOut == NULL || channels == 0) {
53409 return;
53410 }
53411
53412 if (pIn != NULL) {
53413 ma_channel_map_copy(pOut, pIn, channels);
53414 } else {
53415 ma_channel_map_init_standard(ma_standard_channel_map_default, pOut, channelMapCapOut, channels);
53416 }
53417}
53418
53419MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels)
53420{
53421 /* A channel count of 0 is invalid. */
53422 if (channels == 0) {
53423 return MA_FALSE;
53424 }
53425
53426 /* It does not make sense to have a mono channel when there is more than 1 channel. */
53427 if (channels > 1) {
53428 ma_uint32 iChannel;
53429 for (iChannel = 0; iChannel < channels; ++iChannel) {
53430 if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == MA_CHANNEL_MONO) {
53431 return MA_FALSE;
53432 }
53433 }
53434 }
53435
53436 return MA_TRUE;
53437}
53438
53439MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels)
53440{
53441 ma_uint32 iChannel;
53442
53443 if (pChannelMapA == pChannelMapB) {
53444 return MA_TRUE;
53445 }
53446
53447 for (iChannel = 0; iChannel < channels; ++iChannel) {
53448 if (ma_channel_map_get_channel(pChannelMapA, channels, iChannel) != ma_channel_map_get_channel(pChannelMapB, channels, iChannel)) {
53449 return MA_FALSE;
53450 }
53451 }
53452
53453 return MA_TRUE;
53454}
53455
53456MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels)
53457{
53458 ma_uint32 iChannel;
53459
53460 /* A null channel map is equivalent to the default channel map. */
53461 if (pChannelMap == NULL) {
53462 return MA_FALSE;
53463 }
53464
53465 for (iChannel = 0; iChannel < channels; ++iChannel) {
53466 if (pChannelMap[iChannel] != MA_CHANNEL_NONE) {
53467 return MA_FALSE;
53468 }
53469 }
53470
53471 return MA_TRUE;
53472}
53473
53474MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition)
53475{
53476 ma_uint32 iChannel;
53477
53478 for (iChannel = 0; iChannel < channels; ++iChannel) {
53479 if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) {
53480 return MA_TRUE;
53481 }
53482 }
53483
53484 return MA_FALSE;
53485}
53486
53487
53488
53489
53494MA_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)
53495{
53497
53498 config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut);
53499 config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
53500
53501 return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config);
53502}
53503
53504MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig)
53505{
53506 ma_result result;
53507 ma_data_converter converter;
53508
53509 if (frameCountIn == 0 || pConfig == NULL) {
53510 return 0;
53511 }
53512
53513 result = ma_data_converter_init(pConfig, NULL, &converter);
53514 if (result != MA_SUCCESS) {
53515 return 0; /* Failed to initialize the data converter. */
53516 }
53517
53518 if (pOut == NULL) {
53519 result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut);
53520 if (result != MA_SUCCESS) {
53521 if (result == MA_NOT_IMPLEMENTED) {
53522 /* No way to calculate the number of frames, so we'll need to brute force it and loop. */
53523 frameCountOut = 0;
53524
53525 while (frameCountIn > 0) {
53526 ma_uint64 framesProcessedIn = frameCountIn;
53527 ma_uint64 framesProcessedOut = 0xFFFFFFFF;
53528
53529 result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut);
53530 if (result != MA_SUCCESS) {
53531 break;
53532 }
53533
53534 frameCountIn -= framesProcessedIn;
53535 }
53536 }
53537 }
53538 } else {
53539 result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut);
53540 if (result != MA_SUCCESS) {
53541 frameCountOut = 0;
53542 }
53543 }
53544
53545 ma_data_converter_uninit(&converter, NULL);
53546 return frameCountOut;
53547}
53548
53549
53550
53555static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset)
53556{
53557 return encodedOffset & 0x7FFFFFFF;
53558}
53559
53560static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset)
53561{
53562 return encodedOffset & 0x80000000;
53563}
53564
53565static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB)
53566{
53567 MA_ASSERT(pRB != NULL);
53568 return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedReadOffset)));
53569}
53570
53571static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB)
53572{
53573 MA_ASSERT(pRB != NULL);
53574 return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedWriteOffset)));
53575}
53576
53577static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag)
53578{
53579 return offsetLoopFlag | offsetInBytes;
53580}
53581
53582static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag)
53583{
53584 MA_ASSERT(pOffsetInBytes != NULL);
53585 MA_ASSERT(pOffsetLoopFlag != NULL);
53586
53587 *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset);
53588 *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset);
53589}
53590
53591
53592MA_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)
53593{
53594 ma_result result;
53595 const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1);
53596
53597 if (pRB == NULL) {
53598 return MA_INVALID_ARGS;
53599 }
53600
53601 if (subbufferSizeInBytes == 0 || subbufferCount == 0) {
53602 return MA_INVALID_ARGS;
53603 }
53604
53605 if (subbufferSizeInBytes > maxSubBufferSize) {
53606 return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */
53607 }
53608
53609
53610 MA_ZERO_OBJECT(pRB);
53611
53612 result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks);
53613 if (result != MA_SUCCESS) {
53614 return result;
53615 }
53616
53617 pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes;
53618 pRB->subbufferCount = (ma_uint32)subbufferCount;
53619
53620 if (pOptionalPreallocatedBuffer != NULL) {
53621 pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes;
53622 pRB->pBuffer = pOptionalPreallocatedBuffer;
53623 } else {
53624 size_t bufferSizeInBytes;
53625
53626 /*
53627 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
53628 we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT.
53629 */
53631
53632 bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes;
53633 pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks);
53634 if (pRB->pBuffer == NULL) {
53635 return MA_OUT_OF_MEMORY;
53636 }
53637
53638 MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes);
53639 pRB->ownsBuffer = MA_TRUE;
53640 }
53641
53642 return MA_SUCCESS;
53643}
53644
53645MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB)
53646{
53647 return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
53648}
53649
53650MA_API void ma_rb_uninit(ma_rb* pRB)
53651{
53652 if (pRB == NULL) {
53653 return;
53654 }
53655
53656 if (pRB->ownsBuffer) {
53658 }
53659}
53660
53661MA_API void ma_rb_reset(ma_rb* pRB)
53662{
53663 if (pRB == NULL) {
53664 return;
53665 }
53666
53667 c89atomic_exchange_32(&pRB->encodedReadOffset, 0);
53668 c89atomic_exchange_32(&pRB->encodedWriteOffset, 0);
53669}
53670
53671MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
53672{
53673 ma_uint32 writeOffset;
53674 ma_uint32 writeOffsetInBytes;
53675 ma_uint32 writeOffsetLoopFlag;
53676 ma_uint32 readOffset;
53677 ma_uint32 readOffsetInBytes;
53678 ma_uint32 readOffsetLoopFlag;
53679 size_t bytesAvailable;
53680 size_t bytesRequested;
53681
53682 if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
53683 return MA_INVALID_ARGS;
53684 }
53685
53686 /* The returned buffer should never move ahead of the write pointer. */
53687 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
53688 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
53689
53690 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
53691 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
53692
53693 /*
53694 The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we
53695 can only read up to the write pointer. If not, we can only read up to the end of the buffer.
53696 */
53697 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
53698 bytesAvailable = writeOffsetInBytes - readOffsetInBytes;
53699 } else {
53700 bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes;
53701 }
53702
53703 bytesRequested = *pSizeInBytes;
53704 if (bytesRequested > bytesAvailable) {
53705 bytesRequested = bytesAvailable;
53706 }
53707
53708 *pSizeInBytes = bytesRequested;
53709 (*ppBufferOut) = ma_rb__get_read_ptr(pRB);
53710
53711 return MA_SUCCESS;
53712}
53713
53714MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes)
53715{
53716 ma_uint32 readOffset;
53717 ma_uint32 readOffsetInBytes;
53718 ma_uint32 readOffsetLoopFlag;
53719 ma_uint32 newReadOffsetInBytes;
53720 ma_uint32 newReadOffsetLoopFlag;
53721
53722 if (pRB == NULL) {
53723 return MA_INVALID_ARGS;
53724 }
53725
53726 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
53727 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
53728
53729 /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
53730 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes);
53731 if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) {
53732 return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
53733 }
53734
53735 /* Move the read pointer back to the start if necessary. */
53736 newReadOffsetLoopFlag = readOffsetLoopFlag;
53737 if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) {
53738 newReadOffsetInBytes = 0;
53739 newReadOffsetLoopFlag ^= 0x80000000;
53740 }
53741
53742 c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes));
53743
53744 if (ma_rb_pointer_distance(pRB) == 0) {
53745 return MA_AT_END;
53746 } else {
53747 return MA_SUCCESS;
53748 }
53749}
53750
53751MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
53752{
53753 ma_uint32 readOffset;
53754 ma_uint32 readOffsetInBytes;
53755 ma_uint32 readOffsetLoopFlag;
53756 ma_uint32 writeOffset;
53757 ma_uint32 writeOffsetInBytes;
53758 ma_uint32 writeOffsetLoopFlag;
53759 size_t bytesAvailable;
53760 size_t bytesRequested;
53761
53762 if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
53763 return MA_INVALID_ARGS;
53764 }
53765
53766 /* The returned buffer should never overtake the read buffer. */
53767 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
53768 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
53769
53770 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
53771 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
53772
53773 /*
53774 In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only
53775 write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should
53776 never overtake the read pointer.
53777 */
53778 if (writeOffsetLoopFlag == readOffsetLoopFlag) {
53779 bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes;
53780 } else {
53781 bytesAvailable = readOffsetInBytes - writeOffsetInBytes;
53782 }
53783
53784 bytesRequested = *pSizeInBytes;
53785 if (bytesRequested > bytesAvailable) {
53786 bytesRequested = bytesAvailable;
53787 }
53788
53789 *pSizeInBytes = bytesRequested;
53790 *ppBufferOut = ma_rb__get_write_ptr(pRB);
53791
53792 /* Clear the buffer if desired. */
53793 if (pRB->clearOnWriteAcquire) {
53794 MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes);
53795 }
53796
53797 return MA_SUCCESS;
53798}
53799
53800MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes)
53801{
53802 ma_uint32 writeOffset;
53803 ma_uint32 writeOffsetInBytes;
53804 ma_uint32 writeOffsetLoopFlag;
53805 ma_uint32 newWriteOffsetInBytes;
53806 ma_uint32 newWriteOffsetLoopFlag;
53807
53808 if (pRB == NULL) {
53809 return MA_INVALID_ARGS;
53810 }
53811
53812 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
53813 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
53814
53815 /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
53816 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes);
53817 if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) {
53818 return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
53819 }
53820
53821 /* Move the read pointer back to the start if necessary. */
53822 newWriteOffsetLoopFlag = writeOffsetLoopFlag;
53823 if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) {
53824 newWriteOffsetInBytes = 0;
53825 newWriteOffsetLoopFlag ^= 0x80000000;
53826 }
53827
53828 c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes));
53829
53830 if (ma_rb_pointer_distance(pRB) == 0) {
53831 return MA_AT_END;
53832 } else {
53833 return MA_SUCCESS;
53834 }
53835}
53836
53837MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes)
53838{
53839 ma_uint32 readOffset;
53840 ma_uint32 readOffsetInBytes;
53841 ma_uint32 readOffsetLoopFlag;
53842 ma_uint32 writeOffset;
53843 ma_uint32 writeOffsetInBytes;
53844 ma_uint32 writeOffsetLoopFlag;
53845 ma_uint32 newReadOffsetInBytes;
53846 ma_uint32 newReadOffsetLoopFlag;
53847
53848 if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) {
53849 return MA_INVALID_ARGS;
53850 }
53851
53852 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
53853 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
53854
53855 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
53856 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
53857
53858 newReadOffsetLoopFlag = readOffsetLoopFlag;
53859
53860 /* We cannot go past the write buffer. */
53861 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
53862 if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) {
53863 newReadOffsetInBytes = writeOffsetInBytes;
53864 } else {
53865 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
53866 }
53867 } else {
53868 /* May end up looping. */
53869 if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
53870 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
53871 newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
53872 } else {
53873 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
53874 }
53875 }
53876
53877 c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag));
53878 return MA_SUCCESS;
53879}
53880
53881MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes)
53882{
53883 ma_uint32 readOffset;
53884 ma_uint32 readOffsetInBytes;
53885 ma_uint32 readOffsetLoopFlag;
53886 ma_uint32 writeOffset;
53887 ma_uint32 writeOffsetInBytes;
53888 ma_uint32 writeOffsetLoopFlag;
53889 ma_uint32 newWriteOffsetInBytes;
53890 ma_uint32 newWriteOffsetLoopFlag;
53891
53892 if (pRB == NULL) {
53893 return MA_INVALID_ARGS;
53894 }
53895
53896 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
53897 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
53898
53899 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
53900 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
53901
53902 newWriteOffsetLoopFlag = writeOffsetLoopFlag;
53903
53904 /* We cannot go past the write buffer. */
53905 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
53906 /* May end up looping. */
53907 if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
53908 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
53909 newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
53910 } else {
53911 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
53912 }
53913 } else {
53914 if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) {
53915 newWriteOffsetInBytes = readOffsetInBytes;
53916 } else {
53917 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
53918 }
53919 }
53920
53921 c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag));
53922 return MA_SUCCESS;
53923}
53924
53926{
53927 ma_uint32 readOffset;
53928 ma_uint32 readOffsetInBytes;
53929 ma_uint32 readOffsetLoopFlag;
53930 ma_uint32 writeOffset;
53931 ma_uint32 writeOffsetInBytes;
53932 ma_uint32 writeOffsetLoopFlag;
53933
53934 if (pRB == NULL) {
53935 return 0;
53936 }
53937
53938 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
53939 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
53940
53941 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
53942 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
53943
53944 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
53945 return writeOffsetInBytes - readOffsetInBytes;
53946 } else {
53947 return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes);
53948 }
53949}
53950
53952{
53953 ma_int32 dist;
53954
53955 if (pRB == NULL) {
53956 return 0;
53957 }
53958
53959 dist = ma_rb_pointer_distance(pRB);
53960 if (dist < 0) {
53961 return 0;
53962 }
53963
53964 return dist;
53965}
53966
53968{
53969 if (pRB == NULL) {
53970 return 0;
53971 }
53972
53974}
53975
53977{
53978 if (pRB == NULL) {
53979 return 0;
53980 }
53981
53982 return pRB->subbufferSizeInBytes;
53983}
53984
53986{
53987 if (pRB == NULL) {
53988 return 0;
53989 }
53990
53991 if (pRB->subbufferStrideInBytes == 0) {
53992 return (size_t)pRB->subbufferSizeInBytes;
53993 }
53994
53995 return (size_t)pRB->subbufferStrideInBytes;
53996}
53997
53998MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex)
53999{
54000 if (pRB == NULL) {
54001 return 0;
54002 }
54003
54004 return subbufferIndex * ma_rb_get_subbuffer_stride(pRB);
54005}
54006
54007MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer)
54008{
54009 if (pRB == NULL) {
54010 return NULL;
54011 }
54012
54013 return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex));
54014}
54015
54016
54017
54018static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB)
54019{
54020 MA_ASSERT(pRB != NULL);
54021
54022 return ma_get_bytes_per_frame(pRB->format, pRB->channels);
54023}
54024
54025MA_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)
54026{
54027 ma_uint32 bpf;
54028 ma_result result;
54029
54030 if (pRB == NULL) {
54031 return MA_INVALID_ARGS;
54032 }
54033
54034 MA_ZERO_OBJECT(pRB);
54035
54036 bpf = ma_get_bytes_per_frame(format, channels);
54037 if (bpf == 0) {
54038 return MA_INVALID_ARGS;
54039 }
54040
54041 result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb);
54042 if (result != MA_SUCCESS) {
54043 return result;
54044 }
54045
54046 pRB->format = format;
54047 pRB->channels = channels;
54048
54049 return MA_SUCCESS;
54050}
54051
54052MA_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)
54053{
54054 return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
54055}
54056
54058{
54059 if (pRB == NULL) {
54060 return;
54061 }
54062
54063 ma_rb_uninit(&pRB->rb);
54064}
54065
54067{
54068 if (pRB == NULL) {
54069 return;
54070 }
54071
54072 ma_rb_reset(&pRB->rb);
54073}
54074
54075MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
54076{
54077 size_t sizeInBytes;
54078 ma_result result;
54079
54080 if (pRB == NULL || pSizeInFrames == NULL) {
54081 return MA_INVALID_ARGS;
54082 }
54083
54084 sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
54085
54086 result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut);
54087 if (result != MA_SUCCESS) {
54088 return result;
54089 }
54090
54091 *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB));
54092 return MA_SUCCESS;
54093}
54094
54096{
54097 if (pRB == NULL) {
54098 return MA_INVALID_ARGS;
54099 }
54100
54101 return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB));
54102}
54103
54104MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
54105{
54106 size_t sizeInBytes;
54107 ma_result result;
54108
54109 if (pRB == NULL) {
54110 return MA_INVALID_ARGS;
54111 }
54112
54113 sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
54114
54115 result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut);
54116 if (result != MA_SUCCESS) {
54117 return result;
54118 }
54119
54120 *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB));
54121 return MA_SUCCESS;
54122}
54123
54125{
54126 if (pRB == NULL) {
54127 return MA_INVALID_ARGS;
54128 }
54129
54130 return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB));
54131}
54132
54134{
54135 if (pRB == NULL) {
54136 return MA_INVALID_ARGS;
54137 }
54138
54139 return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
54140}
54141
54143{
54144 if (pRB == NULL) {
54145 return MA_INVALID_ARGS;
54146 }
54147
54148 return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
54149}
54150
54152{
54153 if (pRB == NULL) {
54154 return 0;
54155 }
54156
54157 return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
54158}
54159
54161{
54162 if (pRB == NULL) {
54163 return 0;
54164 }
54165
54166 return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
54167}
54168
54170{
54171 if (pRB == NULL) {
54172 return 0;
54173 }
54174
54175 return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
54176}
54177
54179{
54180 if (pRB == NULL) {
54181 return 0;
54182 }
54183
54184 return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
54185}
54186
54188{
54189 if (pRB == NULL) {
54190 return 0;
54191 }
54192
54193 return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
54194}
54195
54197{
54198 if (pRB == NULL) {
54199 return 0;
54200 }
54201
54202 return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB));
54203}
54204
54205MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer)
54206{
54207 if (pRB == NULL) {
54208 return NULL;
54209 }
54210
54211 return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer);
54212}
54213
54214
54215
54216MA_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)
54217{
54218 ma_result result;
54219 ma_uint32 sizeInFrames;
54220
54221 sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5);
54222 if (sizeInFrames == 0) {
54223 return MA_INVALID_ARGS;
54224 }
54225
54226 result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb);
54227 if (result != MA_SUCCESS) {
54228 return result;
54229 }
54230
54231 /* Seek forward a bit so we have a bit of a buffer in case of desyncs. */
54232 ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2);
54233
54234 return MA_SUCCESS;
54235}
54236
54238{
54240 return MA_SUCCESS;
54241}
54242
54243
54244
54245
54250MA_API const char* ma_result_description(ma_result result)
54251{
54252 switch (result)
54253 {
54254 case MA_SUCCESS: return "No error";
54255 case MA_ERROR: return "Unknown error";
54256 case MA_INVALID_ARGS: return "Invalid argument";
54257 case MA_INVALID_OPERATION: return "Invalid operation";
54258 case MA_OUT_OF_MEMORY: return "Out of memory";
54259 case MA_OUT_OF_RANGE: return "Out of range";
54260 case MA_ACCESS_DENIED: return "Permission denied";
54261 case MA_DOES_NOT_EXIST: return "Resource does not exist";
54262 case MA_ALREADY_EXISTS: return "Resource already exists";
54263 case MA_TOO_MANY_OPEN_FILES: return "Too many open files";
54264 case MA_INVALID_FILE: return "Invalid file";
54265 case MA_TOO_BIG: return "Too large";
54266 case MA_PATH_TOO_LONG: return "Path too long";
54267 case MA_NAME_TOO_LONG: return "Name too long";
54268 case MA_NOT_DIRECTORY: return "Not a directory";
54269 case MA_IS_DIRECTORY: return "Is a directory";
54270 case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty";
54271 case MA_AT_END: return "At end";
54272 case MA_NO_SPACE: return "No space available";
54273 case MA_BUSY: return "Device or resource busy";
54274 case MA_IO_ERROR: return "Input/output error";
54275 case MA_INTERRUPT: return "Interrupted";
54276 case MA_UNAVAILABLE: return "Resource unavailable";
54277 case MA_ALREADY_IN_USE: return "Resource already in use";
54278 case MA_BAD_ADDRESS: return "Bad address";
54279 case MA_BAD_SEEK: return "Illegal seek";
54280 case MA_BAD_PIPE: return "Broken pipe";
54281 case MA_DEADLOCK: return "Deadlock";
54282 case MA_TOO_MANY_LINKS: return "Too many links";
54283 case MA_NOT_IMPLEMENTED: return "Not implemented";
54284 case MA_NO_MESSAGE: return "No message of desired type";
54285 case MA_BAD_MESSAGE: return "Invalid message";
54286 case MA_NO_DATA_AVAILABLE: return "No data available";
54287 case MA_INVALID_DATA: return "Invalid data";
54288 case MA_TIMEOUT: return "Timeout";
54289 case MA_NO_NETWORK: return "Network unavailable";
54290 case MA_NOT_UNIQUE: return "Not unique";
54291 case MA_NOT_SOCKET: return "Socket operation on non-socket";
54292 case MA_NO_ADDRESS: return "Destination address required";
54293 case MA_BAD_PROTOCOL: return "Protocol wrong type for socket";
54294 case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available";
54295 case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported";
54296 case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported";
54297 case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported";
54298 case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported";
54299 case MA_CONNECTION_RESET: return "Connection reset";
54300 case MA_ALREADY_CONNECTED: return "Already connected";
54301 case MA_NOT_CONNECTED: return "Not connected";
54302 case MA_CONNECTION_REFUSED: return "Connection refused";
54303 case MA_NO_HOST: return "No host";
54304 case MA_IN_PROGRESS: return "Operation in progress";
54305 case MA_CANCELLED: return "Operation cancelled";
54306 case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped";
54307
54308 case MA_FORMAT_NOT_SUPPORTED: return "Format not supported";
54309 case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported";
54310 case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported";
54311 case MA_NO_BACKEND: return "No backend";
54312 case MA_NO_DEVICE: return "No device";
54313 case MA_API_NOT_FOUND: return "API not found";
54314 case MA_INVALID_DEVICE_CONFIG: return "Invalid device config";
54315
54316 case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized";
54317 case MA_DEVICE_NOT_STARTED: return "Device not started";
54318
54319 case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend";
54320 case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device";
54321 case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device";
54322 case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device";
54323
54324 default: return "Unknown error";
54325 }
54326}
54327
54328MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
54329{
54330 if (pAllocationCallbacks != NULL) {
54331 if (pAllocationCallbacks->onMalloc != NULL) {
54332 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
54333 } else {
54334 return NULL; /* Do not fall back to the default implementation. */
54335 }
54336 } else {
54337 return ma__malloc_default(sz, NULL);
54338 }
54339}
54340
54341MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
54342{
54343 void* p = ma_malloc(sz, pAllocationCallbacks);
54344 if (p != NULL) {
54345 MA_ZERO_MEMORY(p, sz);
54346 }
54347
54348 return p;
54349}
54350
54351MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
54352{
54353 if (pAllocationCallbacks != NULL) {
54354 if (pAllocationCallbacks->onRealloc != NULL) {
54355 return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData);
54356 } else {
54357 return NULL; /* Do not fall back to the default implementation. */
54358 }
54359 } else {
54360 return ma__realloc_default(p, sz, NULL);
54361 }
54362}
54363
54364MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
54365{
54366 if (p == NULL) {
54367 return;
54368 }
54369
54370 if (pAllocationCallbacks != NULL) {
54371 if (pAllocationCallbacks->onFree != NULL) {
54372 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
54373 } else {
54374 return; /* Do no fall back to the default implementation. */
54375 }
54376 } else {
54377 ma__free_default(p, NULL);
54378 }
54379}
54380
54381MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks)
54382{
54383 size_t extraBytes;
54384 void* pUnaligned;
54385 void* pAligned;
54386
54387 if (alignment == 0) {
54388 return 0;
54389 }
54390
54391 extraBytes = alignment-1 + sizeof(void*);
54392
54393 pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks);
54394 if (pUnaligned == NULL) {
54395 return NULL;
54396 }
54397
54398 pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1)));
54399 ((void**)pAligned)[-1] = pUnaligned;
54400
54401 return pAligned;
54402}
54403
54404MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
54405{
54406 ma_free(((void**)p)[-1], pAllocationCallbacks);
54407}
54408
54409MA_API const char* ma_get_format_name(ma_format format)
54410{
54411 switch (format)
54412 {
54413 case ma_format_unknown: return "Unknown";
54414 case ma_format_u8: return "8-bit Unsigned Integer";
54415 case ma_format_s16: return "16-bit Signed Integer";
54416 case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)";
54417 case ma_format_s32: return "32-bit Signed Integer";
54418 case ma_format_f32: return "32-bit IEEE Floating Point";
54419 default: return "Invalid";
54420 }
54421}
54422
54423MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels)
54424{
54425 ma_uint32 i;
54426 for (i = 0; i < channels; ++i) {
54427 pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor);
54428 }
54429}
54430
54431
54433{
54434 ma_uint32 sizes[] = {
54435 0, /* unknown */
54436 1, /* u8 */
54437 2, /* s16 */
54438 3, /* s24 */
54439 4, /* s32 */
54440 4, /* f32 */
54441 };
54442 return sizes[format];
54443}
54444
54445
54446
54448{
54449 ma_data_source_config config;
54450
54451 MA_ZERO_OBJECT(&config);
54452
54453 return config;
54454}
54455
54456
54458{
54459 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
54460
54461 if (pDataSource == NULL) {
54462 return MA_INVALID_ARGS;
54463 }
54464
54465 MA_ZERO_OBJECT(pDataSourceBase);
54466
54467 if (pConfig == NULL) {
54468 return MA_INVALID_ARGS;
54469 }
54470
54471 pDataSourceBase->vtable = pConfig->vtable;
54472 pDataSourceBase->rangeBegInFrames = 0;
54473 pDataSourceBase->rangeEndInFrames = ~((ma_uint64)0);
54474 pDataSourceBase->loopBegInFrames = 0;
54475 pDataSourceBase->loopEndInFrames = ~((ma_uint64)0);
54476 pDataSourceBase->pCurrent = pDataSource; /* Always read from ourself by default. */
54477 pDataSourceBase->pNext = NULL;
54478 pDataSourceBase->onGetNext = NULL;
54479
54480 return MA_SUCCESS;
54481}
54482
54484{
54485 if (pDataSource == NULL) {
54486 return;
54487 }
54488
54489 /*
54490 This is placeholder in case we need this later. Data sources need to call this in their
54491 uninitialization routine to ensure things work later on if something is added here.
54492 */
54493}
54494
54495static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource)
54496{
54497 ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource;
54498
54499 MA_ASSERT(pDataSource != NULL);
54500 MA_ASSERT(ppCurrentDataSource != NULL);
54501
54502 if (pCurrentDataSource->pCurrent == NULL) {
54503 /*
54504 The current data source is NULL. If we're using this in the context of a chain we need to return NULL
54505 here so that we don't end up looping. Otherwise we just return the data source itself.
54506 */
54507 if (pCurrentDataSource->pNext != NULL || pCurrentDataSource->onGetNext != NULL) {
54508 pCurrentDataSource = NULL;
54509 } else {
54510 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. */
54511 }
54512 } else {
54513 pCurrentDataSource = (ma_data_source_base*)pCurrentDataSource->pCurrent;
54514 }
54515
54516 *ppCurrentDataSource = pCurrentDataSource;
54517
54518 return MA_SUCCESS;
54519}
54520
54521static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
54522{
54523 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
54524 ma_result result;
54525 ma_uint64 framesRead = 0;
54526 ma_bool32 loop = ma_data_source_is_looping(pDataSource);
54527
54528 if (pDataSourceBase == NULL) {
54529 return MA_AT_END;
54530 }
54531
54532 if (frameCount == 0) {
54533 return MA_INVALID_ARGS;
54534 }
54535
54536 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))) {
54537 /* 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. */
54538 result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);
54539 } else {
54540 /* Need to clamp to within the range. */
54541 ma_uint64 cursor;
54542
54543 result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &cursor);
54544 if (result != MA_SUCCESS) {
54545 /* 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. */
54546 result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);
54547 } else {
54548 ma_uint64 rangeEnd;
54549
54550 /* We have the cursor. We need to make sure we don't read beyond our range. */
54551 rangeEnd = pDataSourceBase->rangeEndInFrames;
54552
54553 /* If looping, make sure we're within range. */
54554 if (loop) {
54555 if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) {
54556 rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames);
54557 }
54558 }
54559
54560 if (frameCount > (rangeEnd - cursor) && rangeEnd != ~((ma_uint64)0)) {
54561 frameCount = (rangeEnd - cursor);
54562 }
54563
54564 /*
54565 If the cursor is sitting on the end of the range the frame count will be set to 0 which can
54566 result in MA_INVALID_ARGS. In this case, we don't want to try reading, but instead return
54567 MA_AT_END so the higher level function can know about it.
54568 */
54569 if (frameCount > 0) {
54570 result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);
54571 } else {
54572 result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */
54573 }
54574 }
54575 }
54576
54577 if (pFramesRead != NULL) {
54578 *pFramesRead = framesRead;
54579 }
54580
54581 /* We need to make sure MA_AT_END is returned if we hit the end of the range. */
54582 if (result == MA_SUCCESS && framesRead == 0) {
54583 result = MA_AT_END;
54584 }
54585
54586 return result;
54587}
54588
54589MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
54590{
54591 ma_result result = MA_SUCCESS;
54592 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
54593 ma_data_source_base* pCurrentDataSource;
54594 void* pRunningFramesOut = pFramesOut;
54595 ma_uint64 totalFramesProcessed = 0;
54596 ma_format format;
54597 ma_uint32 channels;
54598 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. */
54599 ma_bool32 loop;
54600
54601 if (pFramesRead != NULL) {
54602 *pFramesRead = 0;
54603 }
54604
54605 if (frameCount == 0) {
54606 return MA_INVALID_ARGS;
54607 }
54608
54609 if (pDataSourceBase == NULL) {
54610 return MA_INVALID_ARGS;
54611 }
54612
54613 loop = ma_data_source_is_looping(pDataSource);
54614
54615 /*
54616 We need to know the data format so we can advance the output buffer as we read frames. If this
54617 fails, chaining will not work and we'll just read as much as we can from the current source.
54618 */
54619 if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0) != MA_SUCCESS) {
54620 result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource);
54621 if (result != MA_SUCCESS) {
54622 return result;
54623 }
54624
54625 return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead);
54626 }
54627
54628 /*
54629 Looping is a bit of a special case. When the `loop` argument is true, chaining will not work and
54630 only the current data source will be read from.
54631 */
54632
54633 /* Keep reading until we've read as many frames as possible. */
54634 while (totalFramesProcessed < frameCount) {
54635 ma_uint64 framesProcessed;
54636 ma_uint64 framesRemaining = frameCount - totalFramesProcessed;
54637
54638 /* We need to resolve the data source that we'll actually be reading from. */
54639 result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource);
54640 if (result != MA_SUCCESS) {
54641 break;
54642 }
54643
54644 if (pCurrentDataSource == NULL) {
54645 break;
54646 }
54647
54648 result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed);
54649 totalFramesProcessed += framesProcessed;
54650
54651 /*
54652 If we encounted an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is
54653 not necessarily considered an error.
54654 */
54655 if (result != MA_SUCCESS && result != MA_AT_END) {
54656 break;
54657 }
54658
54659 /*
54660 We can determine if we've reached the end by checking if ma_data_source_read_pcm_frames_within_range() returned
54661 MA_AT_END. To loop back to the start, all we need to do is seek back to the first frame.
54662 */
54663 if (result == MA_AT_END) {
54664 /*
54665 The result needs to be reset back to MA_SUCCESS (from MA_AT_END) so that we don't
54666 accidentally return MA_AT_END when data has been read in prior loop iterations. at the
54667 end of this function, the result will be checked for MA_SUCCESS, and if the total
54668 number of frames processed is 0, will be explicitly set to MA_AT_END.
54669 */
54670 result = MA_SUCCESS;
54671
54672 /*
54673 We reached the end. If we're looping, we just loop back to the start of the current
54674 data source. If we're not looping we need to check if we have another in the chain, and
54675 if so, switch to it.
54676 */
54677 if (loop) {
54678 if (framesProcessed == 0) {
54679 emptyLoopCounter += 1;
54680 if (emptyLoopCounter > 1) {
54681 break; /* Infinite loop detected. Get out. */
54682 }
54683 } else {
54684 emptyLoopCounter = 0;
54685 }
54686
54687 result = ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames);
54688 if (result != MA_SUCCESS) {
54689 break; /* Failed to loop. Abort. */
54690 }
54691
54692 /* Don't return MA_AT_END for looping sounds. */
54693 result = MA_SUCCESS;
54694 } else {
54695 if (pCurrentDataSource->pNext != NULL) {
54696 pDataSourceBase->pCurrent = pCurrentDataSource->pNext;
54697 } else if (pCurrentDataSource->onGetNext != NULL) {
54698 pDataSourceBase->pCurrent = pCurrentDataSource->onGetNext(pCurrentDataSource);
54699 if (pDataSourceBase->pCurrent == NULL) {
54700 break; /* Our callback did not return a next data source. We're done. */
54701 }
54702 } else {
54703 /* Reached the end of the chain. We're done. */
54704 break;
54705 }
54706
54707 /* The next data source needs to be rewound to ensure data is read in looping scenarios. */
54708 result = ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0);
54709 if (result != MA_SUCCESS) {
54710 break;
54711 }
54712 }
54713 }
54714
54715 if (pRunningFramesOut != NULL) {
54716 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels));
54717 }
54718 }
54719
54720 if (pFramesRead != NULL) {
54721 *pFramesRead = totalFramesProcessed;
54722 }
54723
54724 MA_ASSERT(!(result == MA_AT_END && totalFramesProcessed > 0)); /* We should never be returning MA_AT_END if we read some data. */
54725
54726 if (result == MA_SUCCESS && totalFramesProcessed == 0) {
54727 result = MA_AT_END;
54728 }
54729
54730 return result;
54731}
54732
54733MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked)
54734{
54735 return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked);
54736}
54737
54739{
54740 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
54741
54742 if (pDataSourceBase == NULL) {
54743 return MA_SUCCESS;
54744 }
54745
54746 if (pDataSourceBase->vtable->onSeek == NULL) {
54747 return MA_NOT_IMPLEMENTED;
54748 }
54749
54750 if (frameIndex > pDataSourceBase->rangeEndInFrames) {
54751 return MA_INVALID_OPERATION; /* Trying to seek to far forward. */
54752 }
54753
54754 return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex);
54755}
54756
54757MA_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)
54758{
54759 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
54760 ma_result result;
54761 ma_format format;
54762 ma_uint32 channels;
54763 ma_uint32 sampleRate;
54764
54765 /* Initialize to defaults for safety just in case the data source does not implement this callback. */
54766 if (pFormat != NULL) {
54767 *pFormat = ma_format_unknown;
54768 }
54769 if (pChannels != NULL) {
54770 *pChannels = 0;
54771 }
54772 if (pSampleRate != NULL) {
54773 *pSampleRate = 0;
54774 }
54775 if (pChannelMap != NULL) {
54776 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
54777 }
54778
54779 if (pDataSourceBase == NULL) {
54780 return MA_INVALID_ARGS;
54781 }
54782
54783 if (pDataSourceBase->vtable->onGetDataFormat == NULL) {
54784 return MA_NOT_IMPLEMENTED;
54785 }
54786
54787 result = pDataSourceBase->vtable->onGetDataFormat(pDataSource, &format, &channels, &sampleRate, pChannelMap, channelMapCap);
54788 if (result != MA_SUCCESS) {
54789 return result;
54790 }
54791
54792 if (pFormat != NULL) {
54793 *pFormat = format;
54794 }
54795 if (pChannels != NULL) {
54796 *pChannels = channels;
54797 }
54798 if (pSampleRate != NULL) {
54799 *pSampleRate = sampleRate;
54800 }
54801
54802 /* Channel map was passed in directly to the callback. This is safe due to the channelMapCap parameter. */
54803
54804 return MA_SUCCESS;
54805}
54806
54808{
54809 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
54810 ma_result result;
54811 ma_uint64 cursor;
54812
54813 if (pCursor == NULL) {
54814 return MA_INVALID_ARGS;
54815 }
54816
54817 *pCursor = 0;
54818
54819 if (pDataSourceBase == NULL) {
54820 return MA_SUCCESS;
54821 }
54822
54823 if (pDataSourceBase->vtable->onGetCursor == NULL) {
54824 return MA_NOT_IMPLEMENTED;
54825 }
54826
54827 result = pDataSourceBase->vtable->onGetCursor(pDataSourceBase, &cursor);
54828 if (result != MA_SUCCESS) {
54829 return result;
54830 }
54831
54832 /* The cursor needs to be made relative to the start of the range. */
54833 if (cursor < pDataSourceBase->rangeBegInFrames) { /* Safety check so we don't return some huge number. */
54834 *pCursor = 0;
54835 } else {
54836 *pCursor = cursor - pDataSourceBase->rangeBegInFrames;
54837 }
54838
54839 return MA_SUCCESS;
54840}
54841
54843{
54844 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
54845
54846 if (pLength == NULL) {
54847 return MA_INVALID_ARGS;
54848 }
54849
54850 *pLength = 0;
54851
54852 if (pDataSourceBase == NULL) {
54853 return MA_INVALID_ARGS;
54854 }
54855
54856 /*
54857 If we have a range defined we'll use that to determine the length. This is one of rare times
54858 where we'll actually trust the caller. If they've set the range, I think it's mostly safe to
54859 assume they've set it based on some higher level knowledge of the structure of the sound bank.
54860 */
54861 if (pDataSourceBase->rangeEndInFrames != ~((ma_uint64)0)) {
54862 *pLength = pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames;
54863 return MA_SUCCESS;
54864 }
54865
54866 /*
54867 Getting here means a range is not defined so we'll need to get the data source itself to tell
54868 us the length.
54869 */
54870 if (pDataSourceBase->vtable->onGetLength == NULL) {
54871 return MA_NOT_IMPLEMENTED;
54872 }
54873
54874 return pDataSourceBase->vtable->onGetLength(pDataSource, pLength);
54875}
54876
54878{
54879 ma_result result;
54880 ma_uint64 cursorInPCMFrames;
54881 ma_uint32 sampleRate;
54882
54883 if (pCursor == NULL) {
54884 return MA_INVALID_ARGS;
54885 }
54886
54887 *pCursor = 0;
54888
54889 result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursorInPCMFrames);
54890 if (result != MA_SUCCESS) {
54891 return result;
54892 }
54893
54894 result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
54895 if (result != MA_SUCCESS) {
54896 return result;
54897 }
54898
54899 *pCursor = cursorInPCMFrames / (float)sampleRate;
54900
54901 return MA_SUCCESS;
54902}
54903
54905{
54906 ma_result result;
54907 ma_uint64 lengthInPCMFrames;
54908 ma_uint32 sampleRate;
54909
54910 if (pLength == NULL) {
54911 return MA_INVALID_ARGS;
54912 }
54913
54914 *pLength = 0;
54915
54916 result = ma_data_source_get_length_in_pcm_frames(pDataSource, &lengthInPCMFrames);
54917 if (result != MA_SUCCESS) {
54918 return result;
54919 }
54920
54921 result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
54922 if (result != MA_SUCCESS) {
54923 return result;
54924 }
54925
54926 *pLength = lengthInPCMFrames / (float)sampleRate;
54927
54928 return MA_SUCCESS;
54929}
54930
54932{
54933 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
54934
54935 if (pDataSource == NULL) {
54936 return MA_INVALID_ARGS;
54937 }
54938
54939 c89atomic_exchange_32(&pDataSourceBase->isLooping, isLooping);
54940
54941 /* If there's no callback for this just treat it as a successful no-op. */
54942 if (pDataSourceBase->vtable->onSetLooping == NULL) {
54943 return MA_SUCCESS;
54944 }
54945
54946 return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping);
54947}
54948
54950{
54951 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
54952
54953 if (pDataSource == NULL) {
54954 return MA_FALSE;
54955 }
54956
54957 return c89atomic_load_32(&pDataSourceBase->isLooping);
54958}
54959
54960MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames)
54961{
54962 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
54963 ma_result result;
54964 ma_uint64 cursor;
54965 ma_uint64 loopBegAbsolute;
54966 ma_uint64 loopEndAbsolute;
54967
54968 if (pDataSource == NULL) {
54969 return MA_INVALID_ARGS;
54970 }
54971
54972 if (rangeEndInFrames < rangeBegInFrames) {
54973 return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */
54974 }
54975
54976 /*
54977 The loop points need to be updated. We'll be storing the loop points relative to the range. We'll update
54978 these so that they maintain their absolute positioning. The loop points will then be clamped to the range.
54979 */
54980 loopBegAbsolute = pDataSourceBase->loopBegInFrames + pDataSourceBase->rangeBegInFrames;
54981 loopEndAbsolute = pDataSourceBase->loopEndInFrames + ((pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) ? pDataSourceBase->rangeBegInFrames : 0);
54982
54983 pDataSourceBase->rangeBegInFrames = rangeBegInFrames;
54984 pDataSourceBase->rangeEndInFrames = rangeEndInFrames;
54985
54986 /* Make the loop points relative again, and make sure they're clamped to within the range. */
54987 if (loopBegAbsolute > pDataSourceBase->rangeBegInFrames) {
54988 pDataSourceBase->loopBegInFrames = loopBegAbsolute - pDataSourceBase->rangeBegInFrames;
54989 } else {
54990 pDataSourceBase->loopBegInFrames = 0;
54991 }
54992
54993 if (pDataSourceBase->loopBegInFrames > pDataSourceBase->rangeEndInFrames) {
54994 pDataSourceBase->loopBegInFrames = pDataSourceBase->rangeEndInFrames;
54995 }
54996
54997 /* Only need to update the loop end point if it's not -1. */
54998 if (loopEndAbsolute != ~((ma_uint64)0)) {
54999 if (loopEndAbsolute > pDataSourceBase->rangeBegInFrames) {
55000 pDataSourceBase->loopEndInFrames = loopEndAbsolute - pDataSourceBase->rangeBegInFrames;
55001 } else {
55002 pDataSourceBase->loopEndInFrames = 0;
55003 }
55004
55005 if (pDataSourceBase->loopEndInFrames > pDataSourceBase->rangeEndInFrames && pDataSourceBase->loopEndInFrames) {
55006 pDataSourceBase->loopEndInFrames = pDataSourceBase->rangeEndInFrames;
55007 }
55008 }
55009
55010
55011 /* If the new range is past the current cursor position we need to seek to it. */
55012 result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor);
55013 if (result == MA_SUCCESS) {
55014 /* Seek to within range. Note that our seek positions here are relative to the new range. */
55015 if (cursor < rangeBegInFrames) {
55016 ma_data_source_seek_to_pcm_frame(pDataSource, 0);
55017 } else if (cursor > rangeEndInFrames) {
55018 ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames);
55019 }
55020 } else {
55021 /* We failed to get the cursor position. Probably means the data source has no notion of a cursor such a noise data source. Just pretend the seeking worked. */
55022 }
55023
55024 return MA_SUCCESS;
55025}
55026
55027MA_API void ma_data_source_get_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames)
55028{
55029 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
55030
55031 if (pDataSource == NULL) {
55032 return;
55033 }
55034
55035 if (pRangeBegInFrames != NULL) {
55036 *pRangeBegInFrames = pDataSourceBase->rangeBegInFrames;
55037 }
55038
55039 if (pRangeEndInFrames != NULL) {
55040 *pRangeEndInFrames = pDataSourceBase->rangeEndInFrames;
55041 }
55042}
55043
55045{
55046 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
55047
55048 if (pDataSource == NULL) {
55049 return MA_INVALID_ARGS;
55050 }
55051
55052 if (loopEndInFrames < loopBegInFrames) {
55053 return MA_INVALID_ARGS; /* The end of the loop point must come after the beginning. */
55054 }
55055
55056 if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) {
55057 return MA_INVALID_ARGS; /* The end of the loop point must not go beyond the range. */
55058 }
55059
55060 pDataSourceBase->loopBegInFrames = loopBegInFrames;
55061 pDataSourceBase->loopEndInFrames = loopEndInFrames;
55062
55063 /* The end cannot exceed the range. */
55064 if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) {
55065 pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames);
55066 }
55067
55068 return MA_SUCCESS;
55069}
55070
55071MA_API void ma_data_source_get_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames)
55072{
55073 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
55074
55075 if (pDataSource == NULL) {
55076 return;
55077 }
55078
55079 if (pLoopBegInFrames != NULL) {
55080 *pLoopBegInFrames = pDataSourceBase->loopBegInFrames;
55081 }
55082
55083 if (pLoopEndInFrames != NULL) {
55084 *pLoopEndInFrames = pDataSourceBase->loopEndInFrames;
55085 }
55086}
55087
55089{
55090 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
55091
55092 if (pDataSource == NULL) {
55093 return MA_INVALID_ARGS;
55094 }
55095
55096 pDataSourceBase->pCurrent = pCurrentDataSource;
55097
55098 return MA_SUCCESS;
55099}
55100
55102{
55103 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
55104
55105 if (pDataSource == NULL) {
55106 return NULL;
55107 }
55108
55109 return pDataSourceBase->pCurrent;
55110}
55111
55113{
55114 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
55115
55116 if (pDataSource == NULL) {
55117 return MA_INVALID_ARGS;
55118 }
55119
55120 pDataSourceBase->pNext = pNextDataSource;
55121
55122 return MA_SUCCESS;
55123}
55124
55126{
55127 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
55128
55129 if (pDataSource == NULL) {
55130 return NULL;
55131 }
55132
55133 return pDataSourceBase->pNext;
55134}
55135
55137{
55138 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
55139
55140 if (pDataSource == NULL) {
55141 return MA_INVALID_ARGS;
55142 }
55143
55144 pDataSourceBase->onGetNext = onGetNext;
55145
55146 return MA_SUCCESS;
55147}
55148
55150{
55151 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
55152
55153 if (pDataSource == NULL) {
55154 return NULL;
55155 }
55156
55157 return pDataSourceBase->onGetNext;
55158}
55159
55160
55161static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
55162{
55163 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
55164 ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames(pAudioBufferRef, pFramesOut, frameCount, MA_FALSE);
55165
55166 if (pFramesRead != NULL) {
55167 *pFramesRead = framesRead;
55168 }
55169
55170 if (framesRead < frameCount || framesRead == 0) {
55171 return MA_AT_END;
55172 }
55173
55174 return MA_SUCCESS;
55175}
55176
55177static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
55178{
55179 return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex);
55180}
55181
55182static 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)
55183{
55184 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
55185
55186 *pFormat = pAudioBufferRef->format;
55187 *pChannels = pAudioBufferRef->channels;
55188 *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */
55189 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels);
55190
55191 return MA_SUCCESS;
55192}
55193
55194static ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
55195{
55196 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
55197
55198 *pCursor = pAudioBufferRef->cursor;
55199
55200 return MA_SUCCESS;
55201}
55202
55203static ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
55204{
55205 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
55206
55207 *pLength = pAudioBufferRef->sizeInFrames;
55208
55209 return MA_SUCCESS;
55210}
55211
55212static ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable =
55213{
55214 ma_audio_buffer_ref__data_source_on_read,
55215 ma_audio_buffer_ref__data_source_on_seek,
55216 ma_audio_buffer_ref__data_source_on_get_data_format,
55217 ma_audio_buffer_ref__data_source_on_get_cursor,
55218 ma_audio_buffer_ref__data_source_on_get_length,
55219 NULL, /* onSetLooping */
55220 0
55221};
55222
55223MA_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)
55224{
55225 ma_result result;
55226 ma_data_source_config dataSourceConfig;
55227
55228 if (pAudioBufferRef == NULL) {
55229 return MA_INVALID_ARGS;
55230 }
55231
55232 MA_ZERO_OBJECT(pAudioBufferRef);
55233
55234 dataSourceConfig = ma_data_source_config_init();
55235 dataSourceConfig.vtable = &g_ma_audio_buffer_ref_data_source_vtable;
55236
55237 result = ma_data_source_init(&dataSourceConfig, &pAudioBufferRef->ds);
55238 if (result != MA_SUCCESS) {
55239 return result;
55240 }
55241
55242 pAudioBufferRef->format = format;
55243 pAudioBufferRef->channels = channels;
55244 pAudioBufferRef->cursor = 0;
55245 pAudioBufferRef->sizeInFrames = sizeInFrames;
55246 pAudioBufferRef->pData = pData;
55247
55248 return MA_SUCCESS;
55249}
55250
55252{
55253 if (pAudioBufferRef == NULL) {
55254 return;
55255 }
55256
55257 ma_data_source_uninit(&pAudioBufferRef->ds);
55258}
55259
55260MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames)
55261{
55262 if (pAudioBufferRef == NULL) {
55263 return MA_INVALID_ARGS;
55264 }
55265
55266 pAudioBufferRef->cursor = 0;
55267 pAudioBufferRef->sizeInFrames = sizeInFrames;
55268 pAudioBufferRef->pData = pData;
55269
55270 return MA_SUCCESS;
55271}
55272
55273MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop)
55274{
55275 ma_uint64 totalFramesRead = 0;
55276
55277 if (pAudioBufferRef == NULL) {
55278 return 0;
55279 }
55280
55281 if (frameCount == 0) {
55282 return 0;
55283 }
55284
55285 while (totalFramesRead < frameCount) {
55286 ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
55287 ma_uint64 framesRemaining = frameCount - totalFramesRead;
55288 ma_uint64 framesToRead;
55289
55290 framesToRead = framesRemaining;
55291 if (framesToRead > framesAvailable) {
55292 framesToRead = framesAvailable;
55293 }
55294
55295 if (pFramesOut != NULL) {
55296 ma_copy_pcm_frames(pFramesOut, ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), framesToRead, pAudioBufferRef->format, pAudioBufferRef->channels);
55297 }
55298
55299 totalFramesRead += framesToRead;
55300
55301 pAudioBufferRef->cursor += framesToRead;
55302 if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) {
55303 if (loop) {
55304 pAudioBufferRef->cursor = 0;
55305 } else {
55306 break; /* We've reached the end and we're not looping. Done. */
55307 }
55308 }
55309
55310 MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames);
55311 }
55312
55313 return totalFramesRead;
55314}
55315
55317{
55318 if (pAudioBufferRef == NULL) {
55319 return MA_INVALID_ARGS;
55320 }
55321
55322 if (frameIndex > pAudioBufferRef->sizeInFrames) {
55323 return MA_INVALID_ARGS;
55324 }
55325
55326 pAudioBufferRef->cursor = (size_t)frameIndex;
55327
55328 return MA_SUCCESS;
55329}
55330
55331MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount)
55332{
55333 ma_uint64 framesAvailable;
55334 ma_uint64 frameCount = 0;
55335
55336 if (ppFramesOut != NULL) {
55337 *ppFramesOut = NULL; /* Safety. */
55338 }
55339
55340 if (pFrameCount != NULL) {
55341 frameCount = *pFrameCount;
55342 *pFrameCount = 0; /* Safety. */
55343 }
55344
55345 if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) {
55346 return MA_INVALID_ARGS;
55347 }
55348
55349 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
55350 if (frameCount > framesAvailable) {
55351 frameCount = framesAvailable;
55352 }
55353
55354 *ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels));
55355 *pFrameCount = frameCount;
55356
55357 return MA_SUCCESS;
55358}
55359
55361{
55362 ma_uint64 framesAvailable;
55363
55364 if (pAudioBufferRef == NULL) {
55365 return MA_INVALID_ARGS;
55366 }
55367
55368 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
55369 if (frameCount > framesAvailable) {
55370 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. */
55371 }
55372
55373 pAudioBufferRef->cursor += frameCount;
55374
55375 if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) {
55376 return MA_AT_END; /* Successful. Need to tell the caller that the end has been reached so that it can loop if desired. */
55377 } else {
55378 return MA_SUCCESS;
55379 }
55380}
55381
55383{
55384 if (pAudioBufferRef == NULL) {
55385 return MA_FALSE;
55386 }
55387
55388 return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames;
55389}
55390
55392{
55393 if (pCursor == NULL) {
55394 return MA_INVALID_ARGS;
55395 }
55396
55397 *pCursor = 0;
55398
55399 if (pAudioBufferRef == NULL) {
55400 return MA_INVALID_ARGS;
55401 }
55402
55403 *pCursor = pAudioBufferRef->cursor;
55404
55405 return MA_SUCCESS;
55406}
55407
55409{
55410 if (pLength == NULL) {
55411 return MA_INVALID_ARGS;
55412 }
55413
55414 *pLength = 0;
55415
55416 if (pAudioBufferRef == NULL) {
55417 return MA_INVALID_ARGS;
55418 }
55419
55420 *pLength = pAudioBufferRef->sizeInFrames;
55421
55422 return MA_SUCCESS;
55423}
55424
55426{
55427 if (pAvailableFrames == NULL) {
55428 return MA_INVALID_ARGS;
55429 }
55430
55431 *pAvailableFrames = 0;
55432
55433 if (pAudioBufferRef == NULL) {
55434 return MA_INVALID_ARGS;
55435 }
55436
55437 if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) {
55438 *pAvailableFrames = 0;
55439 } else {
55440 *pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
55441 }
55442
55443 return MA_SUCCESS;
55444}
55445
55446
55447
55448
55449MA_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)
55450{
55452
55453 MA_ZERO_OBJECT(&config);
55454 config.format = format;
55455 config.channels = channels;
55456 config.sizeInFrames = sizeInFrames;
55457 config.pData = pData;
55458 ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks);
55459
55460 return config;
55461}
55462
55463static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer)
55464{
55465 ma_result result;
55466
55467 if (pAudioBuffer == NULL) {
55468 return MA_INVALID_ARGS;
55469 }
55470
55471 MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData)); /* Safety. Don't overwrite the extra data. */
55472
55473 if (pConfig == NULL) {
55474 return MA_INVALID_ARGS;
55475 }
55476
55477 if (pConfig->sizeInFrames == 0) {
55478 return MA_INVALID_ARGS; /* Not allowing buffer sizes of 0 frames. */
55479 }
55480
55481 result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref);
55482 if (result != MA_SUCCESS) {
55483 return result;
55484 }
55485
55486 ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks);
55487
55488 if (doCopy) {
55489 ma_uint64 allocationSizeInBytes;
55490 void* pData;
55491
55492 allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels);
55493 if (allocationSizeInBytes > MA_SIZE_MAX) {
55494 return MA_OUT_OF_MEMORY; /* Too big. */
55495 }
55496
55497 pData = ma_malloc((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */
55498 if (pData == NULL) {
55499 return MA_OUT_OF_MEMORY;
55500 }
55501
55502 if (pConfig->pData != NULL) {
55503 ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
55504 } else {
55505 ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
55506 }
55507
55508 ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames);
55509 pAudioBuffer->ownsData = MA_TRUE;
55510 } else {
55511 ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames);
55512 pAudioBuffer->ownsData = MA_FALSE;
55513 }
55514
55515 return MA_SUCCESS;
55516}
55517
55518static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree)
55519{
55520 if (pAudioBuffer == NULL) {
55521 return;
55522 }
55523
55524 if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) {
55525 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. */
55526 }
55527
55528 if (doFree) {
55529 ma_free(pAudioBuffer, &pAudioBuffer->allocationCallbacks);
55530 }
55531
55532 ma_audio_buffer_ref_uninit(&pAudioBuffer->ref);
55533}
55534
55536{
55537 return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer);
55538}
55539
55541{
55542 return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer);
55543}
55544
55546{
55547 ma_result result;
55548 ma_audio_buffer* pAudioBuffer;
55549 ma_audio_buffer_config innerConfig; /* We'll be making some changes to the config, so need to make a copy. */
55550 ma_uint64 allocationSizeInBytes;
55551
55552 if (ppAudioBuffer == NULL) {
55553 return MA_INVALID_ARGS;
55554 }
55555
55556 *ppAudioBuffer = NULL; /* Safety. */
55557
55558 if (pConfig == NULL) {
55559 return MA_INVALID_ARGS;
55560 }
55561
55562 innerConfig = *pConfig;
55563 ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks);
55564
55565 allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels));
55566 if (allocationSizeInBytes > MA_SIZE_MAX) {
55567 return MA_OUT_OF_MEMORY; /* Too big. */
55568 }
55569
55570 pAudioBuffer = (ma_audio_buffer*)ma_malloc((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */
55571 if (pAudioBuffer == NULL) {
55572 return MA_OUT_OF_MEMORY;
55573 }
55574
55575 if (pConfig->pData != NULL) {
55576 ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
55577 } else {
55578 ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels);
55579 }
55580
55581 innerConfig.pData = &pAudioBuffer->_pExtraData[0];
55582
55583 result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer);
55584 if (result != MA_SUCCESS) {
55585 ma_free(pAudioBuffer, &innerConfig.allocationCallbacks);
55586 return result;
55587 }
55588
55589 *ppAudioBuffer = pAudioBuffer;
55590
55591 return MA_SUCCESS;
55592}
55593
55595{
55596 ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE);
55597}
55598
55600{
55601 ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE);
55602}
55603
55604MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop)
55605{
55606 if (pAudioBuffer == NULL) {
55607 return 0;
55608 }
55609
55610 return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop);
55611}
55612
55614{
55615 if (pAudioBuffer == NULL) {
55616 return MA_INVALID_ARGS;
55617 }
55618
55619 return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex);
55620}
55621
55622MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount)
55623{
55624 if (ppFramesOut != NULL) {
55625 *ppFramesOut = NULL; /* Safety. */
55626 }
55627
55628 if (pAudioBuffer == NULL) {
55629 if (pFrameCount != NULL) {
55630 *pFrameCount = 0;
55631 }
55632
55633 return MA_INVALID_ARGS;
55634 }
55635
55636 return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount);
55637}
55638
55640{
55641 if (pAudioBuffer == NULL) {
55642 return MA_INVALID_ARGS;
55643 }
55644
55645 return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount);
55646}
55647
55649{
55650 if (pAudioBuffer == NULL) {
55651 return MA_FALSE;
55652 }
55653
55654 return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref);
55655}
55656
55658{
55659 if (pAudioBuffer == NULL) {
55660 return MA_INVALID_ARGS;
55661 }
55662
55663 return ma_audio_buffer_ref_get_cursor_in_pcm_frames(&pAudioBuffer->ref, pCursor);
55664}
55665
55667{
55668 if (pAudioBuffer == NULL) {
55669 return MA_INVALID_ARGS;
55670 }
55671
55672 return ma_audio_buffer_ref_get_length_in_pcm_frames(&pAudioBuffer->ref, pLength);
55673}
55674
55676{
55677 if (pAvailableFrames == NULL) {
55678 return MA_INVALID_ARGS;
55679 }
55680
55681 *pAvailableFrames = 0;
55682
55683 if (pAudioBuffer == NULL) {
55684 return MA_INVALID_ARGS;
55685 }
55686
55687 return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames);
55688}
55689
55690
55691
55692
55693
55695{
55696 if (pData == NULL) {
55697 return MA_INVALID_ARGS;
55698 }
55699
55700 MA_ZERO_OBJECT(pData);
55701
55702 pData->format = format;
55703 pData->channels = channels;
55704 pData->pTail = &pData->head;
55705
55706 return MA_SUCCESS;
55707}
55708
55710{
55712
55713 if (pData == NULL) {
55714 return;
55715 }
55716
55717 /* All pages need to be freed. */
55718 pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext);
55719 while (pPage != NULL) {
55720 ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext);
55721
55722 ma_free(pPage, pAllocationCallbacks);
55723 pPage = pNext;
55724 }
55725}
55726
55728{
55729 if (pData == NULL) {
55730 return NULL;
55731 }
55732
55733 return &pData->head;
55734}
55735
55737{
55738 if (pData == NULL) {
55739 return NULL;
55740 }
55741
55742 return pData->pTail;
55743}
55744
55746{
55748
55749 if (pLength == NULL) {
55750 return MA_INVALID_ARGS;
55751 }
55752
55753 *pLength = 0;
55754
55755 if (pData == NULL) {
55756 return MA_INVALID_ARGS;
55757 }
55758
55759 /* Calculate the length from the linked list. */
55760 for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) {
55761 *pLength += pPage->sizeInFrames;
55762 }
55763
55764 return MA_SUCCESS;
55765}
55766
55767MA_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)
55768{
55770 ma_uint64 allocationSize;
55771
55772 if (ppPage == NULL) {
55773 return MA_INVALID_ARGS;
55774 }
55775
55776 *ppPage = NULL;
55777
55778 if (pData == NULL) {
55779 return MA_INVALID_ARGS;
55780 }
55781
55782 allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels));
55783 if (allocationSize > MA_SIZE_MAX) {
55784 return MA_OUT_OF_MEMORY; /* Too big. */
55785 }
55786
55787 pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */
55788 if (pPage == NULL) {
55789 return MA_OUT_OF_MEMORY;
55790 }
55791
55792 pPage->pNext = NULL;
55793 pPage->sizeInFrames = pageSizeInFrames;
55794
55795 if (pInitialData != NULL) {
55796 ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels);
55797 }
55798
55799 *ppPage = pPage;
55800
55801 return MA_SUCCESS;
55802}
55803
55805{
55806 if (pData == NULL || pPage == NULL) {
55807 return MA_INVALID_ARGS;
55808 }
55809
55810 /* It's assumed the page is not attached to the list. */
55811 ma_free(pPage, pAllocationCallbacks);
55812
55813 return MA_SUCCESS;
55814}
55815
55817{
55818 if (pData == NULL || pPage == NULL) {
55819 return MA_INVALID_ARGS;
55820 }
55821
55822 /* 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. */
55823
55824 /* First thing to do is update the tail. */
55825 for (;;) {
55826 ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->pTail);
55827 ma_paged_audio_buffer_page* pNewTail = pPage;
55828
55829 if (c89atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) {
55830 /* 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. */
55831 c89atomic_exchange_ptr(&pOldTail->pNext, pPage);
55832 break; /* Done. */
55833 }
55834 }
55835
55836 return MA_SUCCESS;
55837}
55838
55839MA_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)
55840{
55841 ma_result result;
55843
55844 result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage);
55845 if (result != MA_SUCCESS) {
55846 return result;
55847 }
55848
55849 return ma_paged_audio_buffer_data_append_page(pData, pPage); /* <-- Should never fail. */
55850}
55851
55852
55854{
55856
55857 MA_ZERO_OBJECT(&config);
55858 config.pData = pData;
55859
55860 return config;
55861}
55862
55863
55864static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
55865{
55866 return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead);
55867}
55868
55869static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
55870{
55871 return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex);
55872}
55873
55874static 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)
55875{
55876 ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource;
55877
55878 *pFormat = pPagedAudioBuffer->pData->format;
55879 *pChannels = pPagedAudioBuffer->pData->channels;
55880 *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */
55881 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels);
55882
55883 return MA_SUCCESS;
55884}
55885
55886static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
55887{
55889}
55890
55891static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
55892{
55894}
55895
55896static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable =
55897{
55898 ma_paged_audio_buffer__data_source_on_read,
55899 ma_paged_audio_buffer__data_source_on_seek,
55900 ma_paged_audio_buffer__data_source_on_get_data_format,
55901 ma_paged_audio_buffer__data_source_on_get_cursor,
55902 ma_paged_audio_buffer__data_source_on_get_length,
55903 NULL, /* onSetLooping */
55904 0
55905};
55906
55908{
55909 ma_result result;
55910 ma_data_source_config dataSourceConfig;
55911
55912 if (pPagedAudioBuffer == NULL) {
55913 return MA_INVALID_ARGS;
55914 }
55915
55916 MA_ZERO_OBJECT(pPagedAudioBuffer);
55917
55918 /* A config is required for the format and channel count. */
55919 if (pConfig == NULL) {
55920 return MA_INVALID_ARGS;
55921 }
55922
55923 if (pConfig->pData == NULL) {
55924 return MA_INVALID_ARGS; /* No underlying data specified. */
55925 }
55926
55927 dataSourceConfig = ma_data_source_config_init();
55928 dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable;
55929
55930 result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds);
55931 if (result != MA_SUCCESS) {
55932 return result;
55933 }
55934
55935 pPagedAudioBuffer->pData = pConfig->pData;
55936 pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData);
55937 pPagedAudioBuffer->relativeCursor = 0;
55938 pPagedAudioBuffer->absoluteCursor = 0;
55939
55940 return MA_SUCCESS;
55941}
55942
55944{
55945 if (pPagedAudioBuffer == NULL) {
55946 return;
55947 }
55948
55949 /* Nothing to do. The data needs to be deleted separately. */
55950}
55951
55952MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
55953{
55954 ma_result result = MA_SUCCESS;
55955 ma_uint64 totalFramesRead = 0;
55956 ma_format format;
55957 ma_uint32 channels;
55958
55959 if (pPagedAudioBuffer == NULL) {
55960 return MA_INVALID_ARGS;
55961 }
55962
55963 format = pPagedAudioBuffer->pData->format;
55964 channels = pPagedAudioBuffer->pData->channels;
55965
55966 while (totalFramesRead < frameCount) {
55967 /* Read from the current page. The buffer should never be in a state where this is NULL. */
55968 ma_uint64 framesRemainingInCurrentPage;
55969 ma_uint64 framesRemainingToRead = frameCount - totalFramesRead;
55970 ma_uint64 framesToReadThisIteration;
55971
55972 MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL);
55973
55974 framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor;
55975
55976 framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead);
55977 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);
55978 totalFramesRead += framesToReadThisIteration;
55979
55980 pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration;
55981 pPagedAudioBuffer->relativeCursor += framesToReadThisIteration;
55982
55983 /* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */
55984 MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames);
55985
55986 if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) {
55987 /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */
55988 ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext);
55989 if (pNext == NULL) {
55990 result = MA_AT_END;
55991 break; /* We've reached the end. */
55992 } else {
55993 pPagedAudioBuffer->pCurrent = pNext;
55994 pPagedAudioBuffer->relativeCursor = 0;
55995 }
55996 }
55997 }
55998
55999 if (pFramesRead != NULL) {
56000 *pFramesRead = totalFramesRead;
56001 }
56002
56003 return result;
56004}
56005
56007{
56008 if (pPagedAudioBuffer == NULL) {
56009 return MA_INVALID_ARGS;
56010 }
56011
56012 if (frameIndex == pPagedAudioBuffer->absoluteCursor) {
56013 return MA_SUCCESS; /* Nothing to do. */
56014 }
56015
56016 if (frameIndex < pPagedAudioBuffer->absoluteCursor) {
56017 /* Moving backwards. Need to move the cursor back to the start, and then move forward. */
56018 pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData);
56019 pPagedAudioBuffer->absoluteCursor = 0;
56020 pPagedAudioBuffer->relativeCursor = 0;
56021
56022 /* Fall through to the forward seeking section below. */
56023 }
56024
56025 if (frameIndex > pPagedAudioBuffer->absoluteCursor) {
56026 /* Moving forward. */
56028 ma_uint64 runningCursor = 0;
56029
56030 for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) {
56031 ma_uint64 pageRangeBeg = runningCursor;
56032 ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames;
56033
56034 if (frameIndex >= pageRangeBeg) {
56035 if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)c89atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */
56036 /* We found the page. */
56037 pPagedAudioBuffer->pCurrent = pPage;
56038 pPagedAudioBuffer->absoluteCursor = frameIndex;
56039 pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg;
56040 return MA_SUCCESS;
56041 }
56042 }
56043
56044 runningCursor = pageRangeEnd;
56045 }
56046
56047 /* Getting here means we tried seeking too far forward. Don't change any state. */
56048 return MA_BAD_SEEK;
56049 }
56050
56051 return MA_SUCCESS;
56052}
56053
56055{
56056 if (pCursor == NULL) {
56057 return MA_INVALID_ARGS;
56058 }
56059
56060 *pCursor = 0; /* Safety. */
56061
56062 if (pPagedAudioBuffer == NULL) {
56063 return MA_INVALID_ARGS;
56064 }
56065
56066 *pCursor = pPagedAudioBuffer->absoluteCursor;
56067
56068 return MA_SUCCESS;
56069}
56070
56072{
56073 return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength);
56074}
56075
56076
56077
56078
56083MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
56084{
56085 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
56086
56087 if (pFile == NULL) {
56088 return MA_INVALID_ARGS;
56089 }
56090
56091 *pFile = NULL;
56092
56093 if (pVFS == NULL || pFilePath == NULL || openMode == 0) {
56094 return MA_INVALID_ARGS;
56095 }
56096
56097 if (pCallbacks->onOpen == NULL) {
56098 return MA_NOT_IMPLEMENTED;
56099 }
56100
56101 return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile);
56102}
56103
56104MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
56105{
56106 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
56107
56108 if (pFile == NULL) {
56109 return MA_INVALID_ARGS;
56110 }
56111
56112 *pFile = NULL;
56113
56114 if (pVFS == NULL || pFilePath == NULL || openMode == 0) {
56115 return MA_INVALID_ARGS;
56116 }
56117
56118 if (pCallbacks->onOpenW == NULL) {
56119 return MA_NOT_IMPLEMENTED;
56120 }
56121
56122 return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile);
56123}
56124
56126{
56127 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
56128
56129 if (pVFS == NULL || file == NULL) {
56130 return MA_INVALID_ARGS;
56131 }
56132
56133 if (pCallbacks->onClose == NULL) {
56134 return MA_NOT_IMPLEMENTED;
56135 }
56136
56137 return pCallbacks->onClose(pVFS, file);
56138}
56139
56140MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
56141{
56142 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
56143 ma_result result;
56144 size_t bytesRead;
56145
56146 if (pBytesRead != NULL) {
56147 *pBytesRead = 0;
56148 }
56149
56150 if (pVFS == NULL || file == NULL || pDst == NULL) {
56151 return MA_INVALID_ARGS;
56152 }
56153
56154 if (pCallbacks->onRead == NULL) {
56155 return MA_NOT_IMPLEMENTED;
56156 }
56157
56158 result = pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, &bytesRead);
56159
56160 if (pBytesRead != NULL) {
56161 *pBytesRead = bytesRead;
56162 }
56163
56164 if (result == MA_SUCCESS && bytesRead == 0 && sizeInBytes > 0) {
56165 result = MA_AT_END;
56166 }
56167
56168 return result;
56169}
56170
56171MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
56172{
56173 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
56174
56175 if (pBytesWritten != NULL) {
56176 *pBytesWritten = 0;
56177 }
56178
56179 if (pVFS == NULL || file == NULL || pSrc == NULL) {
56180 return MA_INVALID_ARGS;
56181 }
56182
56183 if (pCallbacks->onWrite == NULL) {
56184 return MA_NOT_IMPLEMENTED;
56185 }
56186
56187 return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
56188}
56189
56191{
56192 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
56193
56194 if (pVFS == NULL || file == NULL) {
56195 return MA_INVALID_ARGS;
56196 }
56197
56198 if (pCallbacks->onSeek == NULL) {
56199 return MA_NOT_IMPLEMENTED;
56200 }
56201
56202 return pCallbacks->onSeek(pVFS, file, offset, origin);
56203}
56204
56206{
56207 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
56208
56209 if (pCursor == NULL) {
56210 return MA_INVALID_ARGS;
56211 }
56212
56213 *pCursor = 0;
56214
56215 if (pVFS == NULL || file == NULL) {
56216 return MA_INVALID_ARGS;
56217 }
56218
56219 if (pCallbacks->onTell == NULL) {
56220 return MA_NOT_IMPLEMENTED;
56221 }
56222
56223 return pCallbacks->onTell(pVFS, file, pCursor);
56224}
56225
56227{
56228 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
56229
56230 if (pInfo == NULL) {
56231 return MA_INVALID_ARGS;
56232 }
56233
56234 MA_ZERO_OBJECT(pInfo);
56235
56236 if (pVFS == NULL || file == NULL) {
56237 return MA_INVALID_ARGS;
56238 }
56239
56240 if (pCallbacks->onInfo == NULL) {
56241 return MA_NOT_IMPLEMENTED;
56242 }
56243
56244 return pCallbacks->onInfo(pVFS, file, pInfo);
56245}
56246
56247
56248static 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)
56249{
56250 ma_result result;
56251 ma_vfs_file file;
56252 ma_file_info info;
56253 void* pData;
56254 size_t bytesRead;
56255
56256 if (ppData != NULL) {
56257 *ppData = NULL;
56258 }
56259 if (pSize != NULL) {
56260 *pSize = 0;
56261 }
56262
56263 if (ppData == NULL) {
56264 return MA_INVALID_ARGS;
56265 }
56266
56267 if (pFilePath != NULL) {
56268 result = ma_vfs_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
56269 } else {
56270 result = ma_vfs_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file);
56271 }
56272 if (result != MA_SUCCESS) {
56273 return result;
56274 }
56275
56276 result = ma_vfs_info(pVFS, file, &info);
56277 if (result != MA_SUCCESS) {
56278 ma_vfs_close(pVFS, file);
56279 return result;
56280 }
56281
56282 if (info.sizeInBytes > MA_SIZE_MAX) {
56283 ma_vfs_close(pVFS, file);
56284 return MA_TOO_BIG;
56285 }
56286
56287 pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */
56288 if (pData == NULL) {
56289 ma_vfs_close(pVFS, file);
56290 return result;
56291 }
56292
56293 result = ma_vfs_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */
56294 ma_vfs_close(pVFS, file);
56295
56296 if (result != MA_SUCCESS) {
56297 ma_free(pData, pAllocationCallbacks);
56298 return result;
56299 }
56300
56301 if (pSize != NULL) {
56302 *pSize = bytesRead;
56303 }
56304
56305 MA_ASSERT(ppData != NULL);
56306 *ppData = pData;
56307
56308 return MA_SUCCESS;
56309}
56310
56311MA_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)
56312{
56313 return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks);
56314}
56315
56316MA_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)
56317{
56318 return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks);
56319}
56320
56321
56322#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
56323static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition)
56324{
56325 *pDesiredAccess = 0;
56326 if ((openMode & MA_OPEN_MODE_READ) != 0) {
56327 *pDesiredAccess |= GENERIC_READ;
56328 }
56329 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
56330 *pDesiredAccess |= GENERIC_WRITE;
56331 }
56332
56333 *pShareMode = 0;
56334 if ((openMode & MA_OPEN_MODE_READ) != 0) {
56335 *pShareMode |= FILE_SHARE_READ;
56336 }
56337
56338 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
56339 *pCreationDisposition = CREATE_ALWAYS; /* Opening in write mode. Truncate. */
56340 } else {
56341 *pCreationDisposition = OPEN_EXISTING; /* Opening in read mode. File must exist. */
56342 }
56343}
56344
56345static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
56346{
56347 HANDLE hFile;
56348 DWORD dwDesiredAccess;
56349 DWORD dwShareMode;
56350 DWORD dwCreationDisposition;
56351
56352 (void)pVFS;
56353
56354 ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);
56355
56356 hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
56357 if (hFile == INVALID_HANDLE_VALUE) {
56358 return ma_result_from_GetLastError(GetLastError());
56359 }
56360
56361 *pFile = hFile;
56362 return MA_SUCCESS;
56363}
56364
56365static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
56366{
56367 HANDLE hFile;
56368 DWORD dwDesiredAccess;
56369 DWORD dwShareMode;
56370 DWORD dwCreationDisposition;
56371
56372 (void)pVFS;
56373
56374 ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);
56375
56376 hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
56377 if (hFile == INVALID_HANDLE_VALUE) {
56378 return ma_result_from_GetLastError(GetLastError());
56379 }
56380
56381 *pFile = hFile;
56382 return MA_SUCCESS;
56383}
56384
56385static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file)
56386{
56387 (void)pVFS;
56388
56389 if (CloseHandle((HANDLE)file) == 0) {
56390 return ma_result_from_GetLastError(GetLastError());
56391 }
56392
56393 return MA_SUCCESS;
56394}
56395
56396
56397static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
56398{
56399 ma_result result = MA_SUCCESS;
56400 size_t totalBytesRead;
56401
56402 (void)pVFS;
56403
56404 totalBytesRead = 0;
56405 while (totalBytesRead < sizeInBytes) {
56406 size_t bytesRemaining;
56407 DWORD bytesToRead;
56408 DWORD bytesRead;
56409 BOOL readResult;
56410
56411 bytesRemaining = sizeInBytes - totalBytesRead;
56412 if (bytesRemaining >= 0xFFFFFFFF) {
56413 bytesToRead = 0xFFFFFFFF;
56414 } else {
56415 bytesToRead = (DWORD)bytesRemaining;
56416 }
56417
56418 readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL);
56419 if (readResult == 1 && bytesRead == 0) {
56420 result = MA_AT_END;
56421 break; /* EOF */
56422 }
56423
56424 totalBytesRead += bytesRead;
56425
56426 if (bytesRead < bytesToRead) {
56427 break; /* EOF */
56428 }
56429
56430 if (readResult == 0) {
56431 result = ma_result_from_GetLastError(GetLastError());
56432 break;
56433 }
56434 }
56435
56436 if (pBytesRead != NULL) {
56437 *pBytesRead = totalBytesRead;
56438 }
56439
56440 return result;
56441}
56442
56443static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
56444{
56445 ma_result result = MA_SUCCESS;
56446 size_t totalBytesWritten;
56447
56448 (void)pVFS;
56449
56450 totalBytesWritten = 0;
56451 while (totalBytesWritten < sizeInBytes) {
56452 size_t bytesRemaining;
56453 DWORD bytesToWrite;
56454 DWORD bytesWritten;
56455 BOOL writeResult;
56456
56457 bytesRemaining = sizeInBytes - totalBytesWritten;
56458 if (bytesRemaining >= 0xFFFFFFFF) {
56459 bytesToWrite = 0xFFFFFFFF;
56460 } else {
56461 bytesToWrite = (DWORD)bytesRemaining;
56462 }
56463
56464 writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL);
56465 totalBytesWritten += bytesWritten;
56466
56467 if (writeResult == 0) {
56468 result = ma_result_from_GetLastError(GetLastError());
56469 break;
56470 }
56471 }
56472
56473 if (pBytesWritten != NULL) {
56474 *pBytesWritten = totalBytesWritten;
56475 }
56476
56477 return result;
56478}
56479
56480
56481static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
56482{
56483 LARGE_INTEGER liDistanceToMove;
56484 DWORD dwMoveMethod;
56485 BOOL result;
56486
56487 (void)pVFS;
56488
56489 liDistanceToMove.QuadPart = offset;
56490
56491 /* */ if (origin == ma_seek_origin_current) {
56492 dwMoveMethod = FILE_CURRENT;
56493 } else if (origin == ma_seek_origin_end) {
56494 dwMoveMethod = FILE_END;
56495 } else {
56496 dwMoveMethod = FILE_BEGIN;
56497 }
56498
56499#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__)
56500 /* No SetFilePointerEx() so restrict to 31 bits. */
56501 if (origin > 0x7FFFFFFF) {
56502 return MA_OUT_OF_RANGE;
56503 }
56504
56505 result = SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod);
56506#else
56507 result = SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod);
56508#endif
56509 if (result == 0) {
56510 return ma_result_from_GetLastError(GetLastError());
56511 }
56512
56513 return MA_SUCCESS;
56514}
56515
56516static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
56517{
56518 LARGE_INTEGER liZero;
56519 LARGE_INTEGER liTell;
56520 BOOL result;
56521#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__)
56522 LONG tell;
56523#endif
56524
56525 (void)pVFS;
56526
56527 liZero.QuadPart = 0;
56528
56529#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__)
56530 result = SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT);
56531 liTell.QuadPart = tell;
56532#else
56533 result = SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT);
56534#endif
56535 if (result == 0) {
56536 return ma_result_from_GetLastError(GetLastError());
56537 }
56538
56539 if (pCursor != NULL) {
56540 *pCursor = liTell.QuadPart;
56541 }
56542
56543 return MA_SUCCESS;
56544}
56545
56546static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
56547{
56548 BY_HANDLE_FILE_INFORMATION fi;
56549 BOOL result;
56550
56551 (void)pVFS;
56552
56553 result = GetFileInformationByHandle((HANDLE)file, &fi);
56554 if (result == 0) {
56555 return ma_result_from_GetLastError(GetLastError());
56556 }
56557
56558 pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow);
56559
56560 return MA_SUCCESS;
56561}
56562#else
56563static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
56564{
56565 ma_result result;
56566 FILE* pFileStd;
56567 const char* pOpenModeStr;
56568
56569 MA_ASSERT(pFilePath != NULL);
56570 MA_ASSERT(openMode != 0);
56571 MA_ASSERT(pFile != NULL);
56572
56573 (void)pVFS;
56574
56575 if ((openMode & MA_OPEN_MODE_READ) != 0) {
56576 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
56577 pOpenModeStr = "r+";
56578 } else {
56579 pOpenModeStr = "rb";
56580 }
56581 } else {
56582 pOpenModeStr = "wb";
56583 }
56584
56585 result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr);
56586 if (result != MA_SUCCESS) {
56587 return result;
56588 }
56589
56590 *pFile = pFileStd;
56591
56592 return MA_SUCCESS;
56593}
56594
56595static ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
56596{
56597 ma_result result;
56598 FILE* pFileStd;
56599 const wchar_t* pOpenModeStr;
56600
56601 MA_ASSERT(pFilePath != NULL);
56602 MA_ASSERT(openMode != 0);
56603 MA_ASSERT(pFile != NULL);
56604
56605 (void)pVFS;
56606
56607 if ((openMode & MA_OPEN_MODE_READ) != 0) {
56608 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
56609 pOpenModeStr = L"r+";
56610 } else {
56611 pOpenModeStr = L"rb";
56612 }
56613 } else {
56614 pOpenModeStr = L"wb";
56615 }
56616
56617 result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL);
56618 if (result != MA_SUCCESS) {
56619 return result;
56620 }
56621
56622 *pFile = pFileStd;
56623
56624 return MA_SUCCESS;
56625}
56626
56627static ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file)
56628{
56629 MA_ASSERT(file != NULL);
56630
56631 (void)pVFS;
56632
56633 fclose((FILE*)file);
56634
56635 return MA_SUCCESS;
56636}
56637
56638static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
56639{
56640 size_t result;
56641
56642 MA_ASSERT(file != NULL);
56643 MA_ASSERT(pDst != NULL);
56644
56645 (void)pVFS;
56646
56647 result = fread(pDst, 1, sizeInBytes, (FILE*)file);
56648
56649 if (pBytesRead != NULL) {
56650 *pBytesRead = result;
56651 }
56652
56653 if (result != sizeInBytes) {
56654 if (result == 0 && feof((FILE*)file)) {
56655 return MA_AT_END;
56656 } else {
56657 return ma_result_from_errno(ferror((FILE*)file));
56658 }
56659 }
56660
56661 return MA_SUCCESS;
56662}
56663
56664static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
56665{
56666 size_t result;
56667
56668 MA_ASSERT(file != NULL);
56669 MA_ASSERT(pSrc != NULL);
56670
56671 (void)pVFS;
56672
56673 result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file);
56674
56675 if (pBytesWritten != NULL) {
56676 *pBytesWritten = result;
56677 }
56678
56679 if (result != sizeInBytes) {
56680 return ma_result_from_errno(ferror((FILE*)file));
56681 }
56682
56683 return MA_SUCCESS;
56684}
56685
56686static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
56687{
56688 int result;
56689 int whence;
56690
56691 MA_ASSERT(file != NULL);
56692
56693 (void)pVFS;
56694
56695 if (origin == ma_seek_origin_start) {
56696 whence = SEEK_SET;
56697 } else if (origin == ma_seek_origin_end) {
56698 whence = SEEK_END;
56699 } else {
56700 whence = SEEK_CUR;
56701 }
56702
56703#if defined(_WIN32)
56704 #if defined(_MSC_VER) && _MSC_VER > 1200
56705 result = _fseeki64((FILE*)file, offset, whence);
56706 #else
56707 /* No _fseeki64() so restrict to 31 bits. */
56708 if (origin > 0x7FFFFFFF) {
56709 return MA_OUT_OF_RANGE;
56710 }
56711
56712 result = fseek((FILE*)file, (int)offset, whence);
56713 #endif
56714#else
56715 result = fseek((FILE*)file, (long int)offset, whence);
56716#endif
56717 if (result != 0) {
56718 return MA_ERROR;
56719 }
56720
56721 return MA_SUCCESS;
56722}
56723
56724static ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
56725{
56726 ma_int64 result;
56727
56728 MA_ASSERT(file != NULL);
56729 MA_ASSERT(pCursor != NULL);
56730
56731 (void)pVFS;
56732
56733#if defined(_WIN32)
56734 #if defined(_MSC_VER) && _MSC_VER > 1200
56735 result = _ftelli64((FILE*)file);
56736 #else
56737 result = ftell((FILE*)file);
56738 #endif
56739#else
56740 result = ftell((FILE*)file);
56741#endif
56742
56743 *pCursor = result;
56744
56745 return MA_SUCCESS;
56746}
56747
56748#if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD)
56749int fileno(FILE *stream);
56750#endif
56751
56752static ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
56753{
56754 int fd;
56755 struct stat info;
56756
56757 MA_ASSERT(file != NULL);
56758 MA_ASSERT(pInfo != NULL);
56759
56760 (void)pVFS;
56761
56762#if defined(_MSC_VER)
56763 fd = _fileno((FILE*)file);
56764#else
56765 fd = fileno((FILE*)file);
56766#endif
56767
56768 if (fstat(fd, &info) != 0) {
56769 return ma_result_from_errno(errno);
56770 }
56771
56772 pInfo->sizeInBytes = info.st_size;
56773
56774 return MA_SUCCESS;
56775}
56776#endif
56777
56778
56779static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
56780{
56781 if (pFile == NULL) {
56782 return MA_INVALID_ARGS;
56783 }
56784
56785 *pFile = NULL;
56786
56787 if (pFilePath == NULL || openMode == 0) {
56788 return MA_INVALID_ARGS;
56789 }
56790
56791#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
56792 return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile);
56793#else
56794 return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile);
56795#endif
56796}
56797
56798static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
56799{
56800 if (pFile == NULL) {
56801 return MA_INVALID_ARGS;
56802 }
56803
56804 *pFile = NULL;
56805
56806 if (pFilePath == NULL || openMode == 0) {
56807 return MA_INVALID_ARGS;
56808 }
56809
56810#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
56811 return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile);
56812#else
56813 return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile);
56814#endif
56815}
56816
56817static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file)
56818{
56819 if (file == NULL) {
56820 return MA_INVALID_ARGS;
56821 }
56822
56823#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
56824 return ma_default_vfs_close__win32(pVFS, file);
56825#else
56826 return ma_default_vfs_close__stdio(pVFS, file);
56827#endif
56828}
56829
56830static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
56831{
56832 if (pBytesRead != NULL) {
56833 *pBytesRead = 0;
56834 }
56835
56836 if (file == NULL || pDst == NULL) {
56837 return MA_INVALID_ARGS;
56838 }
56839
56840#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
56841 return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead);
56842#else
56843 return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead);
56844#endif
56845}
56846
56847static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
56848{
56849 if (pBytesWritten != NULL) {
56850 *pBytesWritten = 0;
56851 }
56852
56853 if (file == NULL || pSrc == NULL) {
56854 return MA_INVALID_ARGS;
56855 }
56856
56857#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
56858 return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
56859#else
56860 return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
56861#endif
56862}
56863
56864static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
56865{
56866 if (file == NULL) {
56867 return MA_INVALID_ARGS;
56868 }
56869
56870#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
56871 return ma_default_vfs_seek__win32(pVFS, file, offset, origin);
56872#else
56873 return ma_default_vfs_seek__stdio(pVFS, file, offset, origin);
56874#endif
56875}
56876
56877static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
56878{
56879 if (pCursor == NULL) {
56880 return MA_INVALID_ARGS;
56881 }
56882
56883 *pCursor = 0;
56884
56885 if (file == NULL) {
56886 return MA_INVALID_ARGS;
56887 }
56888
56889#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
56890 return ma_default_vfs_tell__win32(pVFS, file, pCursor);
56891#else
56892 return ma_default_vfs_tell__stdio(pVFS, file, pCursor);
56893#endif
56894}
56895
56896static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
56897{
56898 if (pInfo == NULL) {
56899 return MA_INVALID_ARGS;
56900 }
56901
56902 MA_ZERO_OBJECT(pInfo);
56903
56904 if (file == NULL) {
56905 return MA_INVALID_ARGS;
56906 }
56907
56908#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
56909 return ma_default_vfs_info__win32(pVFS, file, pInfo);
56910#else
56911 return ma_default_vfs_info__stdio(pVFS, file, pInfo);
56912#endif
56913}
56914
56915
56917{
56918 if (pVFS == NULL) {
56919 return MA_INVALID_ARGS;
56920 }
56921
56922 pVFS->cb.onOpen = ma_default_vfs_open;
56923 pVFS->cb.onOpenW = ma_default_vfs_open_w;
56924 pVFS->cb.onClose = ma_default_vfs_close;
56925 pVFS->cb.onRead = ma_default_vfs_read;
56926 pVFS->cb.onWrite = ma_default_vfs_write;
56927 pVFS->cb.onSeek = ma_default_vfs_seek;
56928 pVFS->cb.onTell = ma_default_vfs_tell;
56929 pVFS->cb.onInfo = ma_default_vfs_info;
56930 ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks);
56931
56932 return MA_SUCCESS;
56933}
56934
56935
56936MA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
56937{
56938 if (pVFS != NULL) {
56939 return ma_vfs_open(pVFS, pFilePath, openMode, pFile);
56940 } else {
56941 return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile);
56942 }
56943}
56944
56945MA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
56946{
56947 if (pVFS != NULL) {
56948 return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile);
56949 } else {
56950 return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile);
56951 }
56952}
56953
56954MA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file)
56955{
56956 if (pVFS != NULL) {
56957 return ma_vfs_close(pVFS, file);
56958 } else {
56959 return ma_default_vfs_close(pVFS, file);
56960 }
56961}
56962
56963MA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
56964{
56965 if (pVFS != NULL) {
56966 return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead);
56967 } else {
56968 return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead);
56969 }
56970}
56971
56972MA_API ma_result ma_vfs_or_default_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
56973{
56974 if (pVFS != NULL) {
56975 return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
56976 } else {
56977 return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
56978 }
56979}
56980
56981MA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
56982{
56983 if (pVFS != NULL) {
56984 return ma_vfs_seek(pVFS, file, offset, origin);
56985 } else {
56986 return ma_default_vfs_seek(pVFS, file, offset, origin);
56987 }
56988}
56989
56990MA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
56991{
56992 if (pVFS != NULL) {
56993 return ma_vfs_tell(pVFS, file, pCursor);
56994 } else {
56995 return ma_default_vfs_tell(pVFS, file, pCursor);
56996 }
56997}
56998
56999MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
57000{
57001 if (pVFS != NULL) {
57002 return ma_vfs_info(pVFS, file, pInfo);
57003 } else {
57004 return ma_default_vfs_info(pVFS, file, pInfo);
57005 }
57006}
57007
57008
57009
57010
57015#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
57016/* dr_wav_h begin */
57017#ifndef dr_wav_h
57018#define dr_wav_h
57019#ifdef __cplusplus
57020extern "C" {
57021#endif
57022#define DRWAV_STRINGIFY(x) #x
57023#define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x)
57024#define DRWAV_VERSION_MAJOR 0
57025#define DRWAV_VERSION_MINOR 13
57026#define DRWAV_VERSION_REVISION 5
57027#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)
57028#include <stddef.h>
57029typedef signed char drwav_int8;
57030typedef unsigned char drwav_uint8;
57031typedef signed short drwav_int16;
57032typedef unsigned short drwav_uint16;
57033typedef signed int drwav_int32;
57034typedef unsigned int drwav_uint32;
57035#if defined(_MSC_VER) && !defined(__clang__)
57036 typedef signed __int64 drwav_int64;
57037 typedef unsigned __int64 drwav_uint64;
57038#else
57039 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
57040 #pragma GCC diagnostic push
57041 #pragma GCC diagnostic ignored "-Wlong-long"
57042 #if defined(__clang__)
57043 #pragma GCC diagnostic ignored "-Wc++11-long-long"
57044 #endif
57045 #endif
57046 typedef signed long long drwav_int64;
57047 typedef unsigned long long drwav_uint64;
57048 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
57049 #pragma GCC diagnostic pop
57050 #endif
57051#endif
57052#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__)
57054#else
57056#endif
57057typedef drwav_uint8 drwav_bool8;
57059#define DRWAV_TRUE 1
57060#define DRWAV_FALSE 0
57061#if !defined(DRWAV_API)
57062 #if defined(DRWAV_DLL)
57063 #if defined(_WIN32)
57064 #define DRWAV_DLL_IMPORT __declspec(dllimport)
57065 #define DRWAV_DLL_EXPORT __declspec(dllexport)
57066 #define DRWAV_DLL_PRIVATE static
57067 #else
57068 #if defined(__GNUC__) && __GNUC__ >= 4
57069 #define DRWAV_DLL_IMPORT __attribute__((visibility("default")))
57070 #define DRWAV_DLL_EXPORT __attribute__((visibility("default")))
57071 #define DRWAV_DLL_PRIVATE __attribute__((visibility("hidden")))
57072 #else
57073 #define DRWAV_DLL_IMPORT
57074 #define DRWAV_DLL_EXPORT
57075 #define DRWAV_DLL_PRIVATE static
57076 #endif
57077 #endif
57078 #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION)
57079 #define DRWAV_API DRWAV_DLL_EXPORT
57080 #else
57081 #define DRWAV_API DRWAV_DLL_IMPORT
57082 #endif
57083 #define DRWAV_PRIVATE DRWAV_DLL_PRIVATE
57084 #else
57085 #define DRWAV_API extern
57086 #define DRWAV_PRIVATE static
57087 #endif
57088#endif
57090#define DRWAV_SUCCESS 0
57091#define DRWAV_ERROR -1
57092#define DRWAV_INVALID_ARGS -2
57093#define DRWAV_INVALID_OPERATION -3
57094#define DRWAV_OUT_OF_MEMORY -4
57095#define DRWAV_OUT_OF_RANGE -5
57096#define DRWAV_ACCESS_DENIED -6
57097#define DRWAV_DOES_NOT_EXIST -7
57098#define DRWAV_ALREADY_EXISTS -8
57099#define DRWAV_TOO_MANY_OPEN_FILES -9
57100#define DRWAV_INVALID_FILE -10
57101#define DRWAV_TOO_BIG -11
57102#define DRWAV_PATH_TOO_LONG -12
57103#define DRWAV_NAME_TOO_LONG -13
57104#define DRWAV_NOT_DIRECTORY -14
57105#define DRWAV_IS_DIRECTORY -15
57106#define DRWAV_DIRECTORY_NOT_EMPTY -16
57107#define DRWAV_END_OF_FILE -17
57108#define DRWAV_NO_SPACE -18
57109#define DRWAV_BUSY -19
57110#define DRWAV_IO_ERROR -20
57111#define DRWAV_INTERRUPT -21
57112#define DRWAV_UNAVAILABLE -22
57113#define DRWAV_ALREADY_IN_USE -23
57114#define DRWAV_BAD_ADDRESS -24
57115#define DRWAV_BAD_SEEK -25
57116#define DRWAV_BAD_PIPE -26
57117#define DRWAV_DEADLOCK -27
57118#define DRWAV_TOO_MANY_LINKS -28
57119#define DRWAV_NOT_IMPLEMENTED -29
57120#define DRWAV_NO_MESSAGE -30
57121#define DRWAV_BAD_MESSAGE -31
57122#define DRWAV_NO_DATA_AVAILABLE -32
57123#define DRWAV_INVALID_DATA -33
57124#define DRWAV_TIMEOUT -34
57125#define DRWAV_NO_NETWORK -35
57126#define DRWAV_NOT_UNIQUE -36
57127#define DRWAV_NOT_SOCKET -37
57128#define DRWAV_NO_ADDRESS -38
57129#define DRWAV_BAD_PROTOCOL -39
57130#define DRWAV_PROTOCOL_UNAVAILABLE -40
57131#define DRWAV_PROTOCOL_NOT_SUPPORTED -41
57132#define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED -42
57133#define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED -43
57134#define DRWAV_SOCKET_NOT_SUPPORTED -44
57135#define DRWAV_CONNECTION_RESET -45
57136#define DRWAV_ALREADY_CONNECTED -46
57137#define DRWAV_NOT_CONNECTED -47
57138#define DRWAV_CONNECTION_REFUSED -48
57139#define DRWAV_NO_HOST -49
57140#define DRWAV_IN_PROGRESS -50
57141#define DRWAV_CANCELLED -51
57142#define DRWAV_MEMORY_ALREADY_MAPPED -52
57143#define DRWAV_AT_END -53
57144#define DR_WAVE_FORMAT_PCM 0x1
57145#define DR_WAVE_FORMAT_ADPCM 0x2
57146#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3
57147#define DR_WAVE_FORMAT_ALAW 0x6
57148#define DR_WAVE_FORMAT_MULAW 0x7
57149#define DR_WAVE_FORMAT_DVI_ADPCM 0x11
57150#define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE
57151#define DRWAV_SEQUENTIAL 0x00000001
57152DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision);
57153DRWAV_API const char* drwav_version_string(void);
57154typedef enum
57155{
57159typedef enum
57160{
57165typedef struct
57166{
57167 union
57168 {
57169 drwav_uint8 fourcc[4];
57170 drwav_uint8 guid[16];
57171 } id;
57172 drwav_uint64 sizeInBytes;
57173 unsigned int paddingSize;
57175typedef struct
57176{
57177 drwav_uint16 formatTag;
57178 drwav_uint16 channels;
57179 drwav_uint32 sampleRate;
57180 drwav_uint32 avgBytesPerSec;
57181 drwav_uint16 blockAlign;
57182 drwav_uint16 bitsPerSample;
57183 drwav_uint16 extendedSize;
57184 drwav_uint16 validBitsPerSample;
57185 drwav_uint32 channelMask;
57186 drwav_uint8 subFormat[16];
57187} drwav_fmt;
57189typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
57190typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);
57191typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin);
57192typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader, drwav_container container, const drwav_fmt* pFMT);
57193typedef struct
57194{
57195 void* pUserData;
57196 void* (* onMalloc)(size_t sz, void* pUserData);
57197 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
57198 void (* onFree)(void* p, void* pUserData);
57200typedef struct
57201{
57202 const drwav_uint8* data;
57203 size_t dataSize;
57204 size_t currentReadPos;
57206typedef struct
57207{
57208 void** ppData;
57209 size_t* pDataSize;
57210 size_t dataSize;
57211 size_t dataCapacity;
57212 size_t currentWritePos;
57214typedef struct
57215{
57216 drwav_container container;
57217 drwav_uint32 format;
57218 drwav_uint32 channels;
57219 drwav_uint32 sampleRate;
57220 drwav_uint32 bitsPerSample;
57222typedef enum
57223{
57226 drwav_metadata_type_smpl = 1 << 1,
57227 drwav_metadata_type_inst = 1 << 2,
57228 drwav_metadata_type_cue = 1 << 3,
57229 drwav_metadata_type_acid = 1 << 4,
57230 drwav_metadata_type_bext = 1 << 5,
57258typedef enum
57259{
57264typedef struct
57265{
57266 drwav_uint32 cuePointId;
57267 drwav_uint32 type;
57268 drwav_uint32 firstSampleByteOffset;
57269 drwav_uint32 lastSampleByteOffset;
57270 drwav_uint32 sampleFraction;
57271 drwav_uint32 playCount;
57273typedef struct
57274{
57275 drwav_uint32 manufacturerId;
57276 drwav_uint32 productId;
57277 drwav_uint32 samplePeriodNanoseconds;
57278 drwav_uint32 midiUnityNote;
57279 drwav_uint32 midiPitchFraction;
57280 drwav_uint32 smpteFormat;
57281 drwav_uint32 smpteOffset;
57282 drwav_uint32 sampleLoopCount;
57283 drwav_uint32 samplerSpecificDataSizeInBytes;
57284 drwav_smpl_loop* pLoops;
57285 drwav_uint8* pSamplerSpecificData;
57286} drwav_smpl;
57287typedef struct
57288{
57289 drwav_int8 midiUnityNote;
57290 drwav_int8 fineTuneCents;
57291 drwav_int8 gainDecibels;
57292 drwav_int8 lowNote;
57293 drwav_int8 highNote;
57294 drwav_int8 lowVelocity;
57295 drwav_int8 highVelocity;
57296} drwav_inst;
57297typedef struct
57298{
57300 drwav_uint32 playOrderPosition;
57301 drwav_uint8 dataChunkId[4];
57302 drwav_uint32 chunkStart;
57303 drwav_uint32 blockStart;
57304 drwav_uint32 sampleByteOffset;
57306typedef struct
57307{
57308 drwav_uint32 cuePointCount;
57309 drwav_cue_point *pCuePoints;
57310} drwav_cue;
57311typedef enum
57312{
57319typedef struct
57320{
57321 drwav_uint32 flags;
57322 drwav_uint16 midiUnityNote;
57323 drwav_uint16 reserved1;
57324 float reserved2;
57325 drwav_uint32 numBeats;
57326 drwav_uint16 meterDenominator;
57327 drwav_uint16 meterNumerator;
57328 float tempo;
57329} drwav_acid;
57330typedef struct
57331{
57332 drwav_uint32 cuePointId;
57333 drwav_uint32 stringLength;
57334 char* pString;
57336typedef struct
57337{
57338 char* pDescription;
57339 char* pOriginatorName;
57340 char* pOriginatorReference;
57341 char pOriginationDate[10];
57342 char pOriginationTime[8];
57343 drwav_uint64 timeReference;
57344 drwav_uint16 version;
57345 char* pCodingHistory;
57346 drwav_uint32 codingHistorySize;
57347 drwav_uint8* pUMID;
57348 drwav_uint16 loudnessValue;
57349 drwav_uint16 loudnessRange;
57350 drwav_uint16 maxTruePeakLevel;
57351 drwav_uint16 maxMomentaryLoudness;
57352 drwav_uint16 maxShortTermLoudness;
57353} drwav_bext;
57354typedef struct
57355{
57356 drwav_uint32 stringLength;
57357 char* pString;
57359typedef struct
57360{
57361 drwav_uint32 cuePointId;
57362 drwav_uint32 sampleLength;
57363 drwav_uint8 purposeId[4];
57364 drwav_uint16 country;
57365 drwav_uint16 language;
57366 drwav_uint16 dialect;
57367 drwav_uint16 codePage;
57368 drwav_uint32 stringLength;
57369 char* pString;
57371typedef enum
57372{
57378typedef struct
57379{
57380 drwav_uint8 id[4];
57381 drwav_metadata_location chunkLocation;
57382 drwav_uint32 dataSizeInBytes;
57383 drwav_uint8* pData;
57385typedef struct
57386{
57388 union
57389 {
57390 drwav_cue cue;
57391 drwav_smpl smpl;
57392 drwav_acid acid;
57393 drwav_inst inst;
57394 drwav_bext bext;
57395 drwav_list_label_or_note labelOrNote;
57396 drwav_list_labelled_cue_region labelledCueRegion;
57397 drwav_list_info_text infoText;
57398 drwav_unknown_metadata unknown;
57399 } data;
57401typedef struct
57402{
57403 drwav_read_proc onRead;
57404 drwav_write_proc onWrite;
57405 drwav_seek_proc onSeek;
57406 void* pUserData;
57407 drwav_allocation_callbacks allocationCallbacks;
57408 drwav_container container;
57409 drwav_fmt fmt;
57410 drwav_uint32 sampleRate;
57411 drwav_uint16 channels;
57412 drwav_uint16 bitsPerSample;
57413 drwav_uint16 translatedFormatTag;
57414 drwav_uint64 totalPCMFrameCount;
57415 drwav_uint64 dataChunkDataSize;
57416 drwav_uint64 dataChunkDataPos;
57417 drwav_uint64 bytesRemaining;
57418 drwav_uint64 readCursorInPCMFrames;
57419 drwav_uint64 dataChunkDataSizeTargetWrite;
57420 drwav_bool32 isSequentialWrite;
57421 drwav_metadata_type allowedMetadataTypes;
57422 drwav_metadata* pMetadata;
57423 drwav_uint32 metadataCount;
57424 drwav__memory_stream memoryStream;
57425 drwav__memory_stream_write memoryStreamWrite;
57426 struct
57427 {
57428 drwav_uint32 bytesRemainingInBlock;
57429 drwav_uint16 predictor[2];
57430 drwav_int32 delta[2];
57431 drwav_int32 cachedFrames[4];
57432 drwav_uint32 cachedFrameCount;
57433 drwav_int32 prevFrames[2][2];
57434 } msadpcm;
57435 struct
57436 {
57437 drwav_uint32 bytesRemainingInBlock;
57438 drwav_int32 predictor[2];
57439 drwav_int32 stepIndex[2];
57440 drwav_int32 cachedFrames[16];
57441 drwav_uint32 cachedFrameCount;
57442 } ima;
57443} drwav;
57444DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
57445DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
57446DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
57447DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
57448DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
57449DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
57450DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount);
57451DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount);
57454DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut);
57455DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
57456DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
57457DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
57461DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData);
57462DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
57463DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
57464DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
57465#ifndef DR_WAV_NO_CONVERSION_API
57469DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
57470DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
57471DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount);
57472DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount);
57473DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount);
57474DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
57475DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
57476DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
57477DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
57478DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
57479DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
57480DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount);
57481DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
57482DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount);
57483DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount);
57484DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
57485DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
57489DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
57490DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount);
57491DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
57492DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount);
57493DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount);
57494DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
57495DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
57496#endif
57497#ifndef DR_WAV_NO_STDIO
57498DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks);
57499DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
57500DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks);
57501DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
57502DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
57503DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
57504DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
57505DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
57506DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
57507DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
57508DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
57509DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
57510#endif
57511DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks);
57512DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
57513DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
57514DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
57515DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
57516DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
57517#ifndef DR_WAV_NO_CONVERSION_API
57518DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
57519DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
57520DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
57521#ifndef DR_WAV_NO_STDIO
57522DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
57523DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
57524DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
57525DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
57526DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
57527DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
57528#endif
57529DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
57530DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
57531DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
57532#endif
57533DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks);
57540DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data);
57542DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b);
57543#ifdef __cplusplus
57544}
57545#endif
57546#endif
57547/* dr_wav_h end */
57548#endif /* MA_NO_WAV */
57549
57550#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
57551/* dr_flac_h begin */
57552#ifndef dr_flac_h
57553#define dr_flac_h
57554#ifdef __cplusplus
57555extern "C" {
57556#endif
57557#define DRFLAC_STRINGIFY(x) #x
57558#define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x)
57559#define DRFLAC_VERSION_MAJOR 0
57560#define DRFLAC_VERSION_MINOR 12
57561#define DRFLAC_VERSION_REVISION 37
57562#define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION)
57563#include <stddef.h>
57564typedef signed char drflac_int8;
57565typedef unsigned char drflac_uint8;
57566typedef signed short drflac_int16;
57567typedef unsigned short drflac_uint16;
57568typedef signed int drflac_int32;
57569typedef unsigned int drflac_uint32;
57570#if defined(_MSC_VER) && !defined(__clang__)
57571 typedef signed __int64 drflac_int64;
57572 typedef unsigned __int64 drflac_uint64;
57573#else
57574 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
57575 #pragma GCC diagnostic push
57576 #pragma GCC diagnostic ignored "-Wlong-long"
57577 #if defined(__clang__)
57578 #pragma GCC diagnostic ignored "-Wc++11-long-long"
57579 #endif
57580 #endif
57581 typedef signed long long drflac_int64;
57582 typedef unsigned long long drflac_uint64;
57583 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
57584 #pragma GCC diagnostic pop
57585 #endif
57586#endif
57587#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__)
57589#else
57591#endif
57594#define DRFLAC_TRUE 1
57595#define DRFLAC_FALSE 0
57596#if !defined(DRFLAC_API)
57597 #if defined(DRFLAC_DLL)
57598 #if defined(_WIN32)
57599 #define DRFLAC_DLL_IMPORT __declspec(dllimport)
57600 #define DRFLAC_DLL_EXPORT __declspec(dllexport)
57601 #define DRFLAC_DLL_PRIVATE static
57602 #else
57603 #if defined(__GNUC__) && __GNUC__ >= 4
57604 #define DRFLAC_DLL_IMPORT __attribute__((visibility("default")))
57605 #define DRFLAC_DLL_EXPORT __attribute__((visibility("default")))
57606 #define DRFLAC_DLL_PRIVATE __attribute__((visibility("hidden")))
57607 #else
57608 #define DRFLAC_DLL_IMPORT
57609 #define DRFLAC_DLL_EXPORT
57610 #define DRFLAC_DLL_PRIVATE static
57611 #endif
57612 #endif
57613 #if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION)
57614 #define DRFLAC_API DRFLAC_DLL_EXPORT
57615 #else
57616 #define DRFLAC_API DRFLAC_DLL_IMPORT
57617 #endif
57618 #define DRFLAC_PRIVATE DRFLAC_DLL_PRIVATE
57619 #else
57620 #define DRFLAC_API extern
57621 #define DRFLAC_PRIVATE static
57622 #endif
57623#endif
57624#if defined(_MSC_VER) && _MSC_VER >= 1700
57625 #define DRFLAC_DEPRECATED __declspec(deprecated)
57626#elif (defined(__GNUC__) && __GNUC__ >= 4)
57627 #define DRFLAC_DEPRECATED __attribute__((deprecated))
57628#elif defined(__has_feature)
57629 #if __has_feature(attribute_deprecated)
57630 #define DRFLAC_DEPRECATED __attribute__((deprecated))
57631 #else
57632 #define DRFLAC_DEPRECATED
57633 #endif
57634#else
57635 #define DRFLAC_DEPRECATED
57636#endif
57637DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision);
57638DRFLAC_API const char* drflac_version_string(void);
57639#ifndef DR_FLAC_BUFFER_SIZE
57640#define DR_FLAC_BUFFER_SIZE 4096
57641#endif
57642#if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
57643#define DRFLAC_64BIT
57644#endif
57645#ifdef DRFLAC_64BIT
57647#else
57649#endif
57650#define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0
57651#define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1
57652#define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2
57653#define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3
57654#define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4
57655#define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5
57656#define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6
57657#define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127
57658#define DRFLAC_PICTURE_TYPE_OTHER 0
57659#define DRFLAC_PICTURE_TYPE_FILE_ICON 1
57660#define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2
57661#define DRFLAC_PICTURE_TYPE_COVER_FRONT 3
57662#define DRFLAC_PICTURE_TYPE_COVER_BACK 4
57663#define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5
57664#define DRFLAC_PICTURE_TYPE_MEDIA 6
57665#define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7
57666#define DRFLAC_PICTURE_TYPE_ARTIST 8
57667#define DRFLAC_PICTURE_TYPE_CONDUCTOR 9
57668#define DRFLAC_PICTURE_TYPE_BAND 10
57669#define DRFLAC_PICTURE_TYPE_COMPOSER 11
57670#define DRFLAC_PICTURE_TYPE_LYRICIST 12
57671#define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13
57672#define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14
57673#define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15
57674#define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16
57675#define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17
57676#define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18
57677#define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19
57678#define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20
57679typedef enum
57680{
57685typedef enum
57686{
57690#pragma pack(2)
57691typedef struct
57692{
57693 drflac_uint64 firstPCMFrame;
57694 drflac_uint64 flacFrameOffset;
57695 drflac_uint16 pcmFrameCount;
57697#pragma pack()
57698typedef struct
57699{
57700 drflac_uint16 minBlockSizeInPCMFrames;
57701 drflac_uint16 maxBlockSizeInPCMFrames;
57702 drflac_uint32 minFrameSizeInPCMFrames;
57703 drflac_uint32 maxFrameSizeInPCMFrames;
57704 drflac_uint32 sampleRate;
57705 drflac_uint8 channels;
57706 drflac_uint8 bitsPerSample;
57707 drflac_uint64 totalPCMFrameCount;
57708 drflac_uint8 md5[16];
57710typedef struct
57711{
57712 drflac_uint32 type;
57713 const void* pRawData;
57714 drflac_uint32 rawDataSize;
57715 union
57716 {
57717 drflac_streaminfo streaminfo;
57718 struct
57719 {
57720 int unused;
57721 } padding;
57722 struct
57723 {
57725 const void* pData;
57726 drflac_uint32 dataSize;
57727 } application;
57728 struct
57729 {
57730 drflac_uint32 seekpointCount;
57731 const drflac_seekpoint* pSeekpoints;
57732 } seektable;
57733 struct
57734 {
57735 drflac_uint32 vendorLength;
57736 const char* vendor;
57737 drflac_uint32 commentCount;
57738 const void* pComments;
57739 } vorbis_comment;
57740 struct
57741 {
57742 char catalog[128];
57743 drflac_uint64 leadInSampleCount;
57744 drflac_bool32 isCD;
57745 drflac_uint8 trackCount;
57746 const void* pTrackData;
57747 } cuesheet;
57748 struct
57749 {
57750 drflac_uint32 type;
57751 drflac_uint32 mimeLength;
57752 const char* mime;
57753 drflac_uint32 descriptionLength;
57754 const char* description;
57755 drflac_uint32 width;
57756 drflac_uint32 height;
57757 drflac_uint32 colorDepth;
57758 drflac_uint32 indexColorCount;
57759 drflac_uint32 pictureDataSize;
57760 const drflac_uint8* pPictureData;
57761 } picture;
57762 } data;
57764typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
57765typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin);
57766typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata);
57767typedef struct
57768{
57769 void* pUserData;
57770 void* (* onMalloc)(size_t sz, void* pUserData);
57771 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
57772 void (* onFree)(void* p, void* pUserData);
57774typedef struct
57775{
57776 const drflac_uint8* data;
57777 size_t dataSize;
57778 size_t currentReadPos;
57780typedef struct
57781{
57782 drflac_read_proc onRead;
57783 drflac_seek_proc onSeek;
57784 void* pUserData;
57785 size_t unalignedByteCount;
57786 drflac_cache_t unalignedCache;
57787 drflac_uint32 nextL2Line;
57788 drflac_uint32 consumedBits;
57789 drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)];
57790 drflac_cache_t cache;
57791 drflac_uint16 crc16;
57792 drflac_cache_t crc16Cache;
57793 drflac_uint32 crc16CacheIgnoredBytes;
57794} drflac_bs;
57795typedef struct
57796{
57797 drflac_uint8 subframeType;
57798 drflac_uint8 wastedBitsPerSample;
57799 drflac_uint8 lpcOrder;
57800 drflac_int32* pSamplesS32;
57802typedef struct
57803{
57804 drflac_uint64 pcmFrameNumber;
57805 drflac_uint32 flacFrameNumber;
57806 drflac_uint32 sampleRate;
57807 drflac_uint16 blockSizeInPCMFrames;
57808 drflac_uint8 channelAssignment;
57809 drflac_uint8 bitsPerSample;
57810 drflac_uint8 crc8;
57812typedef struct
57813{
57814 drflac_frame_header header;
57815 drflac_uint32 pcmFramesRemaining;
57816 drflac_subframe subframes[8];
57817} drflac_frame;
57818typedef struct
57819{
57820 drflac_meta_proc onMeta;
57821 void* pUserDataMD;
57822 drflac_allocation_callbacks allocationCallbacks;
57823 drflac_uint32 sampleRate;
57824 drflac_uint8 channels;
57825 drflac_uint8 bitsPerSample;
57826 drflac_uint16 maxBlockSizeInPCMFrames;
57827 drflac_uint64 totalPCMFrameCount;
57828 drflac_container container;
57829 drflac_uint32 seekpointCount;
57830 drflac_frame currentFLACFrame;
57831 drflac_uint64 currentPCMFrame;
57832 drflac_uint64 firstFLACFramePosInBytes;
57833 drflac__memory_stream memoryStream;
57834 drflac_int32* pDecodedSamples;
57835 drflac_seekpoint* pSeekpoints;
57836 void* _oggbs;
57837 drflac_bool32 _noSeekTableSeek : 1;
57838 drflac_bool32 _noBinarySearchSeek : 1;
57839 drflac_bool32 _noBruteForceSeek : 1;
57840 drflac_bs bs;
57841 drflac_uint8 pExtraData[1];
57842} drflac;
57843DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
57844DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
57845DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
57846DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
57847DRFLAC_API void drflac_close(drflac* pFlac);
57850DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut);
57852#ifndef DR_FLAC_NO_STDIO
57853DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks);
57854DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks);
57855DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
57856DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
57857#endif
57858DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks);
57859DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
57860DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
57861DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
57862DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
57863#ifndef DR_FLAC_NO_STDIO
57864DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
57865DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
57866DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
57867#endif
57868DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
57869DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
57870DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
57871DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks);
57872typedef struct
57873{
57874 drflac_uint32 countRemaining;
57875 const char* pRunningData;
57877DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments);
57879typedef struct
57880{
57881 drflac_uint32 countRemaining;
57882 const char* pRunningData;
57884#pragma pack(4)
57885typedef struct
57886{
57887 drflac_uint64 offset;
57888 drflac_uint8 index;
57889 drflac_uint8 reserved[3];
57891#pragma pack()
57892typedef struct
57893{
57894 drflac_uint64 offset;
57895 drflac_uint8 trackNumber;
57896 char ISRC[12];
57897 drflac_bool8 isAudio;
57898 drflac_bool8 preEmphasis;
57899 drflac_uint8 indexCount;
57900 const drflac_cuesheet_track_index* pIndexPoints;
57904#ifdef __cplusplus
57905}
57906#endif
57907#endif
57908/* dr_flac_h end */
57909#endif /* MA_NO_FLAC */
57910
57911#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
57912/* dr_mp3_h begin */
57913#ifndef dr_mp3_h
57914#define dr_mp3_h
57915#ifdef __cplusplus
57916extern "C" {
57917#endif
57918#define DRMP3_STRINGIFY(x) #x
57919#define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x)
57920#define DRMP3_VERSION_MAJOR 0
57921#define DRMP3_VERSION_MINOR 6
57922#define DRMP3_VERSION_REVISION 32
57923#define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION)
57924#include <stddef.h>
57925typedef signed char drmp3_int8;
57926typedef unsigned char drmp3_uint8;
57927typedef signed short drmp3_int16;
57928typedef unsigned short drmp3_uint16;
57929typedef signed int drmp3_int32;
57930typedef unsigned int drmp3_uint32;
57931#if defined(_MSC_VER) && !defined(__clang__)
57932 typedef signed __int64 drmp3_int64;
57933 typedef unsigned __int64 drmp3_uint64;
57934#else
57935 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
57936 #pragma GCC diagnostic push
57937 #pragma GCC diagnostic ignored "-Wlong-long"
57938 #if defined(__clang__)
57939 #pragma GCC diagnostic ignored "-Wc++11-long-long"
57940 #endif
57941 #endif
57942 typedef signed long long drmp3_int64;
57943 typedef unsigned long long drmp3_uint64;
57944 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
57945 #pragma GCC diagnostic pop
57946 #endif
57947#endif
57948#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__)
57950#else
57952#endif
57953typedef drmp3_uint8 drmp3_bool8;
57955#define DRMP3_TRUE 1
57956#define DRMP3_FALSE 0
57957#if !defined(DRMP3_API)
57958 #if defined(DRMP3_DLL)
57959 #if defined(_WIN32)
57960 #define DRMP3_DLL_IMPORT __declspec(dllimport)
57961 #define DRMP3_DLL_EXPORT __declspec(dllexport)
57962 #define DRMP3_DLL_PRIVATE static
57963 #else
57964 #if defined(__GNUC__) && __GNUC__ >= 4
57965 #define DRMP3_DLL_IMPORT __attribute__((visibility("default")))
57966 #define DRMP3_DLL_EXPORT __attribute__((visibility("default")))
57967 #define DRMP3_DLL_PRIVATE __attribute__((visibility("hidden")))
57968 #else
57969 #define DRMP3_DLL_IMPORT
57970 #define DRMP3_DLL_EXPORT
57971 #define DRMP3_DLL_PRIVATE static
57972 #endif
57973 #endif
57974 #if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION)
57975 #define DRMP3_API DRMP3_DLL_EXPORT
57976 #else
57977 #define DRMP3_API DRMP3_DLL_IMPORT
57978 #endif
57979 #define DRMP3_PRIVATE DRMP3_DLL_PRIVATE
57980 #else
57981 #define DRMP3_API extern
57982 #define DRMP3_PRIVATE static
57983 #endif
57984#endif
57986#define DRMP3_SUCCESS 0
57987#define DRMP3_ERROR -1
57988#define DRMP3_INVALID_ARGS -2
57989#define DRMP3_INVALID_OPERATION -3
57990#define DRMP3_OUT_OF_MEMORY -4
57991#define DRMP3_OUT_OF_RANGE -5
57992#define DRMP3_ACCESS_DENIED -6
57993#define DRMP3_DOES_NOT_EXIST -7
57994#define DRMP3_ALREADY_EXISTS -8
57995#define DRMP3_TOO_MANY_OPEN_FILES -9
57996#define DRMP3_INVALID_FILE -10
57997#define DRMP3_TOO_BIG -11
57998#define DRMP3_PATH_TOO_LONG -12
57999#define DRMP3_NAME_TOO_LONG -13
58000#define DRMP3_NOT_DIRECTORY -14
58001#define DRMP3_IS_DIRECTORY -15
58002#define DRMP3_DIRECTORY_NOT_EMPTY -16
58003#define DRMP3_END_OF_FILE -17
58004#define DRMP3_NO_SPACE -18
58005#define DRMP3_BUSY -19
58006#define DRMP3_IO_ERROR -20
58007#define DRMP3_INTERRUPT -21
58008#define DRMP3_UNAVAILABLE -22
58009#define DRMP3_ALREADY_IN_USE -23
58010#define DRMP3_BAD_ADDRESS -24
58011#define DRMP3_BAD_SEEK -25
58012#define DRMP3_BAD_PIPE -26
58013#define DRMP3_DEADLOCK -27
58014#define DRMP3_TOO_MANY_LINKS -28
58015#define DRMP3_NOT_IMPLEMENTED -29
58016#define DRMP3_NO_MESSAGE -30
58017#define DRMP3_BAD_MESSAGE -31
58018#define DRMP3_NO_DATA_AVAILABLE -32
58019#define DRMP3_INVALID_DATA -33
58020#define DRMP3_TIMEOUT -34
58021#define DRMP3_NO_NETWORK -35
58022#define DRMP3_NOT_UNIQUE -36
58023#define DRMP3_NOT_SOCKET -37
58024#define DRMP3_NO_ADDRESS -38
58025#define DRMP3_BAD_PROTOCOL -39
58026#define DRMP3_PROTOCOL_UNAVAILABLE -40
58027#define DRMP3_PROTOCOL_NOT_SUPPORTED -41
58028#define DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED -42
58029#define DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED -43
58030#define DRMP3_SOCKET_NOT_SUPPORTED -44
58031#define DRMP3_CONNECTION_RESET -45
58032#define DRMP3_ALREADY_CONNECTED -46
58033#define DRMP3_NOT_CONNECTED -47
58034#define DRMP3_CONNECTION_REFUSED -48
58035#define DRMP3_NO_HOST -49
58036#define DRMP3_IN_PROGRESS -50
58037#define DRMP3_CANCELLED -51
58038#define DRMP3_MEMORY_ALREADY_MAPPED -52
58039#define DRMP3_AT_END -53
58040#define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152
58041#define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2)
58042#ifdef _MSC_VER
58043 #define DRMP3_INLINE __forceinline
58044#elif defined(__GNUC__)
58045 #if defined(__STRICT_ANSI__)
58046 #define DRMP3_INLINE __inline__ __attribute__((always_inline))
58047 #else
58048 #define DRMP3_INLINE inline __attribute__((always_inline))
58049 #endif
58050#elif defined(__WATCOMC__)
58051 #define DRMP3_INLINE __inline
58052#else
58053 #define DRMP3_INLINE
58054#endif
58055DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision);
58056DRMP3_API const char* drmp3_version_string(void);
58057typedef struct
58058{
58059 int frame_bytes, channels, hz, layer, bitrate_kbps;
58061typedef struct
58062{
58063 float mdct_overlap[2][9*32], qmf_state[15*2*32];
58064 int reserv, free_format_bytes;
58065 drmp3_uint8 header[4], reserv_buf[511];
58066} drmp3dec;
58068DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info);
58069DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples);
58070typedef enum
58071{
58075typedef struct
58076{
58077 drmp3_uint64 seekPosInBytes;
58078 drmp3_uint64 pcmFrameIndex;
58079 drmp3_uint16 mp3FramesToDiscard;
58080 drmp3_uint16 pcmFramesToDiscard;
58082typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
58083typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin);
58084typedef struct
58085{
58086 void* pUserData;
58087 void* (* onMalloc)(size_t sz, void* pUserData);
58088 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
58089 void (* onFree)(void* p, void* pUserData);
58091typedef struct
58092{
58093 drmp3_uint32 channels;
58094 drmp3_uint32 sampleRate;
58095} drmp3_config;
58096typedef struct
58097{
58098 drmp3dec decoder;
58099 drmp3dec_frame_info frameInfo;
58100 drmp3_uint32 channels;
58101 drmp3_uint32 sampleRate;
58102 drmp3_read_proc onRead;
58103 drmp3_seek_proc onSeek;
58104 void* pUserData;
58105 drmp3_allocation_callbacks allocationCallbacks;
58106 drmp3_uint32 mp3FrameChannels;
58107 drmp3_uint32 mp3FrameSampleRate;
58108 drmp3_uint32 pcmFramesConsumedInMP3Frame;
58109 drmp3_uint32 pcmFramesRemainingInMP3Frame;
58110 drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME];
58111 drmp3_uint64 currentPCMFrame;
58112 drmp3_uint64 streamCursor;
58113 drmp3_seek_point* pSeekPoints;
58114 drmp3_uint32 seekPointCount;
58115 size_t dataSize;
58116 size_t dataCapacity;
58117 size_t dataConsumed;
58118 drmp3_uint8* pData;
58119 drmp3_bool32 atEnd : 1;
58120 struct
58121 {
58122 const drmp3_uint8* pData;
58123 size_t dataSize;
58124 size_t currentReadPos;
58125 } memory;
58126} drmp3;
58127DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks);
58128DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks);
58129#ifndef DR_MP3_NO_STDIO
58130DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks);
58131DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks);
58132#endif
58133DRMP3_API void drmp3_uninit(drmp3* pMP3);
58134DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut);
58142DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
58143DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
58144DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
58145DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
58146#ifndef DR_MP3_NO_STDIO
58147DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
58148DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
58149#endif
58150DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks);
58151DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks);
58152#ifdef __cplusplus
58153}
58154#endif
58155#endif
58156/* dr_mp3_h end */
58157#endif /* MA_NO_MP3 */
58158
58159
58160
58165#ifndef MA_NO_DECODING
58166
58167static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
58168{
58169 MA_ASSERT(pDecoder != NULL);
58170
58171 return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead, pBytesRead);
58172}
58173
58174static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin)
58175{
58176 MA_ASSERT(pDecoder != NULL);
58177
58178 return pDecoder->onSeek(pDecoder, byteOffset, origin);
58179}
58180
58181static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor)
58182{
58183 MA_ASSERT(pDecoder != NULL);
58184
58185 if (pDecoder->onTell == NULL) {
58186 return MA_NOT_IMPLEMENTED;
58187 }
58188
58189 return pDecoder->onTell(pDecoder, pCursor);
58190}
58191
58192
58194{
58196
58197 MA_ZERO_OBJECT(&config);
58198 config.preferredFormat = preferredFormat;
58199 config.seekPointCount = seekPointCount;
58200
58201 return config;
58202}
58203
58204
58205MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
58206{
58207 ma_decoder_config config;
58208 MA_ZERO_OBJECT(&config);
58209 config.format = outputFormat;
58210 config.channels = outputChannels;
58211 config.sampleRate = outputSampleRate;
58212 config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate doesn't matter here. */
58214
58215 /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */
58216
58217 return config;
58218}
58219
58221{
58223}
58224
58225MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig)
58226{
58227 ma_decoder_config config;
58228 if (pConfig != NULL) {
58229 config = *pConfig;
58230 } else {
58231 MA_ZERO_OBJECT(&config);
58232 }
58233
58234 return config;
58235}
58236
58237static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig)
58238{
58239 ma_result result;
58240 ma_data_converter_config converterConfig;
58241 ma_format internalFormat;
58242 ma_uint32 internalChannels;
58243 ma_uint32 internalSampleRate;
58244 ma_channel internalChannelMap[MA_MAX_CHANNELS];
58245
58246 MA_ASSERT(pDecoder != NULL);
58247 MA_ASSERT(pConfig != NULL);
58248
58249 result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap));
58250 if (result != MA_SUCCESS) {
58251 return result; /* Failed to retrieve the internal data format. */
58252 }
58253
58254
58255 /* Make sure we're not asking for too many channels. */
58256 if (pConfig->channels > MA_MAX_CHANNELS) {
58257 return MA_INVALID_ARGS;
58258 }
58259
58260 /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */
58261 if (internalChannels > MA_MAX_CHANNELS) {
58262 return MA_INVALID_ARGS;
58263 }
58264
58265
58266 /* Output format. */
58267 if (pConfig->format == ma_format_unknown) {
58268 pDecoder->outputFormat = internalFormat;
58269 } else {
58270 pDecoder->outputFormat = pConfig->format;
58271 }
58272
58273 if (pConfig->channels == 0) {
58274 pDecoder->outputChannels = internalChannels;
58275 } else {
58276 pDecoder->outputChannels = pConfig->channels;
58277 }
58278
58279 if (pConfig->sampleRate == 0) {
58280 pDecoder->outputSampleRate = internalSampleRate;
58281 } else {
58282 pDecoder->outputSampleRate = pConfig->sampleRate;
58283 }
58284
58285 converterConfig = ma_data_converter_config_init(
58286 internalFormat, pDecoder->outputFormat,
58287 internalChannels, pDecoder->outputChannels,
58288 internalSampleRate, pDecoder->outputSampleRate
58289 );
58290 converterConfig.pChannelMapIn = internalChannelMap;
58291 converterConfig.pChannelMapOut = pConfig->pChannelMap;
58292 converterConfig.channelMixMode = pConfig->channelMixMode;
58293 converterConfig.ditherMode = pConfig->ditherMode;
58294 converterConfig.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */
58295 converterConfig.resampling = pConfig->resampling;
58296
58297 result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter);
58298 if (result != MA_SUCCESS) {
58299 return result;
58300 }
58301
58302 /*
58303 Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll
58304 need this if the data converter does not support calculation of the required input frame count. To
58305 determine support for this we'll just run a test.
58306 */
58307 {
58308 ma_uint64 unused;
58309
58310 result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused);
58311 if (result != MA_SUCCESS) {
58312 /*
58313 We were unable to calculate the required input frame count which means we'll need to use
58314 a heap-allocated cache.
58315 */
58316 ma_uint64 inputCacheCapSizeInBytes;
58317
58318 pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels);
58319
58320 /* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */
58321 inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels);
58322 if (inputCacheCapSizeInBytes > MA_SIZE_MAX) {
58324 return MA_OUT_OF_MEMORY;
58325 }
58326
58327 pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks); /* Safe cast to size_t. */
58328 if (pDecoder->pInputCache == NULL) {
58330 return MA_OUT_OF_MEMORY;
58331 }
58332 }
58333 }
58334
58335 return MA_SUCCESS;
58336}
58337
58338
58339
58340static ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
58341{
58342 ma_decoder* pDecoder = (ma_decoder*)pUserData;
58343 MA_ASSERT(pDecoder != NULL);
58344
58345 return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead);
58346}
58347
58348static ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin)
58349{
58350 ma_decoder* pDecoder = (ma_decoder*)pUserData;
58351 MA_ASSERT(pDecoder != NULL);
58352
58353 return ma_decoder_seek_bytes(pDecoder, offset, origin);
58354}
58355
58356static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor)
58357{
58358 ma_decoder* pDecoder = (ma_decoder*)pUserData;
58359 MA_ASSERT(pDecoder != NULL);
58360
58361 return ma_decoder_tell_bytes(pDecoder, pCursor);
58362}
58363
58364
58365static ma_result ma_decoder_init_from_vtable(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
58366{
58367 ma_result result;
58368 ma_decoding_backend_config backendConfig;
58369 ma_data_source* pBackend;
58370
58371 MA_ASSERT(pVTable != NULL);
58372 MA_ASSERT(pConfig != NULL);
58373 MA_ASSERT(pDecoder != NULL);
58374
58375 if (pVTable->onInit == NULL) {
58376 return MA_NOT_IMPLEMENTED;
58377 }
58378
58379 backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);
58380
58381 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);
58382 if (result != MA_SUCCESS) {
58383 return result; /* Failed to initialize the backend from this vtable. */
58384 }
58385
58386 /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */
58387 pDecoder->pBackend = pBackend;
58388 pDecoder->pBackendVTable = pVTable;
58389 pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;
58390
58391 return MA_SUCCESS;
58392}
58393
58394
58395
58396static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
58397{
58398 ma_result result = MA_NO_BACKEND;
58399 size_t ivtable;
58400
58401 MA_ASSERT(pConfig != NULL);
58402 MA_ASSERT(pDecoder != NULL);
58403
58404 if (pConfig->ppCustomBackendVTables == NULL) {
58405 return MA_NO_BACKEND;
58406 }
58407
58408 /* The order each backend is listed is what defines the priority. */
58409 for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {
58410 const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];
58411 if (pVTable != NULL && pVTable->onInit != NULL) {
58412 result = ma_decoder_init_from_vtable(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder);
58413 if (result == MA_SUCCESS) {
58414 return MA_SUCCESS;
58415 } else {
58416 /* 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. */
58417 result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start);
58418 if (result != MA_SUCCESS) {
58419 return result; /* Failed to seek back to the start. */
58420 }
58421 }
58422 } else {
58423 /* No vtable. */
58424 }
58425 }
58426
58427 /* Getting here means we couldn't find a backend. */
58428 return MA_NO_BACKEND;
58429}
58430
58431
58432/* WAV */
58433#ifdef dr_wav_h
58434#define MA_HAS_WAV
58435
58436typedef struct
58437{
58439 ma_read_proc onRead;
58440 ma_seek_proc onSeek;
58441 ma_tell_proc onTell;
58442 void* pReadSeekTellUserData;
58443 ma_format format; /* Can be f32, s16 or s32. */
58444#if !defined(MA_NO_WAV)
58445 drwav dr;
58446#endif
58447} ma_wav;
58448
58449MA_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);
58450MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav);
58451MA_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);
58452MA_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);
58453MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks);
58454MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
58455MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex);
58456MA_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);
58457MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor);
58458MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength);
58459
58460
58461static ma_result ma_wav_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
58462{
58463 return ma_wav_read_pcm_frames((ma_wav*)pDataSource, pFramesOut, frameCount, pFramesRead);
58464}
58465
58466static ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
58467{
58468 return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex);
58469}
58470
58471static 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)
58472{
58473 return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
58474}
58475
58476static ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
58477{
58478 return ma_wav_get_cursor_in_pcm_frames((ma_wav*)pDataSource, pCursor);
58479}
58480
58481static ma_result ma_wav_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
58482{
58483 return ma_wav_get_length_in_pcm_frames((ma_wav*)pDataSource, pLength);
58484}
58485
58486static ma_data_source_vtable g_ma_wav_ds_vtable =
58487{
58488 ma_wav_ds_read,
58489 ma_wav_ds_seek,
58490 ma_wav_ds_get_data_format,
58491 ma_wav_ds_get_cursor,
58492 ma_wav_ds_get_length,
58493 NULL, /* onSetLooping */
58494 0
58495};
58496
58497
58498#if !defined(MA_NO_WAV)
58499static drwav_allocation_callbacks drwav_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks)
58500{
58502
58503 if (pAllocationCallbacks != NULL) {
58504 callbacks.onMalloc = pAllocationCallbacks->onMalloc;
58505 callbacks.onRealloc = pAllocationCallbacks->onRealloc;
58506 callbacks.onFree = pAllocationCallbacks->onFree;
58507 callbacks.pUserData = pAllocationCallbacks->pUserData;
58508 } else {
58509 callbacks.onMalloc = ma__malloc_default;
58510 callbacks.onRealloc = ma__realloc_default;
58511 callbacks.onFree = ma__free_default;
58512 callbacks.pUserData = NULL;
58513 }
58514
58515 return callbacks;
58516}
58517
58518static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
58519{
58520 ma_wav* pWav = (ma_wav*)pUserData;
58521 ma_result result;
58522 size_t bytesRead;
58523
58524 MA_ASSERT(pWav != NULL);
58525
58526 result = pWav->onRead(pWav->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
58527 (void)result;
58528
58529 return bytesRead;
58530}
58531
58532static drwav_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, drwav_seek_origin origin)
58533{
58534 ma_wav* pWav = (ma_wav*)pUserData;
58535 ma_result result;
58536 ma_seek_origin maSeekOrigin;
58537
58538 MA_ASSERT(pWav != NULL);
58539
58540 maSeekOrigin = ma_seek_origin_start;
58541 if (origin == drwav_seek_origin_current) {
58542 maSeekOrigin = ma_seek_origin_current;
58543 }
58544
58545 result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin);
58546 if (result != MA_SUCCESS) {
58547 return MA_FALSE;
58548 }
58549
58550 return MA_TRUE;
58551}
58552#endif
58553
58554static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav)
58555{
58556 ma_result result;
58557 ma_data_source_config dataSourceConfig;
58558
58559 if (pWav == NULL) {
58560 return MA_INVALID_ARGS;
58561 }
58562
58563 MA_ZERO_OBJECT(pWav);
58564 pWav->format = ma_format_unknown; /* Use closest match to source file by default. */
58565
58566 if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) {
58567 pWav->format = pConfig->preferredFormat;
58568 } else {
58569 /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
58570 }
58571
58572 dataSourceConfig = ma_data_source_config_init();
58573 dataSourceConfig.vtable = &g_ma_wav_ds_vtable;
58574
58575 result = ma_data_source_init(&dataSourceConfig, &pWav->ds);
58576 if (result != MA_SUCCESS) {
58577 return result; /* Failed to initialize the base data source. */
58578 }
58579
58580 return MA_SUCCESS;
58581}
58582
58583MA_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)
58584{
58585 ma_result result;
58586
58587 result = ma_wav_init_internal(pConfig, pWav);
58588 if (result != MA_SUCCESS) {
58589 return result;
58590 }
58591
58592 if (onRead == NULL || onSeek == NULL) {
58593 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
58594 }
58595
58596 pWav->onRead = onRead;
58597 pWav->onSeek = onSeek;
58598 pWav->onTell = onTell;
58599 pWav->pReadSeekTellUserData = pReadSeekTellUserData;
58600
58601 #if !defined(MA_NO_WAV)
58602 {
58603 drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
58604 drwav_bool32 wavResult;
58605
58606 wavResult = drwav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, &wavAllocationCallbacks);
58607 if (wavResult != MA_TRUE) {
58608 return MA_INVALID_FILE;
58609 }
58610
58611 /*
58612 If an explicit format was not specified, try picking the closest match based on the internal
58613 format. The format needs to be supported by miniaudio.
58614 */
58615 if (pWav->format == ma_format_unknown) {
58616 switch (pWav->dr.translatedFormatTag)
58617 {
58618 case DR_WAVE_FORMAT_PCM:
58619 {
58620 if (pWav->dr.bitsPerSample == 8) {
58621 pWav->format = ma_format_u8;
58622 } else if (pWav->dr.bitsPerSample == 16) {
58623 pWav->format = ma_format_s16;
58624 } else if (pWav->dr.bitsPerSample == 24) {
58625 pWav->format = ma_format_s24;
58626 } else if (pWav->dr.bitsPerSample == 32) {
58627 pWav->format = ma_format_s32;
58628 }
58629 } break;
58630
58632 {
58633 if (pWav->dr.bitsPerSample == 32) {
58634 pWav->format = ma_format_f32;
58635 }
58636 } break;
58637
58638 default: break;
58639 }
58640
58641 /* Fall back to f32 if we couldn't find anything. */
58642 if (pWav->format == ma_format_unknown) {
58643 pWav->format = ma_format_f32;
58644 }
58645 }
58646
58647 return MA_SUCCESS;
58648 }
58649 #else
58650 {
58651 /* wav is disabled. */
58652 (void)pAllocationCallbacks;
58653 return MA_NOT_IMPLEMENTED;
58654 }
58655 #endif
58656}
58657
58658MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav)
58659{
58660 ma_result result;
58661
58662 result = ma_wav_init_internal(pConfig, pWav);
58663 if (result != MA_SUCCESS) {
58664 return result;
58665 }
58666
58667 #if !defined(MA_NO_WAV)
58668 {
58669 drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
58670 drwav_bool32 wavResult;
58671
58672 wavResult = drwav_init_file(&pWav->dr, pFilePath, &wavAllocationCallbacks);
58673 if (wavResult != MA_TRUE) {
58674 return MA_INVALID_FILE;
58675 }
58676
58677 return MA_SUCCESS;
58678 }
58679 #else
58680 {
58681 /* wav is disabled. */
58682 (void)pFilePath;
58683 (void)pAllocationCallbacks;
58684 return MA_NOT_IMPLEMENTED;
58685 }
58686 #endif
58687}
58688
58689MA_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)
58690{
58691 ma_result result;
58692
58693 result = ma_wav_init_internal(pConfig, pWav);
58694 if (result != MA_SUCCESS) {
58695 return result;
58696 }
58697
58698 #if !defined(MA_NO_WAV)
58699 {
58700 drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
58701 drwav_bool32 wavResult;
58702
58703 wavResult = drwav_init_file_w(&pWav->dr, pFilePath, &wavAllocationCallbacks);
58704 if (wavResult != MA_TRUE) {
58705 return MA_INVALID_FILE;
58706 }
58707
58708 return MA_SUCCESS;
58709 }
58710 #else
58711 {
58712 /* wav is disabled. */
58713 (void)pFilePath;
58714 (void)pAllocationCallbacks;
58715 return MA_NOT_IMPLEMENTED;
58716 }
58717 #endif
58718}
58719
58720MA_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)
58721{
58722 ma_result result;
58723
58724 result = ma_wav_init_internal(pConfig, pWav);
58725 if (result != MA_SUCCESS) {
58726 return result;
58727 }
58728
58729 #if !defined(MA_NO_WAV)
58730 {
58731 drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
58732 drwav_bool32 wavResult;
58733
58734 wavResult = drwav_init_memory(&pWav->dr, pData, dataSize, &wavAllocationCallbacks);
58735 if (wavResult != MA_TRUE) {
58736 return MA_INVALID_FILE;
58737 }
58738
58739 return MA_SUCCESS;
58740 }
58741 #else
58742 {
58743 /* wav is disabled. */
58744 (void)pData;
58745 (void)dataSize;
58746 (void)pAllocationCallbacks;
58747 return MA_NOT_IMPLEMENTED;
58748 }
58749 #endif
58750}
58751
58752MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks)
58753{
58754 if (pWav == NULL) {
58755 return;
58756 }
58757
58758 (void)pAllocationCallbacks;
58759
58760 #if !defined(MA_NO_WAV)
58761 {
58762 drwav_uninit(&pWav->dr);
58763 }
58764 #else
58765 {
58766 /* wav is disabled. Should never hit this since initialization would have failed. */
58767 MA_ASSERT(MA_FALSE);
58768 }
58769 #endif
58770
58771 ma_data_source_uninit(&pWav->ds);
58772}
58773
58774MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
58775{
58776 if (pFramesRead != NULL) {
58777 *pFramesRead = 0;
58778 }
58779
58780 if (frameCount == 0) {
58781 return MA_INVALID_ARGS;
58782 }
58783
58784 if (pWav == NULL) {
58785 return MA_INVALID_ARGS;
58786 }
58787
58788 #if !defined(MA_NO_WAV)
58789 {
58790 /* We always use floating point format. */
58791 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
58792 ma_uint64 totalFramesRead = 0;
58793 ma_format format;
58794
58795 ma_wav_get_data_format(pWav, &format, NULL, NULL, NULL, 0);
58796
58797 switch (format)
58798 {
58799 case ma_format_f32:
58800 {
58801 totalFramesRead = drwav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut);
58802 } break;
58803
58804 case ma_format_s16:
58805 {
58806 totalFramesRead = drwav_read_pcm_frames_s16(&pWav->dr, frameCount, (drwav_int16*)pFramesOut);
58807 } break;
58808
58809 case ma_format_s32:
58810 {
58811 totalFramesRead = drwav_read_pcm_frames_s32(&pWav->dr, frameCount, (drwav_int32*)pFramesOut);
58812 } break;
58813
58814 /* Fallback to a raw read. */
58815 case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */
58816 default:
58817 {
58818 totalFramesRead = drwav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut);
58819 } break;
58820 }
58821
58822 /* In the future we'll update dr_wav to return MA_AT_END for us. */
58823 if (totalFramesRead == 0) {
58824 result = MA_AT_END;
58825 }
58826
58827 if (pFramesRead != NULL) {
58828 *pFramesRead = totalFramesRead;
58829 }
58830
58831 if (result == MA_SUCCESS && totalFramesRead == 0) {
58832 result = MA_AT_END;
58833 }
58834
58835 return result;
58836 }
58837 #else
58838 {
58839 /* wav is disabled. Should never hit this since initialization would have failed. */
58840 MA_ASSERT(MA_FALSE);
58841
58842 (void)pFramesOut;
58843 (void)frameCount;
58844 (void)pFramesRead;
58845
58846 return MA_NOT_IMPLEMENTED;
58847 }
58848 #endif
58849}
58850
58851MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex)
58852{
58853 if (pWav == NULL) {
58854 return MA_INVALID_ARGS;
58855 }
58856
58857 #if !defined(MA_NO_WAV)
58858 {
58859 drwav_bool32 wavResult;
58860
58861 wavResult = drwav_seek_to_pcm_frame(&pWav->dr, frameIndex);
58862 if (wavResult != DRWAV_TRUE) {
58863 return MA_ERROR;
58864 }
58865
58866 return MA_SUCCESS;
58867 }
58868 #else
58869 {
58870 /* wav is disabled. Should never hit this since initialization would have failed. */
58871 MA_ASSERT(MA_FALSE);
58872
58873 (void)frameIndex;
58874
58875 return MA_NOT_IMPLEMENTED;
58876 }
58877 #endif
58878}
58879
58880MA_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)
58881{
58882 /* Defaults for safety. */
58883 if (pFormat != NULL) {
58884 *pFormat = ma_format_unknown;
58885 }
58886 if (pChannels != NULL) {
58887 *pChannels = 0;
58888 }
58889 if (pSampleRate != NULL) {
58890 *pSampleRate = 0;
58891 }
58892 if (pChannelMap != NULL) {
58893 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
58894 }
58895
58896 if (pWav == NULL) {
58897 return MA_INVALID_OPERATION;
58898 }
58899
58900 if (pFormat != NULL) {
58901 *pFormat = pWav->format;
58902 }
58903
58904 #if !defined(MA_NO_WAV)
58905 {
58906 if (pChannels != NULL) {
58907 *pChannels = pWav->dr.channels;
58908 }
58909
58910 if (pSampleRate != NULL) {
58911 *pSampleRate = pWav->dr.sampleRate;
58912 }
58913
58914 if (pChannelMap != NULL) {
58915 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pWav->dr.channels);
58916 }
58917
58918 return MA_SUCCESS;
58919 }
58920 #else
58921 {
58922 /* wav is disabled. Should never hit this since initialization would have failed. */
58923 MA_ASSERT(MA_FALSE);
58924 return MA_NOT_IMPLEMENTED;
58925 }
58926 #endif
58927}
58928
58929MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor)
58930{
58931 if (pCursor == NULL) {
58932 return MA_INVALID_ARGS;
58933 }
58934
58935 *pCursor = 0; /* Safety. */
58936
58937 if (pWav == NULL) {
58938 return MA_INVALID_ARGS;
58939 }
58940
58941 #if !defined(MA_NO_WAV)
58942 {
58943 drwav_result wavResult = drwav_get_cursor_in_pcm_frames(&pWav->dr, pCursor);
58944 if (wavResult != DRWAV_SUCCESS) {
58945 return (ma_result)wavResult; /* dr_wav result codes map to miniaudio's. */
58946 }
58947
58948 return MA_SUCCESS;
58949 }
58950 #else
58951 {
58952 /* wav is disabled. Should never hit this since initialization would have failed. */
58953 MA_ASSERT(MA_FALSE);
58954 return MA_NOT_IMPLEMENTED;
58955 }
58956 #endif
58957}
58958
58959MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength)
58960{
58961 if (pLength == NULL) {
58962 return MA_INVALID_ARGS;
58963 }
58964
58965 *pLength = 0; /* Safety. */
58966
58967 if (pWav == NULL) {
58968 return MA_INVALID_ARGS;
58969 }
58970
58971 #if !defined(MA_NO_WAV)
58972 {
58973 drwav_result wavResult = drwav_get_length_in_pcm_frames(&pWav->dr, pLength);
58974 if (wavResult != DRWAV_SUCCESS) {
58975 return (ma_result)wavResult; /* dr_wav result codes map to miniaudio's. */
58976 }
58977
58978 return MA_SUCCESS;
58979 }
58980 #else
58981 {
58982 /* wav is disabled. Should never hit this since initialization would have failed. */
58983 MA_ASSERT(MA_FALSE);
58984 return MA_NOT_IMPLEMENTED;
58985 }
58986 #endif
58987}
58988
58989
58990static 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)
58991{
58992 ma_result result;
58993 ma_wav* pWav;
58994
58995 (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. */
58996
58997 /* For now we're just allocating the decoder backend on the heap. */
58998 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
58999 if (pWav == NULL) {
59000 return MA_OUT_OF_MEMORY;
59001 }
59002
59003 result = ma_wav_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pWav);
59004 if (result != MA_SUCCESS) {
59005 ma_free(pWav, pAllocationCallbacks);
59006 return result;
59007 }
59008
59009 *ppBackend = pWav;
59010
59011 return MA_SUCCESS;
59012}
59013
59014static 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)
59015{
59016 ma_result result;
59017 ma_wav* pWav;
59018
59019 (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. */
59020
59021 /* For now we're just allocating the decoder backend on the heap. */
59022 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
59023 if (pWav == NULL) {
59024 return MA_OUT_OF_MEMORY;
59025 }
59026
59027 result = ma_wav_init_file(pFilePath, pConfig, pAllocationCallbacks, pWav);
59028 if (result != MA_SUCCESS) {
59029 ma_free(pWav, pAllocationCallbacks);
59030 return result;
59031 }
59032
59033 *ppBackend = pWav;
59034
59035 return MA_SUCCESS;
59036}
59037
59038static 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)
59039{
59040 ma_result result;
59041 ma_wav* pWav;
59042
59043 (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. */
59044
59045 /* For now we're just allocating the decoder backend on the heap. */
59046 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
59047 if (pWav == NULL) {
59048 return MA_OUT_OF_MEMORY;
59049 }
59050
59051 result = ma_wav_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pWav);
59052 if (result != MA_SUCCESS) {
59053 ma_free(pWav, pAllocationCallbacks);
59054 return result;
59055 }
59056
59057 *ppBackend = pWav;
59058
59059 return MA_SUCCESS;
59060}
59061
59062static 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)
59063{
59064 ma_result result;
59065 ma_wav* pWav;
59066
59067 (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. */
59068
59069 /* For now we're just allocating the decoder backend on the heap. */
59070 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
59071 if (pWav == NULL) {
59072 return MA_OUT_OF_MEMORY;
59073 }
59074
59075 result = ma_wav_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pWav);
59076 if (result != MA_SUCCESS) {
59077 ma_free(pWav, pAllocationCallbacks);
59078 return result;
59079 }
59080
59081 *ppBackend = pWav;
59082
59083 return MA_SUCCESS;
59084}
59085
59086static void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
59087{
59088 ma_wav* pWav = (ma_wav*)pBackend;
59089
59090 (void)pUserData;
59091
59092 ma_wav_uninit(pWav, pAllocationCallbacks);
59093 ma_free(pWav, pAllocationCallbacks);
59094}
59095
59096static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav =
59097{
59098 ma_decoding_backend_init__wav,
59099 ma_decoding_backend_init_file__wav,
59100 ma_decoding_backend_init_file_w__wav,
59101 ma_decoding_backend_init_memory__wav,
59102 ma_decoding_backend_uninit__wav
59103};
59104
59105static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
59106{
59107 return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder);
59108}
59109#endif /* dr_wav_h */
59110
59111/* FLAC */
59112#ifdef dr_flac_h
59113#define MA_HAS_FLAC
59114
59115typedef struct
59116{
59118 ma_read_proc onRead;
59119 ma_seek_proc onSeek;
59120 ma_tell_proc onTell;
59121 void* pReadSeekTellUserData;
59122 ma_format format; /* Can be f32, s16 or s32. */
59123#if !defined(MA_NO_FLAC)
59124 drflac* dr;
59125#endif
59126} ma_flac;
59127
59128MA_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);
59129MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac);
59130MA_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);
59131MA_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);
59132MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks);
59133MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
59134MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex);
59135MA_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);
59136MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor);
59137MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength);
59138
59139
59140static ma_result ma_flac_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
59141{
59142 return ma_flac_read_pcm_frames((ma_flac*)pDataSource, pFramesOut, frameCount, pFramesRead);
59143}
59144
59145static ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
59146{
59147 return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex);
59148}
59149
59150static 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)
59151{
59152 return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
59153}
59154
59155static ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
59156{
59157 return ma_flac_get_cursor_in_pcm_frames((ma_flac*)pDataSource, pCursor);
59158}
59159
59160static ma_result ma_flac_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
59161{
59162 return ma_flac_get_length_in_pcm_frames((ma_flac*)pDataSource, pLength);
59163}
59164
59165static ma_data_source_vtable g_ma_flac_ds_vtable =
59166{
59167 ma_flac_ds_read,
59168 ma_flac_ds_seek,
59169 ma_flac_ds_get_data_format,
59170 ma_flac_ds_get_cursor,
59171 ma_flac_ds_get_length,
59172 NULL, /* onSetLooping */
59173 0
59174};
59175
59176
59177#if !defined(MA_NO_FLAC)
59178static drflac_allocation_callbacks drflac_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks)
59179{
59181
59182 if (pAllocationCallbacks != NULL) {
59183 callbacks.onMalloc = pAllocationCallbacks->onMalloc;
59184 callbacks.onRealloc = pAllocationCallbacks->onRealloc;
59185 callbacks.onFree = pAllocationCallbacks->onFree;
59186 callbacks.pUserData = pAllocationCallbacks->pUserData;
59187 } else {
59188 callbacks.onMalloc = ma__malloc_default;
59189 callbacks.onRealloc = ma__realloc_default;
59190 callbacks.onFree = ma__free_default;
59191 callbacks.pUserData = NULL;
59192 }
59193
59194 return callbacks;
59195}
59196
59197static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
59198{
59199 ma_flac* pFlac = (ma_flac*)pUserData;
59200 ma_result result;
59201 size_t bytesRead;
59202
59203 MA_ASSERT(pFlac != NULL);
59204
59205 result = pFlac->onRead(pFlac->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
59206 (void)result;
59207
59208 return bytesRead;
59209}
59210
59211static drflac_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, drflac_seek_origin origin)
59212{
59213 ma_flac* pFlac = (ma_flac*)pUserData;
59214 ma_result result;
59215 ma_seek_origin maSeekOrigin;
59216
59217 MA_ASSERT(pFlac != NULL);
59218
59219 maSeekOrigin = ma_seek_origin_start;
59220 if (origin == drflac_seek_origin_current) {
59221 maSeekOrigin = ma_seek_origin_current;
59222 }
59223
59224 result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin);
59225 if (result != MA_SUCCESS) {
59226 return MA_FALSE;
59227 }
59228
59229 return MA_TRUE;
59230}
59231#endif
59232
59233static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac)
59234{
59235 ma_result result;
59236 ma_data_source_config dataSourceConfig;
59237
59238 if (pFlac == NULL) {
59239 return MA_INVALID_ARGS;
59240 }
59241
59242 MA_ZERO_OBJECT(pFlac);
59243 pFlac->format = ma_format_f32; /* f32 by default. */
59244
59245 if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) {
59246 pFlac->format = pConfig->preferredFormat;
59247 } else {
59248 /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
59249 }
59250
59251 dataSourceConfig = ma_data_source_config_init();
59252 dataSourceConfig.vtable = &g_ma_flac_ds_vtable;
59253
59254 result = ma_data_source_init(&dataSourceConfig, &pFlac->ds);
59255 if (result != MA_SUCCESS) {
59256 return result; /* Failed to initialize the base data source. */
59257 }
59258
59259 return MA_SUCCESS;
59260}
59261
59262MA_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)
59263{
59264 ma_result result;
59265
59266 result = ma_flac_init_internal(pConfig, pFlac);
59267 if (result != MA_SUCCESS) {
59268 return result;
59269 }
59270
59271 if (onRead == NULL || onSeek == NULL) {
59272 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
59273 }
59274
59275 pFlac->onRead = onRead;
59276 pFlac->onSeek = onSeek;
59277 pFlac->onTell = onTell;
59278 pFlac->pReadSeekTellUserData = pReadSeekTellUserData;
59279
59280 #if !defined(MA_NO_FLAC)
59281 {
59282 drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
59283
59284 pFlac->dr = drflac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, &flacAllocationCallbacks);
59285 if (pFlac->dr == NULL) {
59286 return MA_INVALID_FILE;
59287 }
59288
59289 return MA_SUCCESS;
59290 }
59291 #else
59292 {
59293 /* flac is disabled. */
59294 (void)pAllocationCallbacks;
59295 return MA_NOT_IMPLEMENTED;
59296 }
59297 #endif
59298}
59299
59300MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac)
59301{
59302 ma_result result;
59303
59304 result = ma_flac_init_internal(pConfig, pFlac);
59305 if (result != MA_SUCCESS) {
59306 return result;
59307 }
59308
59309 #if !defined(MA_NO_FLAC)
59310 {
59311 drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
59312
59313 pFlac->dr = drflac_open_file(pFilePath, &flacAllocationCallbacks);
59314 if (pFlac->dr == NULL) {
59315 return MA_INVALID_FILE;
59316 }
59317
59318 return MA_SUCCESS;
59319 }
59320 #else
59321 {
59322 /* flac is disabled. */
59323 (void)pFilePath;
59324 (void)pAllocationCallbacks;
59325 return MA_NOT_IMPLEMENTED;
59326 }
59327 #endif
59328}
59329
59330MA_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)
59331{
59332 ma_result result;
59333
59334 result = ma_flac_init_internal(pConfig, pFlac);
59335 if (result != MA_SUCCESS) {
59336 return result;
59337 }
59338
59339 #if !defined(MA_NO_FLAC)
59340 {
59341 drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
59342
59343 pFlac->dr = drflac_open_file_w(pFilePath, &flacAllocationCallbacks);
59344 if (pFlac->dr == NULL) {
59345 return MA_INVALID_FILE;
59346 }
59347
59348 return MA_SUCCESS;
59349 }
59350 #else
59351 {
59352 /* flac is disabled. */
59353 (void)pFilePath;
59354 (void)pAllocationCallbacks;
59355 return MA_NOT_IMPLEMENTED;
59356 }
59357 #endif
59358}
59359
59360MA_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)
59361{
59362 ma_result result;
59363
59364 result = ma_flac_init_internal(pConfig, pFlac);
59365 if (result != MA_SUCCESS) {
59366 return result;
59367 }
59368
59369 #if !defined(MA_NO_FLAC)
59370 {
59371 drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
59372
59373 pFlac->dr = drflac_open_memory(pData, dataSize, &flacAllocationCallbacks);
59374 if (pFlac->dr == NULL) {
59375 return MA_INVALID_FILE;
59376 }
59377
59378 return MA_SUCCESS;
59379 }
59380 #else
59381 {
59382 /* flac is disabled. */
59383 (void)pData;
59384 (void)dataSize;
59385 (void)pAllocationCallbacks;
59386 return MA_NOT_IMPLEMENTED;
59387 }
59388 #endif
59389}
59390
59391MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks)
59392{
59393 if (pFlac == NULL) {
59394 return;
59395 }
59396
59397 (void)pAllocationCallbacks;
59398
59399 #if !defined(MA_NO_FLAC)
59400 {
59401 drflac_close(pFlac->dr);
59402 }
59403 #else
59404 {
59405 /* flac is disabled. Should never hit this since initialization would have failed. */
59406 MA_ASSERT(MA_FALSE);
59407 }
59408 #endif
59409
59410 ma_data_source_uninit(&pFlac->ds);
59411}
59412
59413MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
59414{
59415 if (pFramesRead != NULL) {
59416 *pFramesRead = 0;
59417 }
59418
59419 if (frameCount == 0) {
59420 return MA_INVALID_ARGS;
59421 }
59422
59423 if (pFlac == NULL) {
59424 return MA_INVALID_ARGS;
59425 }
59426
59427 #if !defined(MA_NO_FLAC)
59428 {
59429 /* We always use floating point format. */
59430 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
59431 ma_uint64 totalFramesRead = 0;
59432 ma_format format;
59433
59434 ma_flac_get_data_format(pFlac, &format, NULL, NULL, NULL, 0);
59435
59436 switch (format)
59437 {
59438 case ma_format_f32:
59439 {
59440 totalFramesRead = drflac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut);
59441 } break;
59442
59443 case ma_format_s16:
59444 {
59445 totalFramesRead = drflac_read_pcm_frames_s16(pFlac->dr, frameCount, (drflac_int16*)pFramesOut);
59446 } break;
59447
59448 case ma_format_s32:
59449 {
59450 totalFramesRead = drflac_read_pcm_frames_s32(pFlac->dr, frameCount, (drflac_int32*)pFramesOut);
59451 } break;
59452
59453 case ma_format_u8:
59454 case ma_format_s24:
59455 case ma_format_unknown:
59456 default:
59457 {
59458 return MA_INVALID_OPERATION;
59459 };
59460 }
59461
59462 /* In the future we'll update dr_flac to return MA_AT_END for us. */
59463 if (totalFramesRead == 0) {
59464 result = MA_AT_END;
59465 }
59466
59467 if (pFramesRead != NULL) {
59468 *pFramesRead = totalFramesRead;
59469 }
59470
59471 if (result == MA_SUCCESS && totalFramesRead == 0) {
59472 result = MA_AT_END;
59473 }
59474
59475 return result;
59476 }
59477 #else
59478 {
59479 /* flac is disabled. Should never hit this since initialization would have failed. */
59480 MA_ASSERT(MA_FALSE);
59481
59482 (void)pFramesOut;
59483 (void)frameCount;
59484 (void)pFramesRead;
59485
59486 return MA_NOT_IMPLEMENTED;
59487 }
59488 #endif
59489}
59490
59491MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex)
59492{
59493 if (pFlac == NULL) {
59494 return MA_INVALID_ARGS;
59495 }
59496
59497 #if !defined(MA_NO_FLAC)
59498 {
59499 drflac_bool32 flacResult;
59500
59501 flacResult = drflac_seek_to_pcm_frame(pFlac->dr, frameIndex);
59502 if (flacResult != DRFLAC_TRUE) {
59503 return MA_ERROR;
59504 }
59505
59506 return MA_SUCCESS;
59507 }
59508 #else
59509 {
59510 /* flac is disabled. Should never hit this since initialization would have failed. */
59511 MA_ASSERT(MA_FALSE);
59512
59513 (void)frameIndex;
59514
59515 return MA_NOT_IMPLEMENTED;
59516 }
59517 #endif
59518}
59519
59520MA_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)
59521{
59522 /* Defaults for safety. */
59523 if (pFormat != NULL) {
59524 *pFormat = ma_format_unknown;
59525 }
59526 if (pChannels != NULL) {
59527 *pChannels = 0;
59528 }
59529 if (pSampleRate != NULL) {
59530 *pSampleRate = 0;
59531 }
59532 if (pChannelMap != NULL) {
59533 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
59534 }
59535
59536 if (pFlac == NULL) {
59537 return MA_INVALID_OPERATION;
59538 }
59539
59540 if (pFormat != NULL) {
59541 *pFormat = pFlac->format;
59542 }
59543
59544 #if !defined(MA_NO_FLAC)
59545 {
59546 if (pChannels != NULL) {
59547 *pChannels = pFlac->dr->channels;
59548 }
59549
59550 if (pSampleRate != NULL) {
59551 *pSampleRate = pFlac->dr->sampleRate;
59552 }
59553
59554 if (pChannelMap != NULL) {
59555 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pFlac->dr->channels);
59556 }
59557
59558 return MA_SUCCESS;
59559 }
59560 #else
59561 {
59562 /* flac is disabled. Should never hit this since initialization would have failed. */
59563 MA_ASSERT(MA_FALSE);
59564 return MA_NOT_IMPLEMENTED;
59565 }
59566 #endif
59567}
59568
59569MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor)
59570{
59571 if (pCursor == NULL) {
59572 return MA_INVALID_ARGS;
59573 }
59574
59575 *pCursor = 0; /* Safety. */
59576
59577 if (pFlac == NULL) {
59578 return MA_INVALID_ARGS;
59579 }
59580
59581 #if !defined(MA_NO_FLAC)
59582 {
59583 *pCursor = pFlac->dr->currentPCMFrame;
59584
59585 return MA_SUCCESS;
59586 }
59587 #else
59588 {
59589 /* flac is disabled. Should never hit this since initialization would have failed. */
59590 MA_ASSERT(MA_FALSE);
59591 return MA_NOT_IMPLEMENTED;
59592 }
59593 #endif
59594}
59595
59596MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength)
59597{
59598 if (pLength == NULL) {
59599 return MA_INVALID_ARGS;
59600 }
59601
59602 *pLength = 0; /* Safety. */
59603
59604 if (pFlac == NULL) {
59605 return MA_INVALID_ARGS;
59606 }
59607
59608 #if !defined(MA_NO_FLAC)
59609 {
59610 *pLength = pFlac->dr->totalPCMFrameCount;
59611
59612 return MA_SUCCESS;
59613 }
59614 #else
59615 {
59616 /* flac is disabled. Should never hit this since initialization would have failed. */
59617 MA_ASSERT(MA_FALSE);
59618 return MA_NOT_IMPLEMENTED;
59619 }
59620 #endif
59621}
59622
59623
59624static 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)
59625{
59626 ma_result result;
59627 ma_flac* pFlac;
59628
59629 (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. */
59630
59631 /* For now we're just allocating the decoder backend on the heap. */
59632 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
59633 if (pFlac == NULL) {
59634 return MA_OUT_OF_MEMORY;
59635 }
59636
59637 result = ma_flac_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pFlac);
59638 if (result != MA_SUCCESS) {
59639 ma_free(pFlac, pAllocationCallbacks);
59640 return result;
59641 }
59642
59643 *ppBackend = pFlac;
59644
59645 return MA_SUCCESS;
59646}
59647
59648static 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)
59649{
59650 ma_result result;
59651 ma_flac* pFlac;
59652
59653 (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. */
59654
59655 /* For now we're just allocating the decoder backend on the heap. */
59656 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
59657 if (pFlac == NULL) {
59658 return MA_OUT_OF_MEMORY;
59659 }
59660
59661 result = ma_flac_init_file(pFilePath, pConfig, pAllocationCallbacks, pFlac);
59662 if (result != MA_SUCCESS) {
59663 ma_free(pFlac, pAllocationCallbacks);
59664 return result;
59665 }
59666
59667 *ppBackend = pFlac;
59668
59669 return MA_SUCCESS;
59670}
59671
59672static 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)
59673{
59674 ma_result result;
59675 ma_flac* pFlac;
59676
59677 (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. */
59678
59679 /* For now we're just allocating the decoder backend on the heap. */
59680 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
59681 if (pFlac == NULL) {
59682 return MA_OUT_OF_MEMORY;
59683 }
59684
59685 result = ma_flac_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pFlac);
59686 if (result != MA_SUCCESS) {
59687 ma_free(pFlac, pAllocationCallbacks);
59688 return result;
59689 }
59690
59691 *ppBackend = pFlac;
59692
59693 return MA_SUCCESS;
59694}
59695
59696static 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)
59697{
59698 ma_result result;
59699 ma_flac* pFlac;
59700
59701 (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. */
59702
59703 /* For now we're just allocating the decoder backend on the heap. */
59704 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
59705 if (pFlac == NULL) {
59706 return MA_OUT_OF_MEMORY;
59707 }
59708
59709 result = ma_flac_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pFlac);
59710 if (result != MA_SUCCESS) {
59711 ma_free(pFlac, pAllocationCallbacks);
59712 return result;
59713 }
59714
59715 *ppBackend = pFlac;
59716
59717 return MA_SUCCESS;
59718}
59719
59720static void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
59721{
59722 ma_flac* pFlac = (ma_flac*)pBackend;
59723
59724 (void)pUserData;
59725
59726 ma_flac_uninit(pFlac, pAllocationCallbacks);
59727 ma_free(pFlac, pAllocationCallbacks);
59728}
59729
59730static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac =
59731{
59732 ma_decoding_backend_init__flac,
59733 ma_decoding_backend_init_file__flac,
59734 ma_decoding_backend_init_file_w__flac,
59735 ma_decoding_backend_init_memory__flac,
59736 ma_decoding_backend_uninit__flac
59737};
59738
59739static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
59740{
59741 return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder);
59742}
59743#endif /* dr_flac_h */
59744
59745/* MP3 */
59746#ifdef dr_mp3_h
59747#define MA_HAS_MP3
59748
59749typedef struct
59750{
59752 ma_read_proc onRead;
59753 ma_seek_proc onSeek;
59754 ma_tell_proc onTell;
59755 void* pReadSeekTellUserData;
59756 ma_format format; /* Can be f32 or s16. */
59757#if !defined(MA_NO_MP3)
59758 drmp3 dr;
59759 drmp3_uint32 seekPointCount;
59760 drmp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */
59761#endif
59762} ma_mp3;
59763
59764MA_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);
59765MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3);
59766MA_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);
59767MA_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);
59768MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks);
59769MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
59770MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex);
59771MA_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);
59772MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor);
59773MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength);
59774
59775
59776static ma_result ma_mp3_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
59777{
59778 return ma_mp3_read_pcm_frames((ma_mp3*)pDataSource, pFramesOut, frameCount, pFramesRead);
59779}
59780
59781static ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
59782{
59783 return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex);
59784}
59785
59786static 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)
59787{
59788 return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
59789}
59790
59791static ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
59792{
59793 return ma_mp3_get_cursor_in_pcm_frames((ma_mp3*)pDataSource, pCursor);
59794}
59795
59796static ma_result ma_mp3_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
59797{
59798 return ma_mp3_get_length_in_pcm_frames((ma_mp3*)pDataSource, pLength);
59799}
59800
59801static ma_data_source_vtable g_ma_mp3_ds_vtable =
59802{
59803 ma_mp3_ds_read,
59804 ma_mp3_ds_seek,
59805 ma_mp3_ds_get_data_format,
59806 ma_mp3_ds_get_cursor,
59807 ma_mp3_ds_get_length,
59808 NULL, /* onSetLooping */
59809 0
59810};
59811
59812
59813#if !defined(MA_NO_MP3)
59814static drmp3_allocation_callbacks drmp3_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks)
59815{
59817
59818 if (pAllocationCallbacks != NULL) {
59819 callbacks.onMalloc = pAllocationCallbacks->onMalloc;
59820 callbacks.onRealloc = pAllocationCallbacks->onRealloc;
59821 callbacks.onFree = pAllocationCallbacks->onFree;
59822 callbacks.pUserData = pAllocationCallbacks->pUserData;
59823 } else {
59824 callbacks.onMalloc = ma__malloc_default;
59825 callbacks.onRealloc = ma__realloc_default;
59826 callbacks.onFree = ma__free_default;
59827 callbacks.pUserData = NULL;
59828 }
59829
59830 return callbacks;
59831}
59832
59833static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
59834{
59835 ma_mp3* pMP3 = (ma_mp3*)pUserData;
59836 ma_result result;
59837 size_t bytesRead;
59838
59839 MA_ASSERT(pMP3 != NULL);
59840
59841 result = pMP3->onRead(pMP3->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
59842 (void)result;
59843
59844 return bytesRead;
59845}
59846
59847static drmp3_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, drmp3_seek_origin origin)
59848{
59849 ma_mp3* pMP3 = (ma_mp3*)pUserData;
59850 ma_result result;
59851 ma_seek_origin maSeekOrigin;
59852
59853 MA_ASSERT(pMP3 != NULL);
59854
59855 maSeekOrigin = ma_seek_origin_start;
59856 if (origin == drmp3_seek_origin_current) {
59857 maSeekOrigin = ma_seek_origin_current;
59858 }
59859
59860 result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin);
59861 if (result != MA_SUCCESS) {
59862 return MA_FALSE;
59863 }
59864
59865 return MA_TRUE;
59866}
59867#endif
59868
59869static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3)
59870{
59871 ma_result result;
59872 ma_data_source_config dataSourceConfig;
59873
59874 if (pMP3 == NULL) {
59875 return MA_INVALID_ARGS;
59876 }
59877
59878 MA_ZERO_OBJECT(pMP3);
59879 pMP3->format = ma_format_f32; /* f32 by default. */
59880
59881 if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) {
59882 pMP3->format = pConfig->preferredFormat;
59883 } else {
59884 /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
59885 }
59886
59887 dataSourceConfig = ma_data_source_config_init();
59888 dataSourceConfig.vtable = &g_ma_mp3_ds_vtable;
59889
59890 result = ma_data_source_init(&dataSourceConfig, &pMP3->ds);
59891 if (result != MA_SUCCESS) {
59892 return result; /* Failed to initialize the base data source. */
59893 }
59894
59895 return MA_SUCCESS;
59896}
59897
59898static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks)
59899{
59900 drmp3_bool32 mp3Result;
59901 drmp3_uint32 seekPointCount = 0;
59902 drmp3_seek_point* pSeekPoints = NULL;
59903
59904 MA_ASSERT(pMP3 != NULL);
59905 MA_ASSERT(pConfig != NULL);
59906
59907 seekPointCount = pConfig->seekPointCount;
59908 if (seekPointCount > 0) {
59909 pSeekPoints = (drmp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks);
59910 if (pSeekPoints == NULL) {
59911 return MA_OUT_OF_MEMORY;
59912 }
59913 }
59914
59915 mp3Result = drmp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints);
59916 if (mp3Result != MA_TRUE) {
59917 return MA_ERROR;
59918 }
59919
59920 mp3Result = drmp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints);
59921 if (mp3Result != MA_TRUE) {
59922 ma_free(pSeekPoints, pAllocationCallbacks);
59923 return MA_ERROR;
59924 }
59925
59926 pMP3->seekPointCount = seekPointCount;
59927 pMP3->pSeekPoints = pSeekPoints;
59928
59929 return MA_SUCCESS;
59930}
59931
59932MA_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)
59933{
59934 ma_result result;
59935
59936 result = ma_mp3_init_internal(pConfig, pMP3);
59937 if (result != MA_SUCCESS) {
59938 return result;
59939 }
59940
59941 if (onRead == NULL || onSeek == NULL) {
59942 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
59943 }
59944
59945 pMP3->onRead = onRead;
59946 pMP3->onSeek = onSeek;
59947 pMP3->onTell = onTell;
59948 pMP3->pReadSeekTellUserData = pReadSeekTellUserData;
59949
59950 #if !defined(MA_NO_MP3)
59951 {
59952 drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
59953 drmp3_bool32 mp3Result;
59954
59955 mp3Result = drmp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, &mp3AllocationCallbacks);
59956 if (mp3Result != MA_TRUE) {
59957 return MA_INVALID_FILE;
59958 }
59959
59960 ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks);
59961
59962 return MA_SUCCESS;
59963 }
59964 #else
59965 {
59966 /* mp3 is disabled. */
59967 (void)pAllocationCallbacks;
59968 return MA_NOT_IMPLEMENTED;
59969 }
59970 #endif
59971}
59972
59973MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3)
59974{
59975 ma_result result;
59976
59977 result = ma_mp3_init_internal(pConfig, pMP3);
59978 if (result != MA_SUCCESS) {
59979 return result;
59980 }
59981
59982 #if !defined(MA_NO_MP3)
59983 {
59984 drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
59985 drmp3_bool32 mp3Result;
59986
59987 mp3Result = drmp3_init_file(&pMP3->dr, pFilePath, &mp3AllocationCallbacks);
59988 if (mp3Result != MA_TRUE) {
59989 return MA_INVALID_FILE;
59990 }
59991
59992 ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks);
59993
59994 return MA_SUCCESS;
59995 }
59996 #else
59997 {
59998 /* mp3 is disabled. */
59999 (void)pFilePath;
60000 (void)pAllocationCallbacks;
60001 return MA_NOT_IMPLEMENTED;
60002 }
60003 #endif
60004}
60005
60006MA_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)
60007{
60008 ma_result result;
60009
60010 result = ma_mp3_init_internal(pConfig, pMP3);
60011 if (result != MA_SUCCESS) {
60012 return result;
60013 }
60014
60015 #if !defined(MA_NO_MP3)
60016 {
60017 drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
60018 drmp3_bool32 mp3Result;
60019
60020 mp3Result = drmp3_init_file_w(&pMP3->dr, pFilePath, &mp3AllocationCallbacks);
60021 if (mp3Result != MA_TRUE) {
60022 return MA_INVALID_FILE;
60023 }
60024
60025 ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks);
60026
60027 return MA_SUCCESS;
60028 }
60029 #else
60030 {
60031 /* mp3 is disabled. */
60032 (void)pFilePath;
60033 (void)pAllocationCallbacks;
60034 return MA_NOT_IMPLEMENTED;
60035 }
60036 #endif
60037}
60038
60039MA_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)
60040{
60041 ma_result result;
60042
60043 result = ma_mp3_init_internal(pConfig, pMP3);
60044 if (result != MA_SUCCESS) {
60045 return result;
60046 }
60047
60048 #if !defined(MA_NO_MP3)
60049 {
60050 drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
60051 drmp3_bool32 mp3Result;
60052
60053 mp3Result = drmp3_init_memory(&pMP3->dr, pData, dataSize, &mp3AllocationCallbacks);
60054 if (mp3Result != MA_TRUE) {
60055 return MA_INVALID_FILE;
60056 }
60057
60058 ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks);
60059
60060 return MA_SUCCESS;
60061 }
60062 #else
60063 {
60064 /* mp3 is disabled. */
60065 (void)pData;
60066 (void)dataSize;
60067 (void)pAllocationCallbacks;
60068 return MA_NOT_IMPLEMENTED;
60069 }
60070 #endif
60071}
60072
60073MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks)
60074{
60075 if (pMP3 == NULL) {
60076 return;
60077 }
60078
60079 #if !defined(MA_NO_MP3)
60080 {
60081 drmp3_uninit(&pMP3->dr);
60082 }
60083 #else
60084 {
60085 /* mp3 is disabled. Should never hit this since initialization would have failed. */
60086 MA_ASSERT(MA_FALSE);
60087 }
60088 #endif
60089
60090 /* Seek points need to be freed after the MP3 decoder has been uninitialized to ensure they're no longer being referenced. */
60091 ma_free(pMP3->pSeekPoints, pAllocationCallbacks);
60092
60093 ma_data_source_uninit(&pMP3->ds);
60094}
60095
60096MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
60097{
60098 if (pFramesRead != NULL) {
60099 *pFramesRead = 0;
60100 }
60101
60102 if (frameCount == 0) {
60103 return MA_INVALID_ARGS;
60104 }
60105
60106 if (pMP3 == NULL) {
60107 return MA_INVALID_ARGS;
60108 }
60109
60110 #if !defined(MA_NO_MP3)
60111 {
60112 /* We always use floating point format. */
60113 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
60114 ma_uint64 totalFramesRead = 0;
60115 ma_format format;
60116
60117 ma_mp3_get_data_format(pMP3, &format, NULL, NULL, NULL, 0);
60118
60119 switch (format)
60120 {
60121 case ma_format_f32:
60122 {
60123 totalFramesRead = drmp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut);
60124 } break;
60125
60126 case ma_format_s16:
60127 {
60128 totalFramesRead = drmp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (drmp3_int16*)pFramesOut);
60129 } break;
60130
60131 case ma_format_u8:
60132 case ma_format_s24:
60133 case ma_format_s32:
60134 case ma_format_unknown:
60135 default:
60136 {
60137 return MA_INVALID_OPERATION;
60138 };
60139 }
60140
60141 /* In the future we'll update dr_mp3 to return MA_AT_END for us. */
60142 if (totalFramesRead == 0) {
60143 result = MA_AT_END;
60144 }
60145
60146 if (pFramesRead != NULL) {
60147 *pFramesRead = totalFramesRead;
60148 }
60149
60150 return result;
60151 }
60152 #else
60153 {
60154 /* mp3 is disabled. Should never hit this since initialization would have failed. */
60155 MA_ASSERT(MA_FALSE);
60156
60157 (void)pFramesOut;
60158 (void)frameCount;
60159 (void)pFramesRead;
60160
60161 return MA_NOT_IMPLEMENTED;
60162 }
60163 #endif
60164}
60165
60166MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex)
60167{
60168 if (pMP3 == NULL) {
60169 return MA_INVALID_ARGS;
60170 }
60171
60172 #if !defined(MA_NO_MP3)
60173 {
60174 drmp3_bool32 mp3Result;
60175
60176 mp3Result = drmp3_seek_to_pcm_frame(&pMP3->dr, frameIndex);
60177 if (mp3Result != DRMP3_TRUE) {
60178 return MA_ERROR;
60179 }
60180
60181 return MA_SUCCESS;
60182 }
60183 #else
60184 {
60185 /* mp3 is disabled. Should never hit this since initialization would have failed. */
60186 MA_ASSERT(MA_FALSE);
60187
60188 (void)frameIndex;
60189
60190 return MA_NOT_IMPLEMENTED;
60191 }
60192 #endif
60193}
60194
60195MA_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)
60196{
60197 /* Defaults for safety. */
60198 if (pFormat != NULL) {
60199 *pFormat = ma_format_unknown;
60200 }
60201 if (pChannels != NULL) {
60202 *pChannels = 0;
60203 }
60204 if (pSampleRate != NULL) {
60205 *pSampleRate = 0;
60206 }
60207 if (pChannelMap != NULL) {
60208 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
60209 }
60210
60211 if (pMP3 == NULL) {
60212 return MA_INVALID_OPERATION;
60213 }
60214
60215 if (pFormat != NULL) {
60216 *pFormat = pMP3->format;
60217 }
60218
60219 #if !defined(MA_NO_MP3)
60220 {
60221 if (pChannels != NULL) {
60222 *pChannels = pMP3->dr.channels;
60223 }
60224
60225 if (pSampleRate != NULL) {
60226 *pSampleRate = pMP3->dr.sampleRate;
60227 }
60228
60229 if (pChannelMap != NULL) {
60230 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pMP3->dr.channels);
60231 }
60232
60233 return MA_SUCCESS;
60234 }
60235 #else
60236 {
60237 /* mp3 is disabled. Should never hit this since initialization would have failed. */
60238 MA_ASSERT(MA_FALSE);
60239 return MA_NOT_IMPLEMENTED;
60240 }
60241 #endif
60242}
60243
60244MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor)
60245{
60246 if (pCursor == NULL) {
60247 return MA_INVALID_ARGS;
60248 }
60249
60250 *pCursor = 0; /* Safety. */
60251
60252 if (pMP3 == NULL) {
60253 return MA_INVALID_ARGS;
60254 }
60255
60256 #if !defined(MA_NO_MP3)
60257 {
60258 *pCursor = pMP3->dr.currentPCMFrame;
60259
60260 return MA_SUCCESS;
60261 }
60262 #else
60263 {
60264 /* mp3 is disabled. Should never hit this since initialization would have failed. */
60265 MA_ASSERT(MA_FALSE);
60266 return MA_NOT_IMPLEMENTED;
60267 }
60268 #endif
60269}
60270
60271MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength)
60272{
60273 if (pLength == NULL) {
60274 return MA_INVALID_ARGS;
60275 }
60276
60277 *pLength = 0; /* Safety. */
60278
60279 if (pMP3 == NULL) {
60280 return MA_INVALID_ARGS;
60281 }
60282
60283 #if !defined(MA_NO_MP3)
60284 {
60285 *pLength = drmp3_get_pcm_frame_count(&pMP3->dr);
60286
60287 return MA_SUCCESS;
60288 }
60289 #else
60290 {
60291 /* mp3 is disabled. Should never hit this since initialization would have failed. */
60292 MA_ASSERT(MA_FALSE);
60293 return MA_NOT_IMPLEMENTED;
60294 }
60295 #endif
60296}
60297
60298
60299static 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)
60300{
60301 ma_result result;
60302 ma_mp3* pMP3;
60303
60304 (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. */
60305
60306 /* For now we're just allocating the decoder backend on the heap. */
60307 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
60308 if (pMP3 == NULL) {
60309 return MA_OUT_OF_MEMORY;
60310 }
60311
60312 result = ma_mp3_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pMP3);
60313 if (result != MA_SUCCESS) {
60314 ma_free(pMP3, pAllocationCallbacks);
60315 return result;
60316 }
60317
60318 *ppBackend = pMP3;
60319
60320 return MA_SUCCESS;
60321}
60322
60323static 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)
60324{
60325 ma_result result;
60326 ma_mp3* pMP3;
60327
60328 (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. */
60329
60330 /* For now we're just allocating the decoder backend on the heap. */
60331 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
60332 if (pMP3 == NULL) {
60333 return MA_OUT_OF_MEMORY;
60334 }
60335
60336 result = ma_mp3_init_file(pFilePath, pConfig, pAllocationCallbacks, pMP3);
60337 if (result != MA_SUCCESS) {
60338 ma_free(pMP3, pAllocationCallbacks);
60339 return result;
60340 }
60341
60342 *ppBackend = pMP3;
60343
60344 return MA_SUCCESS;
60345}
60346
60347static 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)
60348{
60349 ma_result result;
60350 ma_mp3* pMP3;
60351
60352 (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. */
60353
60354 /* For now we're just allocating the decoder backend on the heap. */
60355 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
60356 if (pMP3 == NULL) {
60357 return MA_OUT_OF_MEMORY;
60358 }
60359
60360 result = ma_mp3_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pMP3);
60361 if (result != MA_SUCCESS) {
60362 ma_free(pMP3, pAllocationCallbacks);
60363 return result;
60364 }
60365
60366 *ppBackend = pMP3;
60367
60368 return MA_SUCCESS;
60369}
60370
60371static 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)
60372{
60373 ma_result result;
60374 ma_mp3* pMP3;
60375
60376 (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. */
60377
60378 /* For now we're just allocating the decoder backend on the heap. */
60379 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
60380 if (pMP3 == NULL) {
60381 return MA_OUT_OF_MEMORY;
60382 }
60383
60384 result = ma_mp3_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pMP3);
60385 if (result != MA_SUCCESS) {
60386 ma_free(pMP3, pAllocationCallbacks);
60387 return result;
60388 }
60389
60390 *ppBackend = pMP3;
60391
60392 return MA_SUCCESS;
60393}
60394
60395static void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
60396{
60397 ma_mp3* pMP3 = (ma_mp3*)pBackend;
60398
60399 (void)pUserData;
60400
60401 ma_mp3_uninit(pMP3, pAllocationCallbacks);
60402 ma_free(pMP3, pAllocationCallbacks);
60403}
60404
60405static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 =
60406{
60407 ma_decoding_backend_init__mp3,
60408 ma_decoding_backend_init_file__mp3,
60409 ma_decoding_backend_init_file_w__mp3,
60410 ma_decoding_backend_init_memory__mp3,
60411 ma_decoding_backend_uninit__mp3
60412};
60413
60414static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
60415{
60416 return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder);
60417}
60418#endif /* dr_mp3_h */
60419
60420/* Vorbis */
60421#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
60422#define MA_HAS_VORBIS
60423
60424/* The size in bytes of each chunk of data to read from the Vorbis stream. */
60425#define MA_VORBIS_DATA_CHUNK_SIZE 4096
60426
60427typedef struct
60428{
60430 ma_read_proc onRead;
60431 ma_seek_proc onSeek;
60432 ma_tell_proc onTell;
60433 void* pReadSeekTellUserData;
60434 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. */
60435 ma_format format; /* Only f32 is allowed with stb_vorbis. */
60436 ma_uint32 channels;
60437 ma_uint32 sampleRate;
60438 ma_uint64 cursor;
60439#if !defined(MA_NO_VORBIS)
60440 stb_vorbis* stb;
60441 ma_bool32 usingPushMode;
60442 struct
60443 {
60444 ma_uint8* pData;
60445 size_t dataSize;
60446 size_t dataCapacity;
60447 ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */
60448 ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */
60449 float** ppPacketData;
60450 } push;
60451#endif
60452} ma_stbvorbis;
60453
60454MA_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);
60455MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis);
60456MA_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);
60457MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks);
60458MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
60459MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex);
60460MA_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);
60461MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor);
60462MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength);
60463
60464
60465static ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
60466{
60467 return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead);
60468}
60469
60470static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
60471{
60472 return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex);
60473}
60474
60475static 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)
60476{
60477 return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
60478}
60479
60480static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
60481{
60482 return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor);
60483}
60484
60485static ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
60486{
60487 return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength);
60488}
60489
60490static ma_data_source_vtable g_ma_stbvorbis_ds_vtable =
60491{
60492 ma_stbvorbis_ds_read,
60493 ma_stbvorbis_ds_seek,
60494 ma_stbvorbis_ds_get_data_format,
60495 ma_stbvorbis_ds_get_cursor,
60496 ma_stbvorbis_ds_get_length,
60497 NULL, /* onSetLooping */
60498 0
60499};
60500
60501
60502static ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis)
60503{
60504 ma_result result;
60505 ma_data_source_config dataSourceConfig;
60506
60507 (void)pConfig;
60508
60509 if (pVorbis == NULL) {
60510 return MA_INVALID_ARGS;
60511 }
60512
60513 MA_ZERO_OBJECT(pVorbis);
60514 pVorbis->format = ma_format_f32; /* Only supporting f32. */
60515
60516 dataSourceConfig = ma_data_source_config_init();
60517 dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable;
60518
60519 result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds);
60520 if (result != MA_SUCCESS) {
60521 return result; /* Failed to initialize the base data source. */
60522 }
60523
60524 return MA_SUCCESS;
60525}
60526
60527#if !defined(MA_NO_VORBIS)
60528static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis)
60529{
60530 stb_vorbis_info info;
60531
60532 MA_ASSERT(pVorbis != NULL);
60533
60534 info = stb_vorbis_get_info(pVorbis->stb);
60535
60536 pVorbis->channels = info.channels;
60537 pVorbis->sampleRate = info.sample_rate;
60538
60539 return MA_SUCCESS;
60540}
60541#endif
60542
60543MA_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)
60544{
60545 ma_result result;
60546
60547 result = ma_stbvorbis_init_internal(pConfig, pVorbis);
60548 if (result != MA_SUCCESS) {
60549 return result;
60550 }
60551
60552 if (onRead == NULL || onSeek == NULL) {
60553 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
60554 }
60555
60556 pVorbis->onRead = onRead;
60557 pVorbis->onSeek = onSeek;
60558 pVorbis->onTell = onTell;
60559 pVorbis->pReadSeekTellUserData = pReadSeekTellUserData;
60560 ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks);
60561
60562 #if !defined(MA_NO_VORBIS)
60563 {
60564 /*
60565 stb_vorbis lacks a callback based API for it's pulling API which means we're stuck with the
60566 pushing API. In order for us to be able to successfully initialize the decoder we need to
60567 supply it with enough data. We need to keep loading data until we have enough.
60568 */
60569 stb_vorbis* stb;
60570 size_t dataSize = 0;
60571 size_t dataCapacity = 0;
60572 ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */
60573
60574 for (;;) {
60575 int vorbisError;
60576 int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */
60577 size_t bytesRead;
60578 ma_uint8* pNewData;
60579
60580 /* Allocate memory for the new chunk. */
60581 dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE;
60582 pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, pAllocationCallbacks);
60583 if (pNewData == NULL) {
60584 ma_free(pData, pAllocationCallbacks);
60585 return MA_OUT_OF_MEMORY;
60586 }
60587
60588 pData = pNewData;
60589
60590 /* Read in the next chunk. */
60591 result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead);
60592 dataSize += bytesRead;
60593
60594 if (result != MA_SUCCESS) {
60595 ma_free(pData, pAllocationCallbacks);
60596 return result;
60597 }
60598
60599 /* We have a maximum of 31 bits with stb_vorbis. */
60600 if (dataSize > INT_MAX) {
60601 ma_free(pData, pAllocationCallbacks);
60602 return MA_TOO_BIG;
60603 }
60604
60605 stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL);
60606 if (stb != NULL) {
60607 /*
60608 Successfully opened the Vorbis decoder. We might have some leftover unprocessed
60609 data so we'll need to move that down to the front.
60610 */
60611 dataSize -= (size_t)consumedDataSize; /* Consume the data. */
60612 MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize);
60613 break;
60614 } else {
60615 /* Failed to open the decoder. */
60616 if (vorbisError == VORBIS_need_more_data) {
60617 continue;
60618 } else {
60619 ma_free(pData, pAllocationCallbacks);
60620 return MA_ERROR; /* Failed to open the stb_vorbis decoder. */
60621 }
60622 }
60623 }
60624
60625 MA_ASSERT(stb != NULL);
60626 pVorbis->stb = stb;
60627 pVorbis->push.pData = pData;
60628 pVorbis->push.dataSize = dataSize;
60629 pVorbis->push.dataCapacity = dataCapacity;
60630
60631 pVorbis->usingPushMode = MA_TRUE;
60632
60633 result = ma_stbvorbis_post_init(pVorbis);
60634 if (result != MA_SUCCESS) {
60635 stb_vorbis_close(pVorbis->stb);
60636 ma_free(pData, pAllocationCallbacks);
60637 return result;
60638 }
60639
60640 return MA_SUCCESS;
60641 }
60642 #else
60643 {
60644 /* vorbis is disabled. */
60645 (void)pAllocationCallbacks;
60646 return MA_NOT_IMPLEMENTED;
60647 }
60648 #endif
60649}
60650
60651MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis)
60652{
60653 ma_result result;
60654
60655 result = ma_stbvorbis_init_internal(pConfig, pVorbis);
60656 if (result != MA_SUCCESS) {
60657 return result;
60658 }
60659
60660 #if !defined(MA_NO_VORBIS)
60661 {
60662 (void)pAllocationCallbacks; /* Don't know how to make use of this with stb_vorbis. */
60663
60664 /* We can use stb_vorbis' pull mode for file based streams. */
60665 pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL);
60666 if (pVorbis->stb == NULL) {
60667 return MA_INVALID_FILE;
60668 }
60669
60670 pVorbis->usingPushMode = MA_FALSE;
60671
60672 result = ma_stbvorbis_post_init(pVorbis);
60673 if (result != MA_SUCCESS) {
60674 stb_vorbis_close(pVorbis->stb);
60675 return result;
60676 }
60677
60678 return MA_SUCCESS;
60679 }
60680 #else
60681 {
60682 /* vorbis is disabled. */
60683 (void)pFilePath;
60684 (void)pAllocationCallbacks;
60685 return MA_NOT_IMPLEMENTED;
60686 }
60687 #endif
60688}
60689
60690MA_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)
60691{
60692 ma_result result;
60693
60694 result = ma_stbvorbis_init_internal(pConfig, pVorbis);
60695 if (result != MA_SUCCESS) {
60696 return result;
60697 }
60698
60699 #if !defined(MA_NO_VORBIS)
60700 {
60701 (void)pAllocationCallbacks;
60702
60703 /* stb_vorbis uses an int as it's size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */
60704 if (dataSize > INT_MAX) {
60705 return MA_TOO_BIG;
60706 }
60707
60708 pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL);
60709 if (pVorbis->stb == NULL) {
60710 return MA_INVALID_FILE;
60711 }
60712
60713 pVorbis->usingPushMode = MA_FALSE;
60714
60715 result = ma_stbvorbis_post_init(pVorbis);
60716 if (result != MA_SUCCESS) {
60717 stb_vorbis_close(pVorbis->stb);
60718 return result;
60719 }
60720
60721 return MA_SUCCESS;
60722 }
60723 #else
60724 {
60725 /* vorbis is disabled. */
60726 (void)pData;
60727 (void)dataSize;
60728 (void)pAllocationCallbacks;
60729 return MA_NOT_IMPLEMENTED;
60730 }
60731 #endif
60732}
60733
60734MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks)
60735{
60736 if (pVorbis == NULL) {
60737 return;
60738 }
60739
60740 #if !defined(MA_NO_VORBIS)
60741 {
60742 stb_vorbis_close(pVorbis->stb);
60743
60744 /* We'll have to clear some memory if we're using push mode. */
60745 if (pVorbis->usingPushMode) {
60746 ma_free(pVorbis->push.pData, pAllocationCallbacks);
60747 }
60748 }
60749 #else
60750 {
60751 /* vorbis is disabled. Should never hit this since initialization would have failed. */
60752 MA_ASSERT(MA_FALSE);
60753 }
60754 #endif
60755
60756 ma_data_source_uninit(&pVorbis->ds);
60757}
60758
60759MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
60760{
60761 if (pFramesRead != NULL) {
60762 *pFramesRead = 0;
60763 }
60764
60765 if (frameCount == 0) {
60766 return MA_INVALID_ARGS;
60767 }
60768
60769 if (pVorbis == NULL) {
60770 return MA_INVALID_ARGS;
60771 }
60772
60773 #if !defined(MA_NO_VORBIS)
60774 {
60775 /* We always use floating point format. */
60776 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
60777 ma_uint64 totalFramesRead = 0;
60778 ma_format format;
60779 ma_uint32 channels;
60780
60781 ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0);
60782
60783 if (format == ma_format_f32) {
60784 /* We read differently depending on whether or not we're using push mode. */
60785 if (pVorbis->usingPushMode) {
60786 /* Push mode. This is the complex case. */
60787 float* pFramesOutF32 = (float*)pFramesOut;
60788
60789 while (totalFramesRead < frameCount) {
60790 /* The first thing to do is read from any already-cached frames. */
60791 ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */
60792
60793 /* The output pointer can be null in which case we just treate it as a seek. */
60794 if (pFramesOut != NULL) {
60795 ma_uint64 iFrame;
60796 for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) {
60797 ma_uint32 iChannel;
60798 for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) {
60799 pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame];
60800 }
60801
60802 pFramesOutF32 += pVorbis->channels;
60803 }
60804 }
60805
60806 /* Update pointers and counters. */
60807 pVorbis->push.framesConsumed += framesToReadFromCache;
60808 pVorbis->push.framesRemaining -= framesToReadFromCache;
60809 totalFramesRead += framesToReadFromCache;
60810
60811 /* Don't bother reading any more frames right now if we've just finished loading. */
60812 if (totalFramesRead == frameCount) {
60813 break;
60814 }
60815
60816 MA_ASSERT(pVorbis->push.framesRemaining == 0);
60817
60818 /* Getting here means we've run out of cached frames. We'll need to load some more. */
60819 for (;;) {
60820 int samplesRead = 0;
60821 int consumedDataSize;
60822
60823 /* We need to case dataSize to an int, so make sure we can do it safely. */
60824 if (pVorbis->push.dataSize > INT_MAX) {
60825 break; /* Too big. */
60826 }
60827
60828 consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead);
60829 if (consumedDataSize != 0) {
60830 /* Successfully decoded a Vorbis frame. Consume the data. */
60831 pVorbis->push.dataSize -= (size_t)consumedDataSize;
60832 MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize);
60833
60834 pVorbis->push.framesConsumed = 0;
60835 pVorbis->push.framesRemaining = samplesRead;
60836
60837 break;
60838 } else {
60839 /* Not enough data. Read more. */
60840 size_t bytesRead;
60841
60842 /* Expand the data buffer if necessary. */
60843 if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) {
60844 size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE;
60845 ma_uint8* pNewData;
60846
60847 pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks);
60848 if (pNewData == NULL) {
60849 result = MA_OUT_OF_MEMORY;
60850 break;
60851 }
60852
60853 pVorbis->push.pData = pNewData;
60854 pVorbis->push.dataCapacity = newCap;
60855 }
60856
60857 /* We should have enough room to load some data. */
60858 result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead);
60859 pVorbis->push.dataSize += bytesRead;
60860
60861 if (result != MA_SUCCESS) {
60862 break; /* Failed to read any data. Get out. */
60863 }
60864 }
60865 }
60866
60867 /* If we don't have a success code at this point it means we've encounted an error or the end of the file has been reached (probably the latter). */
60868 if (result != MA_SUCCESS) {
60869 break;
60870 }
60871 }
60872 } else {
60873 /* 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. */
60874 while (totalFramesRead < frameCount) {
60875 ma_uint64 framesRemaining = (frameCount - totalFramesRead);
60876 int framesRead;
60877
60878 if (framesRemaining > INT_MAX) {
60879 framesRemaining = INT_MAX;
60880 }
60881
60882 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. */
60883 totalFramesRead += framesRead;
60884
60885 if (framesRead < (int)framesRemaining) {
60886 break; /* Nothing left to read. Get out. */
60887 }
60888 }
60889 }
60890 } else {
60891 result = MA_INVALID_ARGS;
60892 }
60893
60894 pVorbis->cursor += totalFramesRead;
60895
60896 if (totalFramesRead == 0) {
60897 result = MA_AT_END;
60898 }
60899
60900 if (pFramesRead != NULL) {
60901 *pFramesRead = totalFramesRead;
60902 }
60903
60904 if (result == MA_SUCCESS && totalFramesRead == 0) {
60905 result = MA_AT_END;
60906 }
60907
60908 return result;
60909 }
60910 #else
60911 {
60912 /* vorbis is disabled. Should never hit this since initialization would have failed. */
60913 MA_ASSERT(MA_FALSE);
60914
60915 (void)pFramesOut;
60916 (void)frameCount;
60917 (void)pFramesRead;
60918
60919 return MA_NOT_IMPLEMENTED;
60920 }
60921 #endif
60922}
60923
60924MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex)
60925{
60926 if (pVorbis == NULL) {
60927 return MA_INVALID_ARGS;
60928 }
60929
60930 #if !defined(MA_NO_VORBIS)
60931 {
60932 /* Different seeking methods depending on whether or not we're using push mode. */
60933 if (pVorbis->usingPushMode) {
60934 /* Push mode. This is the complex case. */
60935 ma_result result;
60936 float buffer[4096];
60937
60938 /*
60939 This is terribly inefficient because stb_vorbis does not have a good seeking solution with it's push API. Currently this just performs
60940 a full decode right from the start of the stream. Later on I'll need to write a layer that goes through all of the Ogg pages until we
60941 find the one containing the sample we need. Then we know exactly where to seek for stb_vorbis.
60942
60943 TODO: Use seeking logic documented for stb_vorbis_flush_pushdata().
60944 */
60945
60946 /* Seek to the start of the file to begin with. */
60947 result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start);
60948 if (result != MA_SUCCESS) {
60949 return result;
60950 }
60951
60952 stb_vorbis_flush_pushdata(pVorbis->stb);
60953 pVorbis->push.framesRemaining = 0;
60954 pVorbis->push.dataSize = 0;
60955
60956 /* Move the cursor back to the start. We'll increment this in the loop below. */
60957 pVorbis->cursor = 0;
60958
60959 while (pVorbis->cursor < frameIndex) {
60960 ma_uint64 framesRead;
60961 ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels;
60962 if (framesToRead > (frameIndex - pVorbis->cursor)) {
60963 framesToRead = (frameIndex - pVorbis->cursor);
60964 }
60965
60966 result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead);
60967 pVorbis->cursor += framesRead;
60968
60969 if (result != MA_SUCCESS) {
60970 return result;
60971 }
60972 }
60973 } else {
60974 /* Pull mode. This is the simple case. */
60975 int vorbisResult;
60976
60977 if (frameIndex > UINT_MAX) {
60978 return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */
60979 }
60980
60981 vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex); /* Safe cast. */
60982 if (vorbisResult == 0) {
60983 return MA_ERROR; /* See failed. */
60984 }
60985
60986 pVorbis->cursor = frameIndex;
60987 }
60988
60989 return MA_SUCCESS;
60990 }
60991 #else
60992 {
60993 /* vorbis is disabled. Should never hit this since initialization would have failed. */
60994 MA_ASSERT(MA_FALSE);
60995
60996 (void)frameIndex;
60997
60998 return MA_NOT_IMPLEMENTED;
60999 }
61000 #endif
61001}
61002
61003MA_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)
61004{
61005 /* Defaults for safety. */
61006 if (pFormat != NULL) {
61007 *pFormat = ma_format_unknown;
61008 }
61009 if (pChannels != NULL) {
61010 *pChannels = 0;
61011 }
61012 if (pSampleRate != NULL) {
61013 *pSampleRate = 0;
61014 }
61015 if (pChannelMap != NULL) {
61016 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
61017 }
61018
61019 if (pVorbis == NULL) {
61020 return MA_INVALID_OPERATION;
61021 }
61022
61023 if (pFormat != NULL) {
61024 *pFormat = pVorbis->format;
61025 }
61026
61027 #if !defined(MA_NO_VORBIS)
61028 {
61029 if (pChannels != NULL) {
61030 *pChannels = pVorbis->channels;
61031 }
61032
61033 if (pSampleRate != NULL) {
61034 *pSampleRate = pVorbis->sampleRate;
61035 }
61036
61037 if (pChannelMap != NULL) {
61038 ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pVorbis->channels);
61039 }
61040
61041 return MA_SUCCESS;
61042 }
61043 #else
61044 {
61045 /* vorbis is disabled. Should never hit this since initialization would have failed. */
61046 MA_ASSERT(MA_FALSE);
61047 return MA_NOT_IMPLEMENTED;
61048 }
61049 #endif
61050}
61051
61052MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor)
61053{
61054 if (pCursor == NULL) {
61055 return MA_INVALID_ARGS;
61056 }
61057
61058 *pCursor = 0; /* Safety. */
61059
61060 if (pVorbis == NULL) {
61061 return MA_INVALID_ARGS;
61062 }
61063
61064 #if !defined(MA_NO_VORBIS)
61065 {
61066 *pCursor = pVorbis->cursor;
61067
61068 return MA_SUCCESS;
61069 }
61070 #else
61071 {
61072 /* vorbis is disabled. Should never hit this since initialization would have failed. */
61073 MA_ASSERT(MA_FALSE);
61074 return MA_NOT_IMPLEMENTED;
61075 }
61076 #endif
61077}
61078
61079MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength)
61080{
61081 if (pLength == NULL) {
61082 return MA_INVALID_ARGS;
61083 }
61084
61085 *pLength = 0; /* Safety. */
61086
61087 if (pVorbis == NULL) {
61088 return MA_INVALID_ARGS;
61089 }
61090
61091 #if !defined(MA_NO_VORBIS)
61092 {
61093 if (pVorbis->usingPushMode) {
61094 *pLength = 0; /* I don't know of a good way to determine this reliably with stb_vorbis and push mode. */
61095 } else {
61096 *pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb);
61097 }
61098
61099 return MA_SUCCESS;
61100 }
61101 #else
61102 {
61103 /* vorbis is disabled. Should never hit this since initialization would have failed. */
61104 MA_ASSERT(MA_FALSE);
61105 return MA_NOT_IMPLEMENTED;
61106 }
61107 #endif
61108}
61109
61110
61111static 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)
61112{
61113 ma_result result;
61114 ma_stbvorbis* pVorbis;
61115
61116 (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. */
61117
61118 /* For now we're just allocating the decoder backend on the heap. */
61119 pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
61120 if (pVorbis == NULL) {
61121 return MA_OUT_OF_MEMORY;
61122 }
61123
61124 result = ma_stbvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis);
61125 if (result != MA_SUCCESS) {
61126 ma_free(pVorbis, pAllocationCallbacks);
61127 return result;
61128 }
61129
61130 *ppBackend = pVorbis;
61131
61132 return MA_SUCCESS;
61133}
61134
61135static 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)
61136{
61137 ma_result result;
61138 ma_stbvorbis* pVorbis;
61139
61140 (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. */
61141
61142 /* For now we're just allocating the decoder backend on the heap. */
61143 pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
61144 if (pVorbis == NULL) {
61145 return MA_OUT_OF_MEMORY;
61146 }
61147
61148 result = ma_stbvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis);
61149 if (result != MA_SUCCESS) {
61150 ma_free(pVorbis, pAllocationCallbacks);
61151 return result;
61152 }
61153
61154 *ppBackend = pVorbis;
61155
61156 return MA_SUCCESS;
61157}
61158
61159static 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)
61160{
61161 ma_result result;
61162 ma_stbvorbis* pVorbis;
61163
61164 (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. */
61165
61166 /* For now we're just allocating the decoder backend on the heap. */
61167 pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
61168 if (pVorbis == NULL) {
61169 return MA_OUT_OF_MEMORY;
61170 }
61171
61172 result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis);
61173 if (result != MA_SUCCESS) {
61174 ma_free(pVorbis, pAllocationCallbacks);
61175 return result;
61176 }
61177
61178 *ppBackend = pVorbis;
61179
61180 return MA_SUCCESS;
61181}
61182
61183static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
61184{
61185 ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend;
61186
61187 (void)pUserData;
61188
61189 ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks);
61190 ma_free(pVorbis, pAllocationCallbacks);
61191}
61192
61193static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis =
61194{
61195 ma_decoding_backend_init__stbvorbis,
61196 ma_decoding_backend_init_file__stbvorbis,
61197 NULL, /* onInitFileW() */
61198 ma_decoding_backend_init_memory__stbvorbis,
61199 ma_decoding_backend_uninit__stbvorbis
61200};
61201
61202static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
61203{
61204 return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder);
61205}
61206#endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */
61207
61208
61209
61210static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
61211{
61212 MA_ASSERT(pDecoder != NULL);
61213
61214 if (pConfig != NULL) {
61215 return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks);
61216 } else {
61217 pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default();
61218 return MA_SUCCESS;
61219 }
61220}
61221
61222static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
61223{
61224 return ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount, pFramesRead);
61225}
61226
61227static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
61228{
61229 return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex);
61230}
61231
61232static 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)
61233{
61234 return ma_decoder_get_data_format((ma_decoder*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
61235}
61236
61237static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
61238{
61239 return ma_decoder_get_cursor_in_pcm_frames((ma_decoder*)pDataSource, pCursor);
61240}
61241
61242static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
61243{
61244 return ma_decoder_get_length_in_pcm_frames((ma_decoder*)pDataSource, pLength);
61245}
61246
61247static ma_data_source_vtable g_ma_decoder_data_source_vtable =
61248{
61249 ma_decoder__data_source_on_read,
61250 ma_decoder__data_source_on_seek,
61251 ma_decoder__data_source_on_get_data_format,
61252 ma_decoder__data_source_on_get_cursor,
61253 ma_decoder__data_source_on_get_length,
61254 NULL, /* onSetLooping */
61255 0
61256};
61257
61258static 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)
61259{
61260 ma_result result;
61261 ma_data_source_config dataSourceConfig;
61262
61263 MA_ASSERT(pConfig != NULL);
61264
61265 if (pDecoder == NULL) {
61266 return MA_INVALID_ARGS;
61267 }
61268
61269 MA_ZERO_OBJECT(pDecoder);
61270
61271 if (onRead == NULL || onSeek == NULL) {
61272 return MA_INVALID_ARGS;
61273 }
61274
61275 dataSourceConfig = ma_data_source_config_init();
61276 dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable;
61277
61278 result = ma_data_source_init(&dataSourceConfig, &pDecoder->ds);
61279 if (result != MA_SUCCESS) {
61280 return result;
61281 }
61282
61283 pDecoder->onRead = onRead;
61284 pDecoder->onSeek = onSeek;
61285 pDecoder->onTell = onTell;
61286 pDecoder->pUserData = pUserData;
61287
61288 result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder);
61289 if (result != MA_SUCCESS) {
61290 ma_data_source_uninit(&pDecoder->ds);
61291 return result;
61292 }
61293
61294 return MA_SUCCESS;
61295}
61296
61297static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
61298{
61299 ma_result result;
61300
61301 result = ma_decoder__init_data_converter(pDecoder, pConfig);
61302
61303 /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */
61304 if (result != MA_SUCCESS) {
61305 ma_decoder_uninit(pDecoder);
61306 return result;
61307 }
61308
61309 return result;
61310}
61311
61312
61313static 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)
61314{
61315 ma_result result = MA_NO_BACKEND;
61316
61317 MA_ASSERT(pConfig != NULL);
61318 MA_ASSERT(pDecoder != NULL);
61319
61320 /* Silence some warnings in the case that we don't have any decoder backends enabled. */
61321 (void)onRead;
61322 (void)onSeek;
61323 (void)pUserData;
61324
61325
61326 /* If we've specified a specific encoding type, try that first. */
61328 #ifdef MA_HAS_WAV
61329 if (pConfig->encodingFormat == ma_encoding_format_wav) {
61330 result = ma_decoder_init_wav__internal(pConfig, pDecoder);
61331 }
61332 #endif
61333 #ifdef MA_HAS_FLAC
61334 if (pConfig->encodingFormat == ma_encoding_format_flac) {
61335 result = ma_decoder_init_flac__internal(pConfig, pDecoder);
61336 }
61337 #endif
61338 #ifdef MA_HAS_MP3
61339 if (pConfig->encodingFormat == ma_encoding_format_mp3) {
61340 result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
61341 }
61342 #endif
61343 #ifdef MA_HAS_VORBIS
61344 if (pConfig->encodingFormat == ma_encoding_format_vorbis) {
61345 result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
61346 }
61347 #endif
61348
61349 /* If we weren't able to initialize the decoder, seek back to the start to give the next attempts a clean start. */
61350 if (result != MA_SUCCESS) {
61351 onSeek(pDecoder, 0, ma_seek_origin_start);
61352 }
61353 }
61354
61355 if (result != MA_SUCCESS) {
61356 /* Getting here means we couldn't load a specific decoding backend based on the encoding format. */
61357
61358 /*
61359 We use trial and error to open a decoder. We prioritize custom decoders so that if they
61360 implement the same encoding format they take priority over the built-in decoders.
61361 */
61362 if (result != MA_SUCCESS) {
61363 result = ma_decoder_init_custom__internal(pConfig, pDecoder);
61364 if (result != MA_SUCCESS) {
61365 onSeek(pDecoder, 0, ma_seek_origin_start);
61366 }
61367 }
61368
61369 /*
61370 If we get to this point and we still haven't found a decoder, and the caller has requested a
61371 specific encoding format, there's no hope for it. Abort.
61372 */
61374 return MA_NO_BACKEND;
61375 }
61376
61377 #ifdef MA_HAS_WAV
61378 if (result != MA_SUCCESS) {
61379 result = ma_decoder_init_wav__internal(pConfig, pDecoder);
61380 if (result != MA_SUCCESS) {
61381 onSeek(pDecoder, 0, ma_seek_origin_start);
61382 }
61383 }
61384 #endif
61385 #ifdef MA_HAS_FLAC
61386 if (result != MA_SUCCESS) {
61387 result = ma_decoder_init_flac__internal(pConfig, pDecoder);
61388 if (result != MA_SUCCESS) {
61389 onSeek(pDecoder, 0, ma_seek_origin_start);
61390 }
61391 }
61392 #endif
61393 #ifdef MA_HAS_MP3
61394 if (result != MA_SUCCESS) {
61395 result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
61396 if (result != MA_SUCCESS) {
61397 onSeek(pDecoder, 0, ma_seek_origin_start);
61398 }
61399 }
61400 #endif
61401 #ifdef MA_HAS_VORBIS
61402 if (result != MA_SUCCESS) {
61403 result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
61404 if (result != MA_SUCCESS) {
61405 onSeek(pDecoder, 0, ma_seek_origin_start);
61406 }
61407 }
61408 #endif
61409 }
61410
61411 if (result != MA_SUCCESS) {
61412 return result;
61413 }
61414
61415 return ma_decoder__postinit(pConfig, pDecoder);
61416}
61417
61418MA_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)
61419{
61420 ma_decoder_config config;
61421 ma_result result;
61422
61423 config = ma_decoder_config_init_copy(pConfig);
61424
61425 result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder);
61426 if (result != MA_SUCCESS) {
61427 return result;
61428 }
61429
61430 return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder);
61431}
61432
61433
61434static ma_result ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
61435{
61436 size_t bytesRemaining;
61437
61438 MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos);
61439
61440 if (pBytesRead != NULL) {
61441 *pBytesRead = 0;
61442 }
61443
61444 bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos;
61445 if (bytesToRead > bytesRemaining) {
61446 bytesToRead = bytesRemaining;
61447 }
61448
61449 if (bytesRemaining == 0) {
61450 return MA_AT_END;
61451 }
61452
61453 if (bytesToRead > 0) {
61454 MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead);
61455 pDecoder->data.memory.currentReadPos += bytesToRead;
61456 }
61457
61458 if (pBytesRead != NULL) {
61459 *pBytesRead = bytesToRead;
61460 }
61461
61462 return MA_SUCCESS;
61463}
61464
61465static ma_result ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin)
61466{
61467 if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) {
61468 return MA_BAD_SEEK;
61469 }
61470
61471 if (origin == ma_seek_origin_current) {
61472 if (byteOffset > 0) {
61473 if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) {
61474 byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos); /* Trying to seek too far forward. */
61475 }
61476
61477 pDecoder->data.memory.currentReadPos += (size_t)byteOffset;
61478 } else {
61479 if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) {
61480 byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos; /* Trying to seek too far backwards. */
61481 }
61482
61483 pDecoder->data.memory.currentReadPos -= (size_t)-byteOffset;
61484 }
61485 } else {
61486 if (origin == ma_seek_origin_end) {
61487 if (byteOffset < 0) {
61488 byteOffset = -byteOffset;
61489 }
61490
61491 if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) {
61492 pDecoder->data.memory.currentReadPos = 0; /* Trying to seek too far back. */
61493 } else {
61494 pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - (size_t)byteOffset;
61495 }
61496 } else {
61497 if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) {
61498 pDecoder->data.memory.currentReadPos = (size_t)byteOffset;
61499 } else {
61500 pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize; /* Trying to seek too far forward. */
61501 }
61502 }
61503 }
61504
61505 return MA_SUCCESS;
61506}
61507
61508static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor)
61509{
61510 MA_ASSERT(pDecoder != NULL);
61511 MA_ASSERT(pCursor != NULL);
61512
61513 *pCursor = (ma_int64)pDecoder->data.memory.currentReadPos;
61514
61515 return MA_SUCCESS;
61516}
61517
61518static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
61519{
61520 ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder);
61521 if (result != MA_SUCCESS) {
61522 return result;
61523 }
61524
61525 if (pData == NULL || dataSize == 0) {
61526 return MA_INVALID_ARGS;
61527 }
61528
61529 pDecoder->data.memory.pData = (const ma_uint8*)pData;
61530 pDecoder->data.memory.dataSize = dataSize;
61531 pDecoder->data.memory.currentReadPos = 0;
61532
61533 (void)pConfig;
61534 return MA_SUCCESS;
61535}
61536
61537MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
61538{
61539 ma_decoder_config config;
61540 ma_result result;
61541
61542 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
61543
61544 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
61545 if (result != MA_SUCCESS) {
61546 return result;
61547 }
61548
61549 return ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder);
61550}
61551
61552
61553#if defined(MA_HAS_WAV) || \
61554 defined(MA_HAS_MP3) || \
61555 defined(MA_HAS_FLAC) || \
61556 defined(MA_HAS_VORBIS) || \
61557 defined(MA_HAS_OPUS)
61558#define MA_HAS_PATH_API
61559#endif
61560
61561#if defined(MA_HAS_PATH_API)
61562static const char* ma_path_file_name(const char* path)
61563{
61564 const char* fileName;
61565
61566 if (path == NULL) {
61567 return NULL;
61568 }
61569
61570 fileName = path;
61571
61572 /* We just loop through the path until we find the last slash. */
61573 while (path[0] != '\0') {
61574 if (path[0] == '/' || path[0] == '\\') {
61575 fileName = path;
61576 }
61577
61578 path += 1;
61579 }
61580
61581 /* At this point the file name is sitting on a slash, so just move forward. */
61582 while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
61583 fileName += 1;
61584 }
61585
61586 return fileName;
61587}
61588
61589static const wchar_t* ma_path_file_name_w(const wchar_t* path)
61590{
61591 const wchar_t* fileName;
61592
61593 if (path == NULL) {
61594 return NULL;
61595 }
61596
61597 fileName = path;
61598
61599 /* We just loop through the path until we find the last slash. */
61600 while (path[0] != '\0') {
61601 if (path[0] == '/' || path[0] == '\\') {
61602 fileName = path;
61603 }
61604
61605 path += 1;
61606 }
61607
61608 /* At this point the file name is sitting on a slash, so just move forward. */
61609 while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
61610 fileName += 1;
61611 }
61612
61613 return fileName;
61614}
61615
61616
61617static const char* ma_path_extension(const char* path)
61618{
61619 const char* extension;
61620 const char* lastOccurance;
61621
61622 if (path == NULL) {
61623 path = "";
61624 }
61625
61626 extension = ma_path_file_name(path);
61627 lastOccurance = NULL;
61628
61629 /* Just find the last '.' and return. */
61630 while (extension[0] != '\0') {
61631 if (extension[0] == '.') {
61632 extension += 1;
61633 lastOccurance = extension;
61634 }
61635
61636 extension += 1;
61637 }
61638
61639 return (lastOccurance != NULL) ? lastOccurance : extension;
61640}
61641
61642static const wchar_t* ma_path_extension_w(const wchar_t* path)
61643{
61644 const wchar_t* extension;
61645 const wchar_t* lastOccurance;
61646
61647 if (path == NULL) {
61648 path = L"";
61649 }
61650
61651 extension = ma_path_file_name_w(path);
61652 lastOccurance = NULL;
61653
61654 /* Just find the last '.' and return. */
61655 while (extension[0] != '\0') {
61656 if (extension[0] == '.') {
61657 extension += 1;
61658 lastOccurance = extension;
61659 }
61660
61661 extension += 1;
61662 }
61663
61664 return (lastOccurance != NULL) ? lastOccurance : extension;
61665}
61666
61667
61668static ma_bool32 ma_path_extension_equal(const char* path, const char* extension)
61669{
61670 const char* ext1;
61671 const char* ext2;
61672
61673 if (path == NULL || extension == NULL) {
61674 return MA_FALSE;
61675 }
61676
61677 ext1 = extension;
61678 ext2 = ma_path_extension(path);
61679
61680#if defined(_MSC_VER) || defined(__DMC__)
61681 return _stricmp(ext1, ext2) == 0;
61682#else
61683 return strcasecmp(ext1, ext2) == 0;
61684#endif
61685}
61686
61687static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension)
61688{
61689 const wchar_t* ext1;
61690 const wchar_t* ext2;
61691
61692 if (path == NULL || extension == NULL) {
61693 return MA_FALSE;
61694 }
61695
61696 ext1 = extension;
61697 ext2 = ma_path_extension_w(path);
61698
61699#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)
61700 return _wcsicmp(ext1, ext2) == 0;
61701#else
61702 /*
61703 I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This
61704 isn't the most efficient way to do it, but it should work OK.
61705 */
61706 {
61707 char ext1MB[4096];
61708 char ext2MB[4096];
61709 const wchar_t* pext1 = ext1;
61710 const wchar_t* pext2 = ext2;
61711 mbstate_t mbs1;
61712 mbstate_t mbs2;
61713
61714 MA_ZERO_OBJECT(&mbs1);
61715 MA_ZERO_OBJECT(&mbs2);
61716
61717 if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) {
61718 return MA_FALSE;
61719 }
61720 if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) {
61721 return MA_FALSE;
61722 }
61723
61724 return strcasecmp(ext1MB, ext2MB) == 0;
61725 }
61726#endif
61727}
61728#endif /* MA_HAS_PATH_API */
61729
61730
61731
61732static ma_result ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
61733{
61734 MA_ASSERT(pDecoder != NULL);
61735 MA_ASSERT(pBufferOut != NULL);
61736
61737 return ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, pBytesRead);
61738}
61739
61740static ma_result ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin)
61741{
61742 MA_ASSERT(pDecoder != NULL);
61743
61744 return ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin);
61745}
61746
61747static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor)
61748{
61749 MA_ASSERT(pDecoder != NULL);
61750
61751 return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor);
61752}
61753
61754static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
61755{
61756 ma_result result;
61757 ma_vfs_file file;
61758
61759 result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder);
61760 if (result != MA_SUCCESS) {
61761 return result;
61762 }
61763
61764 if (pFilePath == NULL || pFilePath[0] == '\0') {
61765 return MA_INVALID_ARGS;
61766 }
61767
61768 result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
61769 if (result != MA_SUCCESS) {
61770 return result;
61771 }
61772
61773 pDecoder->data.vfs.pVFS = pVFS;
61774 pDecoder->data.vfs.file = file;
61775
61776 return MA_SUCCESS;
61777}
61778
61779MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
61780{
61781 ma_result result;
61782 ma_decoder_config config;
61783
61784 config = ma_decoder_config_init_copy(pConfig);
61785 result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder);
61786 if (result != MA_SUCCESS) {
61787 return result;
61788 }
61789
61790 result = MA_NO_BACKEND;
61791
61793 #ifdef MA_HAS_WAV
61794 if (config.encodingFormat == ma_encoding_format_wav) {
61795 result = ma_decoder_init_wav__internal(&config, pDecoder);
61796 }
61797 #endif
61798 #ifdef MA_HAS_FLAC
61800 result = ma_decoder_init_flac__internal(&config, pDecoder);
61801 }
61802 #endif
61803 #ifdef MA_HAS_MP3
61804 if (config.encodingFormat == ma_encoding_format_mp3) {
61805 result = ma_decoder_init_mp3__internal(&config, pDecoder);
61806 }
61807 #endif
61808 #ifdef MA_HAS_VORBIS
61810 result = ma_decoder_init_vorbis__internal(&config, pDecoder);
61811 }
61812 #endif
61813
61814 /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */
61815 if (result != MA_SUCCESS) {
61816 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
61817 }
61818 }
61819
61820 if (result != MA_SUCCESS) {
61821 /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */
61822
61823 /*
61824 We use trial and error to open a decoder. We prioritize custom decoders so that if they
61825 implement the same encoding format they take priority over the built-in decoders.
61826 */
61827 if (result != MA_SUCCESS) {
61828 result = ma_decoder_init_custom__internal(&config, pDecoder);
61829 if (result != MA_SUCCESS) {
61830 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
61831 }
61832 }
61833
61834 /*
61835 If we get to this point and we still haven't found a decoder, and the caller has requested a
61836 specific encoding format, there's no hope for it. Abort.
61837 */
61839 return MA_NO_BACKEND;
61840 }
61841
61842 #ifdef MA_HAS_WAV
61843 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) {
61844 result = ma_decoder_init_wav__internal(&config, pDecoder);
61845 if (result != MA_SUCCESS) {
61846 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
61847 }
61848 }
61849 #endif
61850 #ifdef MA_HAS_FLAC
61851 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) {
61852 result = ma_decoder_init_flac__internal(&config, pDecoder);
61853 if (result != MA_SUCCESS) {
61854 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
61855 }
61856 }
61857 #endif
61858 #ifdef MA_HAS_MP3
61859 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) {
61860 result = ma_decoder_init_mp3__internal(&config, pDecoder);
61861 if (result != MA_SUCCESS) {
61862 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
61863 }
61864 }
61865 #endif
61866 }
61867
61868 /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */
61869 if (result != MA_SUCCESS) {
61870 result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder);
61871 } else {
61872 result = ma_decoder__postinit(&config, pDecoder);
61873 }
61874
61875 if (result != MA_SUCCESS) {
61876 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. */
61877 ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
61878 }
61879
61880 return result;
61881 }
61882
61883 return MA_SUCCESS;
61884}
61885
61886
61887static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
61888{
61889 ma_result result;
61890 ma_vfs_file file;
61891
61892 result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder);
61893 if (result != MA_SUCCESS) {
61894 return result;
61895 }
61896
61897 if (pFilePath == NULL || pFilePath[0] == '\0') {
61898 return MA_INVALID_ARGS;
61899 }
61900
61901 result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
61902 if (result != MA_SUCCESS) {
61903 return result;
61904 }
61905
61906 pDecoder->data.vfs.pVFS = pVFS;
61907 pDecoder->data.vfs.file = file;
61908
61909 return MA_SUCCESS;
61910}
61911
61912MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
61913{
61914 ma_result result;
61915 ma_decoder_config config;
61916
61917 config = ma_decoder_config_init_copy(pConfig);
61918 result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder);
61919 if (result != MA_SUCCESS) {
61920 return result;
61921 }
61922
61923 result = MA_NO_BACKEND;
61924
61926 #ifdef MA_HAS_WAV
61927 if (config.encodingFormat == ma_encoding_format_wav) {
61928 result = ma_decoder_init_wav__internal(&config, pDecoder);
61929 }
61930 #endif
61931 #ifdef MA_HAS_FLAC
61933 result = ma_decoder_init_flac__internal(&config, pDecoder);
61934 }
61935 #endif
61936 #ifdef MA_HAS_MP3
61937 if (config.encodingFormat == ma_encoding_format_mp3) {
61938 result = ma_decoder_init_mp3__internal(&config, pDecoder);
61939 }
61940 #endif
61941 #ifdef MA_HAS_VORBIS
61943 result = ma_decoder_init_vorbis__internal(&config, pDecoder);
61944 }
61945 #endif
61946
61947 /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */
61948 if (result != MA_SUCCESS) {
61949 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
61950 }
61951 }
61952
61953 if (result != MA_SUCCESS) {
61954 /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */
61955
61956 /*
61957 We use trial and error to open a decoder. We prioritize custom decoders so that if they
61958 implement the same encoding format they take priority over the built-in decoders.
61959 */
61960 if (result != MA_SUCCESS) {
61961 result = ma_decoder_init_custom__internal(&config, pDecoder);
61962 if (result != MA_SUCCESS) {
61963 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
61964 }
61965 }
61966
61967 /*
61968 If we get to this point and we still haven't found a decoder, and the caller has requested a
61969 specific encoding format, there's no hope for it. Abort.
61970 */
61972 return MA_NO_BACKEND;
61973 }
61974
61975 #ifdef MA_HAS_WAV
61976 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) {
61977 result = ma_decoder_init_wav__internal(&config, pDecoder);
61978 if (result != MA_SUCCESS) {
61979 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
61980 }
61981 }
61982 #endif
61983 #ifdef MA_HAS_FLAC
61984 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) {
61985 result = ma_decoder_init_flac__internal(&config, pDecoder);
61986 if (result != MA_SUCCESS) {
61987 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
61988 }
61989 }
61990 #endif
61991 #ifdef MA_HAS_MP3
61992 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) {
61993 result = ma_decoder_init_mp3__internal(&config, pDecoder);
61994 if (result != MA_SUCCESS) {
61995 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
61996 }
61997 }
61998 #endif
61999 }
62000
62001 /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */
62002 if (result != MA_SUCCESS) {
62003 result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder);
62004 } else {
62005 result = ma_decoder__postinit(&config, pDecoder);
62006 }
62007
62008 if (result != MA_SUCCESS) {
62009 ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
62010 return result;
62011 }
62012
62013 return MA_SUCCESS;
62014}
62015
62016MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62017{
62018 return ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder);
62019}
62020
62021MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62022{
62023 return ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder);
62024}
62025
62027{
62028 if (pDecoder == NULL) {
62029 return MA_INVALID_ARGS;
62030 }
62031
62032 if (pDecoder->pBackend != NULL) {
62033 if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {
62034 pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks);
62035 }
62036 }
62037
62038 if (pDecoder->onRead == ma_decoder__on_read_vfs) {
62039 ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file);
62040 pDecoder->data.vfs.file = NULL;
62041 }
62042
62044 ma_data_source_uninit(&pDecoder->ds);
62045
62046 if (pDecoder->pInputCache != NULL) {
62047 ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks);
62048 }
62049
62050 return MA_SUCCESS;
62051}
62052
62053MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
62054{
62055 ma_result result = MA_SUCCESS;
62056 ma_uint64 totalFramesReadOut;
62057 void* pRunningFramesOut;
62058
62059 if (pFramesRead != NULL) {
62060 *pFramesRead = 0; /* Safety. */
62061 }
62062
62063 if (frameCount == 0) {
62064 return MA_INVALID_ARGS;
62065 }
62066
62067 if (pDecoder == NULL) {
62068 return MA_INVALID_ARGS;
62069 }
62070
62071 if (pDecoder->pBackend == NULL) {
62072 return MA_INVALID_OPERATION;
62073 }
62074
62075 /* Fast path. */
62076 if (pDecoder->converter.isPassthrough) {
62077 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut);
62078 } else {
62079 /*
62080 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
62081 need to run through each sample because we need to ensure it's internal cache is updated.
62082 */
62083 if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) {
62084 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut);
62085 } else {
62086 /* Slow path. Need to run everything through the data converter. */
62087 ma_format internalFormat;
62088 ma_uint32 internalChannels;
62089
62090 totalFramesReadOut = 0;
62091 pRunningFramesOut = pFramesOut;
62092
62093 result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0);
62094 if (result != MA_SUCCESS) {
62095 return result; /* Failed to retrieve the internal format and channel count. */
62096 }
62097
62098 /*
62099 We run a different path depending on whether or not we are using a heap-allocated
62100 intermediary buffer or not. If the data converter does not support the calculation of
62101 the required number of input frames, we'll use the heap-allocated path. Otherwise we'll
62102 use the stack-allocated path.
62103 */
62104 if (pDecoder->pInputCache != NULL) {
62105 /* We don't have a way of determining the required number of input frames, so need to persistently store input data in a cache. */
62106 while (totalFramesReadOut < frameCount) {
62107 ma_uint64 framesToReadThisIterationIn;
62108 ma_uint64 framesToReadThisIterationOut;
62109
62110 /* If there's any data available in the cache, that needs to get processed first. */
62111 if (pDecoder->inputCacheRemaining > 0) {
62112 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
62113 framesToReadThisIterationIn = framesToReadThisIterationOut;
62114 if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) {
62115 framesToReadThisIterationIn = pDecoder->inputCacheRemaining;
62116 }
62117
62118 result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut);
62119 if (result != MA_SUCCESS) {
62120 break;
62121 }
62122
62123 pDecoder->inputCacheConsumed += framesToReadThisIterationIn;
62124 pDecoder->inputCacheRemaining -= framesToReadThisIterationIn;
62125
62126 totalFramesReadOut += framesToReadThisIterationOut;
62127
62128 if (pRunningFramesOut != NULL) {
62129 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
62130 }
62131
62132 if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) {
62133 break; /* We're done. */
62134 }
62135 }
62136
62137 /* Getting here means there's no data in the cache and we need to fill it up from the data source. */
62138 if (pDecoder->inputCacheRemaining == 0) {
62139 pDecoder->inputCacheConsumed = 0;
62140
62141 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining);
62142 if (result != MA_SUCCESS) {
62143 break;
62144 }
62145 }
62146 }
62147 } else {
62148 /* We have a way of determining the required number of input frames so just use the stack. */
62149 while (totalFramesReadOut < frameCount) {
62150 ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */
62151 ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels);
62152 ma_uint64 framesToReadThisIterationIn;
62153 ma_uint64 framesReadThisIterationIn;
62154 ma_uint64 framesToReadThisIterationOut;
62155 ma_uint64 framesReadThisIterationOut;
62156 ma_uint64 requiredInputFrameCount;
62157
62158 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
62159 framesToReadThisIterationIn = framesToReadThisIterationOut;
62160 if (framesToReadThisIterationIn > intermediaryBufferCap) {
62161 framesToReadThisIterationIn = intermediaryBufferCap;
62162 }
62163
62164 ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount);
62165 if (framesToReadThisIterationIn > requiredInputFrameCount) {
62166 framesToReadThisIterationIn = requiredInputFrameCount;
62167 }
62168
62169 if (requiredInputFrameCount > 0) {
62170 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn);
62171 } else {
62172 framesReadThisIterationIn = 0;
62173 }
62174
62175 /*
62176 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
62177 input frames, we still want to try processing frames because there may some output frames generated from cached input data.
62178 */
62179 framesReadThisIterationOut = framesToReadThisIterationOut;
62180 result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
62181 if (result != MA_SUCCESS) {
62182 break;
62183 }
62184
62185 totalFramesReadOut += framesReadThisIterationOut;
62186
62187 if (pRunningFramesOut != NULL) {
62188 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
62189 }
62190
62191 if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
62192 break; /* We're done. */
62193 }
62194 }
62195 }
62196 }
62197 }
62198
62199 pDecoder->readPointerInPCMFrames += totalFramesReadOut;
62200
62201 if (pFramesRead != NULL) {
62202 *pFramesRead = totalFramesReadOut;
62203 }
62204
62205 if (result == MA_SUCCESS && totalFramesReadOut == 0) {
62206 result = MA_AT_END;
62207 }
62208
62209 return result;
62210}
62211
62213{
62214 if (pDecoder == NULL) {
62215 return MA_INVALID_ARGS;
62216 }
62217
62218 if (pDecoder->pBackend != NULL) {
62219 ma_result result;
62220 ma_uint64 internalFrameIndex;
62221 ma_uint32 internalSampleRate;
62222
62223 result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0);
62224 if (result != MA_SUCCESS) {
62225 return result; /* Failed to retrieve the internal sample rate. */
62226 }
62227
62228 if (internalSampleRate == pDecoder->outputSampleRate) {
62229 internalFrameIndex = frameIndex;
62230 } else {
62231 internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex);
62232 }
62233
62234 result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex);
62235 if (result == MA_SUCCESS) {
62236 pDecoder->readPointerInPCMFrames = frameIndex;
62237 }
62238
62239 return result;
62240 }
62241
62242 /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */
62243 return MA_INVALID_ARGS;
62244}
62245
62246MA_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)
62247{
62248 if (pDecoder == NULL) {
62249 return MA_INVALID_ARGS;
62250 }
62251
62252 if (pFormat != NULL) {
62253 *pFormat = pDecoder->outputFormat;
62254 }
62255
62256 if (pChannels != NULL) {
62257 *pChannels = pDecoder->outputChannels;
62258 }
62259
62260 if (pSampleRate != NULL) {
62261 *pSampleRate = pDecoder->outputSampleRate;
62262 }
62263
62264 if (pChannelMap != NULL) {
62265 ma_data_converter_get_output_channel_map(&pDecoder->converter, pChannelMap, channelMapCap);
62266 }
62267
62268 return MA_SUCCESS;
62269}
62270
62272{
62273 if (pCursor == NULL) {
62274 return MA_INVALID_ARGS;
62275 }
62276
62277 *pCursor = 0;
62278
62279 if (pDecoder == NULL) {
62280 return MA_INVALID_ARGS;
62281 }
62282
62283 *pCursor = pDecoder->readPointerInPCMFrames;
62284
62285 return MA_SUCCESS;
62286}
62287
62289{
62290 if (pLength == NULL) {
62291 return MA_INVALID_ARGS;
62292 }
62293
62294 *pLength = 0;
62295
62296 if (pDecoder == NULL) {
62297 return MA_INVALID_ARGS;
62298 }
62299
62300 if (pDecoder->pBackend != NULL) {
62301 ma_result result;
62302 ma_uint64 internalLengthInPCMFrames;
62303 ma_uint32 internalSampleRate;
62304
62305 result = ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &internalLengthInPCMFrames);
62306 if (result != MA_SUCCESS) {
62307 return result; /* Failed to retrieve the internal length. */
62308 }
62309
62310 result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0);
62311 if (result != MA_SUCCESS) {
62312 return result; /* Failed to retrieve the internal sample rate. */
62313 }
62314
62315 if (internalSampleRate == pDecoder->outputSampleRate) {
62316 *pLength = internalLengthInPCMFrames;
62317 } else {
62318 *pLength = ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, internalLengthInPCMFrames);
62319 }
62320
62321 return MA_SUCCESS;
62322 } else {
62323 return MA_NO_BACKEND;
62324 }
62325}
62326
62328{
62329 ma_result result;
62330 ma_uint64 totalFrameCount;
62331
62332 if (pAvailableFrames == NULL) {
62333 return MA_INVALID_ARGS;
62334 }
62335
62336 *pAvailableFrames = 0;
62337
62338 if (pDecoder == NULL) {
62339 return MA_INVALID_ARGS;
62340 }
62341
62342 result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount);
62343 if (result != MA_SUCCESS) {
62344 return result;
62345 }
62346
62347 if (totalFrameCount <= pDecoder->readPointerInPCMFrames) {
62348 *pAvailableFrames = 0;
62349 } else {
62350 *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames;
62351 }
62352
62353 return MA_SUCCESS;
62354}
62355
62356
62357static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
62358{
62359 ma_result result;
62360 ma_uint64 totalFrameCount;
62361 ma_uint64 bpf;
62362 ma_uint64 dataCapInFrames;
62363 void* pPCMFramesOut;
62364
62365 MA_ASSERT(pDecoder != NULL);
62366
62367 totalFrameCount = 0;
62368 bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
62369
62370 /* The frame count is unknown until we try reading. Thus, we just run in a loop. */
62371 dataCapInFrames = 0;
62372 pPCMFramesOut = NULL;
62373 for (;;) {
62374 ma_uint64 frameCountToTryReading;
62375 ma_uint64 framesJustRead;
62376
62377 /* Make room if there's not enough. */
62378 if (totalFrameCount == dataCapInFrames) {
62379 void* pNewPCMFramesOut;
62380 ma_uint64 newDataCapInFrames = dataCapInFrames*2;
62381 if (newDataCapInFrames == 0) {
62382 newDataCapInFrames = 4096;
62383 }
62384
62385 if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) {
62386 ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);
62387 return MA_TOO_BIG;
62388 }
62389
62390 pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), &pDecoder->allocationCallbacks);
62391 if (pNewPCMFramesOut == NULL) {
62392 ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);
62393 return MA_OUT_OF_MEMORY;
62394 }
62395
62396 dataCapInFrames = newDataCapInFrames;
62397 pPCMFramesOut = pNewPCMFramesOut;
62398 }
62399
62400 frameCountToTryReading = dataCapInFrames - totalFrameCount;
62401 MA_ASSERT(frameCountToTryReading > 0);
62402
62403 result = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading, &framesJustRead);
62404 totalFrameCount += framesJustRead;
62405
62406 if (result != MA_SUCCESS) {
62407 break;
62408 }
62409
62410 if (framesJustRead < frameCountToTryReading) {
62411 break;
62412 }
62413 }
62414
62415
62416 if (pConfigOut != NULL) {
62417 pConfigOut->format = pDecoder->outputFormat;
62418 pConfigOut->channels = pDecoder->outputChannels;
62419 pConfigOut->sampleRate = pDecoder->outputSampleRate;
62420 }
62421
62422 if (ppPCMFramesOut != NULL) {
62423 *ppPCMFramesOut = pPCMFramesOut;
62424 } else {
62425 ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);
62426 }
62427
62428 if (pFrameCountOut != NULL) {
62429 *pFrameCountOut = totalFrameCount;
62430 }
62431
62432 ma_decoder_uninit(pDecoder);
62433 return MA_SUCCESS;
62434}
62435
62436MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
62437{
62438 ma_result result;
62439 ma_decoder_config config;
62440 ma_decoder decoder;
62441
62442 if (pFrameCountOut != NULL) {
62443 *pFrameCountOut = 0;
62444 }
62445 if (ppPCMFramesOut != NULL) {
62446 *ppPCMFramesOut = NULL;
62447 }
62448
62449 config = ma_decoder_config_init_copy(pConfig);
62450
62451 result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder);
62452 if (result != MA_SUCCESS) {
62453 return result;
62454 }
62455
62456 result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
62457
62458 return result;
62459}
62460
62461MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
62462{
62463 return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut);
62464}
62465
62466MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
62467{
62468 ma_decoder_config config;
62469 ma_decoder decoder;
62470 ma_result result;
62471
62472 if (pFrameCountOut != NULL) {
62473 *pFrameCountOut = 0;
62474 }
62475 if (ppPCMFramesOut != NULL) {
62476 *ppPCMFramesOut = NULL;
62477 }
62478
62479 if (pData == NULL || dataSize == 0) {
62480 return MA_INVALID_ARGS;
62481 }
62482
62483 config = ma_decoder_config_init_copy(pConfig);
62484
62485 result = ma_decoder_init_memory(pData, dataSize, &config, &decoder);
62486 if (result != MA_SUCCESS) {
62487 return result;
62488 }
62489
62490 return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
62491}
62492#endif /* MA_NO_DECODING */
62493
62494
62495#ifndef MA_NO_ENCODING
62496
62497#if defined(MA_HAS_WAV)
62498static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite)
62499{
62500 ma_encoder* pEncoder = (ma_encoder*)pUserData;
62501 size_t bytesWritten = 0;
62502
62503 MA_ASSERT(pEncoder != NULL);
62504
62505 pEncoder->onWrite(pEncoder, pData, bytesToWrite, &bytesWritten);
62506 return bytesWritten;
62507}
62508
62509static drwav_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, drwav_seek_origin origin)
62510{
62511 ma_encoder* pEncoder = (ma_encoder*)pUserData;
62512 ma_result result;
62513
62514 MA_ASSERT(pEncoder != NULL);
62515
62516 result = pEncoder->onSeek(pEncoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
62517 if (result != MA_SUCCESS) {
62518 return DRWAV_FALSE;
62519 } else {
62520 return DRWAV_TRUE;
62521 }
62522}
62523
62524static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder)
62525{
62526 drwav_data_format wavFormat;
62527 drwav_allocation_callbacks allocationCallbacks;
62528 drwav* pWav;
62529
62530 MA_ASSERT(pEncoder != NULL);
62531
62532 pWav = (drwav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks);
62533 if (pWav == NULL) {
62534 return MA_OUT_OF_MEMORY;
62535 }
62536
62537 wavFormat.container = drwav_container_riff;
62538 wavFormat.channels = pEncoder->config.channels;
62539 wavFormat.sampleRate = pEncoder->config.sampleRate;
62540 wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8;
62541 if (pEncoder->config.format == ma_format_f32) {
62543 } else {
62544 wavFormat.format = DR_WAVE_FORMAT_PCM;
62545 }
62546
62547 allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData;
62548 allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc;
62549 allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc;
62550 allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree;
62551
62552 if (!drwav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) {
62553 return MA_ERROR;
62554 }
62555
62556 pEncoder->pInternalEncoder = pWav;
62557
62558 return MA_SUCCESS;
62559}
62560
62561static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder)
62562{
62563 drwav* pWav;
62564
62565 MA_ASSERT(pEncoder != NULL);
62566
62567 pWav = (drwav*)pEncoder->pInternalEncoder;
62568 MA_ASSERT(pWav != NULL);
62569
62570 drwav_uninit(pWav);
62571 ma_free(pWav, &pEncoder->config.allocationCallbacks);
62572}
62573
62574static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten)
62575{
62576 drwav* pWav;
62577 ma_uint64 framesWritten;
62578
62579 MA_ASSERT(pEncoder != NULL);
62580
62581 pWav = (drwav*)pEncoder->pInternalEncoder;
62582 MA_ASSERT(pWav != NULL);
62583
62584 framesWritten = drwav_write_pcm_frames(pWav, frameCount, pFramesIn);
62585
62586 if (pFramesWritten != NULL) {
62587 *pFramesWritten = framesWritten;
62588 }
62589
62590 return MA_SUCCESS;
62591}
62592#endif
62593
62595{
62596 ma_encoder_config config;
62597
62598 MA_ZERO_OBJECT(&config);
62599 config.encodingFormat = encodingFormat;
62600 config.format = format;
62601 config.channels = channels;
62602 config.sampleRate = sampleRate;
62603
62604 return config;
62605}
62606
62607MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder)
62608{
62609 ma_result result;
62610
62611 if (pEncoder == NULL) {
62612 return MA_INVALID_ARGS;
62613 }
62614
62615 MA_ZERO_OBJECT(pEncoder);
62616
62617 if (pConfig == NULL) {
62618 return MA_INVALID_ARGS;
62619 }
62620
62621 if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) {
62622 return MA_INVALID_ARGS;
62623 }
62624
62625 pEncoder->config = *pConfig;
62626
62627 result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks);
62628 if (result != MA_SUCCESS) {
62629 return result;
62630 }
62631
62632 return MA_SUCCESS;
62633}
62634
62635MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder)
62636{
62637 ma_result result = MA_SUCCESS;
62638
62639 /* This assumes ma_encoder_preinit() has been called prior. */
62640 MA_ASSERT(pEncoder != NULL);
62641
62642 if (onWrite == NULL || onSeek == NULL) {
62643 return MA_INVALID_ARGS;
62644 }
62645
62646 pEncoder->onWrite = onWrite;
62647 pEncoder->onSeek = onSeek;
62648 pEncoder->pUserData = pUserData;
62649
62650 switch (pEncoder->config.encodingFormat)
62651 {
62653 {
62654 #if defined(MA_HAS_WAV)
62655 pEncoder->onInit = ma_encoder__on_init_wav;
62656 pEncoder->onUninit = ma_encoder__on_uninit_wav;
62657 pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav;
62658 #else
62659 result = MA_NO_BACKEND;
62660 #endif
62661 } break;
62662
62663 default:
62664 {
62665 result = MA_INVALID_ARGS;
62666 } break;
62667 }
62668
62669 /* Getting here means we should have our backend callbacks set up. */
62670 if (result == MA_SUCCESS) {
62671 result = pEncoder->onInit(pEncoder);
62672 }
62673
62674 return result;
62675}
62676
62677static ma_result ma_encoder__on_write_vfs(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten)
62678{
62679 return ma_vfs_or_default_write(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, pBufferIn, bytesToWrite, pBytesWritten);
62680}
62681
62682static ma_result ma_encoder__on_seek_vfs(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin)
62683{
62684 return ma_vfs_or_default_seek(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, offset, origin);
62685}
62686
62687MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
62688{
62689 ma_result result;
62690 ma_vfs_file file;
62691
62692 result = ma_encoder_preinit(pConfig, pEncoder);
62693 if (result != MA_SUCCESS) {
62694 return result;
62695 }
62696
62697 /* Now open the file. If this fails we don't need to uninitialize the encoder. */
62698 result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file);
62699 if (result != MA_SUCCESS) {
62700 return result;
62701 }
62702
62703 pEncoder->data.vfs.pVFS = pVFS;
62704 pEncoder->data.vfs.file = file;
62705
62706 result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder);
62707 if (result != MA_SUCCESS) {
62708 ma_vfs_or_default_close(pVFS, file);
62709 return result;
62710 }
62711
62712 return MA_SUCCESS;
62713}
62714
62715MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
62716{
62717 ma_result result;
62718 ma_vfs_file file;
62719
62720 result = ma_encoder_preinit(pConfig, pEncoder);
62721 if (result != MA_SUCCESS) {
62722 return result;
62723 }
62724
62725 /* Now open the file. If this fails we don't need to uninitialize the encoder. */
62726 result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file);
62727 if (result != MA_SUCCESS) {
62728 return result;
62729 }
62730
62731 pEncoder->data.vfs.pVFS = pVFS;
62732 pEncoder->data.vfs.file = file;
62733
62734 result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder);
62735 if (result != MA_SUCCESS) {
62736 ma_vfs_or_default_close(pVFS, file);
62737 return result;
62738 }
62739
62740 return MA_SUCCESS;
62741}
62742
62743MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
62744{
62745 return ma_encoder_init_vfs(NULL, pFilePath, pConfig, pEncoder);
62746}
62747
62748MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
62749{
62750 return ma_encoder_init_vfs_w(NULL, pFilePath, pConfig, pEncoder);
62751}
62752
62753MA_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)
62754{
62755 ma_result result;
62756
62757 result = ma_encoder_preinit(pConfig, pEncoder);
62758 if (result != MA_SUCCESS) {
62759 return result;
62760 }
62761
62762 return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder);
62763}
62764
62765
62766MA_API void ma_encoder_uninit(ma_encoder* pEncoder)
62767{
62768 if (pEncoder == NULL) {
62769 return;
62770 }
62771
62772 if (pEncoder->onUninit) {
62773 pEncoder->onUninit(pEncoder);
62774 }
62775
62776 /* If we have a file handle, close it. */
62777 if (pEncoder->onWrite == ma_encoder__on_write_vfs) {
62778 ma_vfs_or_default_close(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file);
62779 pEncoder->data.vfs.file = NULL;
62780 }
62781}
62782
62783
62784MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten)
62785{
62786 if (pFramesWritten != NULL) {
62787 *pFramesWritten = 0;
62788 }
62789
62790 if (pEncoder == NULL || pFramesIn == NULL) {
62791 return MA_INVALID_ARGS;
62792 }
62793
62794 return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount, pFramesWritten);
62795}
62796#endif /* MA_NO_ENCODING */
62797
62798
62799
62800
62805#ifndef MA_NO_GENERATION
62806MA_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)
62807{
62808 ma_waveform_config config;
62809
62810 MA_ZERO_OBJECT(&config);
62811 config.format = format;
62812 config.channels = channels;
62813 config.sampleRate = sampleRate;
62814 config.type = type;
62815 config.amplitude = amplitude;
62816 config.frequency = frequency;
62817
62818 return config;
62819}
62820
62821static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
62822{
62823 return ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount, pFramesRead);
62824}
62825
62826static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
62827{
62828 return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex);
62829}
62830
62831static 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)
62832{
62833 ma_waveform* pWaveform = (ma_waveform*)pDataSource;
62834
62835 *pFormat = pWaveform->config.format;
62836 *pChannels = pWaveform->config.channels;
62837 *pSampleRate = pWaveform->config.sampleRate;
62838 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pWaveform->config.channels);
62839
62840 return MA_SUCCESS;
62841}
62842
62843static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
62844{
62845 ma_waveform* pWaveform = (ma_waveform*)pDataSource;
62846
62847 *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance);
62848
62849 return MA_SUCCESS;
62850}
62851
62852static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency)
62853{
62854 return (1.0 / (sampleRate / frequency));
62855}
62856
62857static void ma_waveform__update_advance(ma_waveform* pWaveform)
62858{
62859 pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency);
62860}
62861
62862static ma_data_source_vtable g_ma_waveform_data_source_vtable =
62863{
62864 ma_waveform__data_source_on_read,
62865 ma_waveform__data_source_on_seek,
62866 ma_waveform__data_source_on_get_data_format,
62867 ma_waveform__data_source_on_get_cursor,
62868 NULL, /* onGetLength. There's no notion of a length in waveforms. */
62869 NULL, /* onSetLooping */
62870 0
62871};
62872
62874{
62875 ma_result result;
62876 ma_data_source_config dataSourceConfig;
62877
62878 if (pWaveform == NULL) {
62879 return MA_INVALID_ARGS;
62880 }
62881
62882 MA_ZERO_OBJECT(pWaveform);
62883
62884 dataSourceConfig = ma_data_source_config_init();
62885 dataSourceConfig.vtable = &g_ma_waveform_data_source_vtable;
62886
62887 result = ma_data_source_init(&dataSourceConfig, &pWaveform->ds);
62888 if (result != MA_SUCCESS) {
62889 return result;
62890 }
62891
62892 pWaveform->config = *pConfig;
62893 pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency);
62894 pWaveform->time = 0;
62895
62896 return MA_SUCCESS;
62897}
62898
62899MA_API void ma_waveform_uninit(ma_waveform* pWaveform)
62900{
62901 if (pWaveform == NULL) {
62902 return;
62903 }
62904
62905 ma_data_source_uninit(&pWaveform->ds);
62906}
62907
62908MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude)
62909{
62910 if (pWaveform == NULL) {
62911 return MA_INVALID_ARGS;
62912 }
62913
62914 pWaveform->config.amplitude = amplitude;
62915 return MA_SUCCESS;
62916}
62917
62918MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency)
62919{
62920 if (pWaveform == NULL) {
62921 return MA_INVALID_ARGS;
62922 }
62923
62924 pWaveform->config.frequency = frequency;
62925 ma_waveform__update_advance(pWaveform);
62926
62927 return MA_SUCCESS;
62928}
62929
62931{
62932 if (pWaveform == NULL) {
62933 return MA_INVALID_ARGS;
62934 }
62935
62936 pWaveform->config.type = type;
62937 return MA_SUCCESS;
62938}
62939
62941{
62942 if (pWaveform == NULL) {
62943 return MA_INVALID_ARGS;
62944 }
62945
62946 pWaveform->config.sampleRate = sampleRate;
62947 ma_waveform__update_advance(pWaveform);
62948
62949 return MA_SUCCESS;
62950}
62951
62952static float ma_waveform_sine_f32(double time, double amplitude)
62953{
62954 return (float)(ma_sind(MA_TAU_D * time) * amplitude);
62955}
62956
62957static ma_int16 ma_waveform_sine_s16(double time, double amplitude)
62958{
62959 return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude));
62960}
62961
62962static float ma_waveform_square_f32(double time, double amplitude)
62963{
62964 double f = time - (ma_int64)time;
62965 double r;
62966
62967 if (f < 0.5) {
62968 r = amplitude;
62969 } else {
62970 r = -amplitude;
62971 }
62972
62973 return (float)r;
62974}
62975
62976static ma_int16 ma_waveform_square_s16(double time, double amplitude)
62977{
62978 return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, amplitude));
62979}
62980
62981static float ma_waveform_triangle_f32(double time, double amplitude)
62982{
62983 double f = time - (ma_int64)time;
62984 double r;
62985
62986 r = 2 * ma_abs(2 * (f - 0.5)) - 1;
62987
62988 return (float)(r * amplitude);
62989}
62990
62991static ma_int16 ma_waveform_triangle_s16(double time, double amplitude)
62992{
62993 return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude));
62994}
62995
62996static float ma_waveform_sawtooth_f32(double time, double amplitude)
62997{
62998 double f = time - (ma_int64)time;
62999 double r;
63000
63001 r = 2 * (f - 0.5);
63002
63003 return (float)(r * amplitude);
63004}
63005
63006static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude)
63007{
63008 return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude));
63009}
63010
63011static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
63012{
63013 ma_uint64 iFrame;
63014 ma_uint64 iChannel;
63016 ma_uint32 bpf = bps * pWaveform->config.channels;
63017
63018 MA_ASSERT(pWaveform != NULL);
63019 MA_ASSERT(pFramesOut != NULL);
63020
63021 if (pWaveform->config.format == ma_format_f32) {
63022 float* pFramesOutF32 = (float*)pFramesOut;
63023 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63024 float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude);
63025 pWaveform->time += pWaveform->advance;
63026
63027 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
63028 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
63029 }
63030 }
63031 } else if (pWaveform->config.format == ma_format_s16) {
63032 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
63033 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63034 ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude);
63035 pWaveform->time += pWaveform->advance;
63036
63037 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
63038 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
63039 }
63040 }
63041 } else {
63042 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63043 float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude);
63044 pWaveform->time += pWaveform->advance;
63045
63046 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
63047 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
63048 }
63049 }
63050 }
63051}
63052
63053static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
63054{
63055 ma_uint64 iFrame;
63056 ma_uint64 iChannel;
63058 ma_uint32 bpf = bps * pWaveform->config.channels;
63059
63060 MA_ASSERT(pWaveform != NULL);
63061 MA_ASSERT(pFramesOut != NULL);
63062
63063 if (pWaveform->config.format == ma_format_f32) {
63064 float* pFramesOutF32 = (float*)pFramesOut;
63065 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63066 float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude);
63067 pWaveform->time += pWaveform->advance;
63068
63069 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
63070 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
63071 }
63072 }
63073 } else if (pWaveform->config.format == ma_format_s16) {
63074 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
63075 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63076 ma_int16 s = ma_waveform_square_s16(pWaveform->time, pWaveform->config.amplitude);
63077 pWaveform->time += pWaveform->advance;
63078
63079 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
63080 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
63081 }
63082 }
63083 } else {
63084 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63085 float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude);
63086 pWaveform->time += pWaveform->advance;
63087
63088 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
63089 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
63090 }
63091 }
63092 }
63093}
63094
63095static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
63096{
63097 ma_uint64 iFrame;
63098 ma_uint64 iChannel;
63100 ma_uint32 bpf = bps * pWaveform->config.channels;
63101
63102 MA_ASSERT(pWaveform != NULL);
63103 MA_ASSERT(pFramesOut != NULL);
63104
63105 if (pWaveform->config.format == ma_format_f32) {
63106 float* pFramesOutF32 = (float*)pFramesOut;
63107 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63108 float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude);
63109 pWaveform->time += pWaveform->advance;
63110
63111 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
63112 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
63113 }
63114 }
63115 } else if (pWaveform->config.format == ma_format_s16) {
63116 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
63117 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63118 ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude);
63119 pWaveform->time += pWaveform->advance;
63120
63121 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
63122 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
63123 }
63124 }
63125 } else {
63126 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63127 float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude);
63128 pWaveform->time += pWaveform->advance;
63129
63130 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
63131 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
63132 }
63133 }
63134 }
63135}
63136
63137static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
63138{
63139 ma_uint64 iFrame;
63140 ma_uint64 iChannel;
63142 ma_uint32 bpf = bps * pWaveform->config.channels;
63143
63144 MA_ASSERT(pWaveform != NULL);
63145 MA_ASSERT(pFramesOut != NULL);
63146
63147 if (pWaveform->config.format == ma_format_f32) {
63148 float* pFramesOutF32 = (float*)pFramesOut;
63149 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63150 float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude);
63151 pWaveform->time += pWaveform->advance;
63152
63153 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
63154 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
63155 }
63156 }
63157 } else if (pWaveform->config.format == ma_format_s16) {
63158 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
63159 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63160 ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude);
63161 pWaveform->time += pWaveform->advance;
63162
63163 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
63164 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
63165 }
63166 }
63167 } else {
63168 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63169 float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude);
63170 pWaveform->time += pWaveform->advance;
63171
63172 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
63173 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
63174 }
63175 }
63176 }
63177}
63178
63179MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
63180{
63181 if (pFramesRead != NULL) {
63182 *pFramesRead = 0;
63183 }
63184
63185 if (frameCount == 0) {
63186 return MA_INVALID_ARGS;
63187 }
63188
63189 if (pWaveform == NULL) {
63190 return MA_INVALID_ARGS;
63191 }
63192
63193 if (pFramesOut != NULL) {
63194 switch (pWaveform->config.type)
63195 {
63197 {
63198 ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount);
63199 } break;
63200
63202 {
63203 ma_waveform_read_pcm_frames__square(pWaveform, pFramesOut, frameCount);
63204 } break;
63205
63207 {
63208 ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount);
63209 } break;
63210
63212 {
63213 ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount);
63214 } break;
63215
63216 default: return MA_INVALID_OPERATION; /* Unknown waveform type. */
63217 }
63218 } else {
63219 pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */
63220 }
63221
63222 if (pFramesRead != NULL) {
63223 *pFramesRead = frameCount;
63224 }
63225
63226 return MA_SUCCESS;
63227}
63228
63230{
63231 if (pWaveform == NULL) {
63232 return MA_INVALID_ARGS;
63233 }
63234
63235 pWaveform->time = pWaveform->advance * (ma_int64)frameIndex; /* Casting for VC6. Won't be an issue in practice. */
63236
63237 return MA_SUCCESS;
63238}
63239
63240
63241MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude)
63242{
63243 ma_noise_config config;
63244 MA_ZERO_OBJECT(&config);
63245
63246 config.format = format;
63247 config.channels = channels;
63248 config.type = type;
63249 config.seed = seed;
63250 config.amplitude = amplitude;
63251
63252 if (config.seed == 0) {
63253 config.seed = MA_DEFAULT_LCG_SEED;
63254 }
63255
63256 return config;
63257}
63258
63259
63260static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
63261{
63262 return ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount, pFramesRead);
63263}
63264
63265static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
63266{
63267 /* No-op. Just pretend to be successful. */
63268 (void)pDataSource;
63269 (void)frameIndex;
63270 return MA_SUCCESS;
63271}
63272
63273static 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)
63274{
63275 ma_noise* pNoise = (ma_noise*)pDataSource;
63276
63277 *pFormat = pNoise->config.format;
63278 *pChannels = pNoise->config.channels;
63279 *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */
63281
63282 return MA_SUCCESS;
63283}
63284
63285static ma_data_source_vtable g_ma_noise_data_source_vtable =
63286{
63287 ma_noise__data_source_on_read,
63288 ma_noise__data_source_on_seek, /* No-op for noise. */
63289 ma_noise__data_source_on_get_data_format,
63290 NULL, /* onGetCursor. No notion of a cursor for noise. */
63291 NULL, /* onGetLength. No notion of a length for noise. */
63292 NULL, /* onSetLooping */
63293 0
63294};
63295
63296
63297#ifndef MA_PINK_NOISE_BIN_SIZE
63298#define MA_PINK_NOISE_BIN_SIZE 16
63299#endif
63300
63301typedef struct
63302{
63303 size_t sizeInBytes;
63304 struct
63305 {
63306 size_t binOffset;
63307 size_t accumulationOffset;
63308 size_t counterOffset;
63309 } pink;
63310 struct
63311 {
63312 size_t accumulationOffset;
63313 } brownian;
63314} ma_noise_heap_layout;
63315
63316static ma_result ma_noise_get_heap_layout(const ma_noise_config* pConfig, ma_noise_heap_layout* pHeapLayout)
63317{
63318 MA_ASSERT(pHeapLayout != NULL);
63319
63320 MA_ZERO_OBJECT(pHeapLayout);
63321
63322 if (pConfig == NULL) {
63323 return MA_INVALID_ARGS;
63324 }
63325
63326 if (pConfig->channels == 0) {
63327 return MA_INVALID_ARGS;
63328 }
63329
63330 pHeapLayout->sizeInBytes = 0;
63331
63332 /* Pink. */
63333 if (pConfig->type == ma_noise_type_pink) {
63334 /* bin */
63335 pHeapLayout->pink.binOffset = pHeapLayout->sizeInBytes;
63336 pHeapLayout->sizeInBytes += sizeof(double*) * pConfig->channels;
63337 pHeapLayout->sizeInBytes += sizeof(double ) * pConfig->channels * MA_PINK_NOISE_BIN_SIZE;
63338
63339 /* accumulation */
63340 pHeapLayout->pink.accumulationOffset = pHeapLayout->sizeInBytes;
63341 pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels;
63342
63343 /* counter */
63344 pHeapLayout->pink.counterOffset = pHeapLayout->sizeInBytes;
63345 pHeapLayout->sizeInBytes += sizeof(ma_uint32) * pConfig->channels;
63346 }
63347
63348 /* Brownian. */
63349 if (pConfig->type == ma_noise_type_brownian) {
63350 /* accumulation */
63351 pHeapLayout->brownian.accumulationOffset = pHeapLayout->sizeInBytes;
63352 pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels;
63353 }
63354
63355 /* Make sure allocation size is aligned. */
63356 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
63357
63358 return MA_SUCCESS;
63359}
63360
63361MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes)
63362{
63363 ma_result result;
63364 ma_noise_heap_layout heapLayout;
63365
63366 if (pHeapSizeInBytes == NULL) {
63367 return MA_INVALID_ARGS;
63368 }
63369
63370 *pHeapSizeInBytes = 0;
63371
63372 result = ma_noise_get_heap_layout(pConfig, &heapLayout);
63373 if (result != MA_SUCCESS) {
63374 return result;
63375 }
63376
63377 *pHeapSizeInBytes = heapLayout.sizeInBytes;
63378
63379 return MA_SUCCESS;
63380}
63381
63382MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise)
63383{
63384 ma_result result;
63385 ma_noise_heap_layout heapLayout;
63386 ma_data_source_config dataSourceConfig;
63387 ma_uint32 iChannel;
63388
63389 if (pNoise == NULL) {
63390 return MA_INVALID_ARGS;
63391 }
63392
63393 MA_ZERO_OBJECT(pNoise);
63394
63395 result = ma_noise_get_heap_layout(pConfig, &heapLayout);
63396 if (result != MA_SUCCESS) {
63397 return result;
63398 }
63399
63400 pNoise->_pHeap = pHeap;
63401 MA_ZERO_MEMORY(pNoise->_pHeap, heapLayout.sizeInBytes);
63402
63403 dataSourceConfig = ma_data_source_config_init();
63404 dataSourceConfig.vtable = &g_ma_noise_data_source_vtable;
63405
63406 result = ma_data_source_init(&dataSourceConfig, &pNoise->ds);
63407 if (result != MA_SUCCESS) {
63408 return result;
63409 }
63410
63411 pNoise->config = *pConfig;
63412 ma_lcg_seed(&pNoise->lcg, pConfig->seed);
63413
63414 if (pNoise->config.type == ma_noise_type_pink) {
63415 pNoise->state.pink.bin = (double** )ma_offset_ptr(pHeap, heapLayout.pink.binOffset);
63416 pNoise->state.pink.accumulation = (double* )ma_offset_ptr(pHeap, heapLayout.pink.accumulationOffset);
63417 pNoise->state.pink.counter = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.pink.counterOffset);
63418
63419 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
63420 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));
63421 pNoise->state.pink.accumulation[iChannel] = 0;
63422 pNoise->state.pink.counter[iChannel] = 1;
63423 }
63424 }
63425
63426 if (pNoise->config.type == ma_noise_type_brownian) {
63427 pNoise->state.brownian.accumulation = (double*)ma_offset_ptr(pHeap, heapLayout.brownian.accumulationOffset);
63428
63429 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
63430 pNoise->state.brownian.accumulation[iChannel] = 0;
63431 }
63432 }
63433
63434 return MA_SUCCESS;
63435}
63436
63437MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise)
63438{
63439 ma_result result;
63440 size_t heapSizeInBytes;
63441 void* pHeap;
63442
63443 result = ma_noise_get_heap_size(pConfig, &heapSizeInBytes);
63444 if (result != MA_SUCCESS) {
63445 return result;
63446 }
63447
63448 if (heapSizeInBytes > 0) {
63449 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
63450 if (pHeap == NULL) {
63451 return MA_OUT_OF_MEMORY;
63452 }
63453 } else {
63454 pHeap = NULL;
63455 }
63456
63457 result = ma_noise_init_preallocated(pConfig, pHeap, pNoise);
63458 if (result != MA_SUCCESS) {
63459 ma_free(pHeap, pAllocationCallbacks);
63460 return result;
63461 }
63462
63463 pNoise->_ownsHeap = MA_TRUE;
63464 return MA_SUCCESS;
63465}
63466
63467MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks)
63468{
63469 if (pNoise == NULL) {
63470 return;
63471 }
63472
63473 ma_data_source_uninit(&pNoise->ds);
63474
63475 if (pNoise->_ownsHeap) {
63476 ma_free(pNoise->_pHeap, pAllocationCallbacks);
63477 }
63478}
63479
63480MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude)
63481{
63482 if (pNoise == NULL) {
63483 return MA_INVALID_ARGS;
63484 }
63485
63486 pNoise->config.amplitude = amplitude;
63487 return MA_SUCCESS;
63488}
63489
63491{
63492 if (pNoise == NULL) {
63493 return MA_INVALID_ARGS;
63494 }
63495
63496 pNoise->lcg.state = seed;
63497 return MA_SUCCESS;
63498}
63499
63500
63502{
63503 if (pNoise == NULL) {
63504 return MA_INVALID_ARGS;
63505 }
63506
63507 pNoise->config.type = type;
63508 return MA_SUCCESS;
63509}
63510
63511static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise)
63512{
63513 return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude);
63514}
63515
63516static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise)
63517{
63518 return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise));
63519}
63520
63521static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
63522{
63523 ma_uint64 iFrame;
63524 ma_uint32 iChannel;
63525 const ma_uint32 channels = pNoise->config.channels;
63526 MA_ASSUME(channels > 0);
63527
63528 if (pNoise->config.format == ma_format_f32) {
63529 float* pFramesOutF32 = (float*)pFramesOut;
63530 if (pNoise->config.duplicateChannels) {
63531 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63532 float s = ma_noise_f32_white(pNoise);
63533 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63534 pFramesOutF32[iFrame*channels + iChannel] = s;
63535 }
63536 }
63537 } else {
63538 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63539 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63540 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise);
63541 }
63542 }
63543 }
63544 } else if (pNoise->config.format == ma_format_s16) {
63545 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
63546 if (pNoise->config.duplicateChannels) {
63547 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63548 ma_int16 s = ma_noise_s16_white(pNoise);
63549 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63550 pFramesOutS16[iFrame*channels + iChannel] = s;
63551 }
63552 }
63553 } else {
63554 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63555 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63556 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise);
63557 }
63558 }
63559 }
63560 } else {
63561 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
63562 const ma_uint32 bpf = bps * channels;
63563
63564 if (pNoise->config.duplicateChannels) {
63565 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63566 float s = ma_noise_f32_white(pNoise);
63567 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63568 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
63569 }
63570 }
63571 } else {
63572 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63573 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63574 float s = ma_noise_f32_white(pNoise);
63575 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
63576 }
63577 }
63578 }
63579 }
63580
63581 return frameCount;
63582}
63583
63584
63585static MA_INLINE unsigned int ma_tzcnt32(unsigned int x)
63586{
63587 unsigned int n;
63588
63589 /* Special case for odd numbers since they should happen about half the time. */
63590 if (x & 0x1) {
63591 return 0;
63592 }
63593
63594 if (x == 0) {
63595 return sizeof(x) << 3;
63596 }
63597
63598 n = 1;
63599 if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; }
63600 if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; }
63601 if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; }
63602 if ((x & 0x00000003) == 0) { x >>= 2; n += 2; }
63603 n -= x & 0x00000001;
63604
63605 return n;
63606}
63607
63608/*
63609Pink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h
63610
63611This is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/
63612*/
63613static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel)
63614{
63615 double result;
63616 double binPrev;
63617 double binNext;
63618 unsigned int ibin;
63619
63620 ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (MA_PINK_NOISE_BIN_SIZE - 1);
63621
63622 binPrev = pNoise->state.pink.bin[iChannel][ibin];
63623 binNext = ma_lcg_rand_f64(&pNoise->lcg);
63624 pNoise->state.pink.bin[iChannel][ibin] = binNext;
63625
63626 pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev);
63627 pNoise->state.pink.counter[iChannel] += 1;
63628
63629 result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]);
63630 result /= 10;
63631
63632 return (float)(result * pNoise->config.amplitude);
63633}
63634
63635static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel)
63636{
63637 return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel));
63638}
63639
63640static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
63641{
63642 ma_uint64 iFrame;
63643 ma_uint32 iChannel;
63644 const ma_uint32 channels = pNoise->config.channels;
63645 MA_ASSUME(channels > 0);
63646
63647 if (pNoise->config.format == ma_format_f32) {
63648 float* pFramesOutF32 = (float*)pFramesOut;
63649 if (pNoise->config.duplicateChannels) {
63650 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63651 float s = ma_noise_f32_pink(pNoise, 0);
63652 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63653 pFramesOutF32[iFrame*channels + iChannel] = s;
63654 }
63655 }
63656 } else {
63657 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63658 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63659 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel);
63660 }
63661 }
63662 }
63663 } else if (pNoise->config.format == ma_format_s16) {
63664 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
63665 if (pNoise->config.duplicateChannels) {
63666 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63667 ma_int16 s = ma_noise_s16_pink(pNoise, 0);
63668 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63669 pFramesOutS16[iFrame*channels + iChannel] = s;
63670 }
63671 }
63672 } else {
63673 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63674 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63675 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel);
63676 }
63677 }
63678 }
63679 } else {
63680 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
63681 const ma_uint32 bpf = bps * channels;
63682
63683 if (pNoise->config.duplicateChannels) {
63684 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63685 float s = ma_noise_f32_pink(pNoise, 0);
63686 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63687 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
63688 }
63689 }
63690 } else {
63691 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63692 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63693 float s = ma_noise_f32_pink(pNoise, iChannel);
63694 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
63695 }
63696 }
63697 }
63698 }
63699
63700 return frameCount;
63701}
63702
63703
63704static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel)
63705{
63706 double result;
63707
63708 result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]);
63709 result /= 1.005; /* Don't escape the -1..1 range on average. */
63710
63711 pNoise->state.brownian.accumulation[iChannel] = result;
63712 result /= 20;
63713
63714 return (float)(result * pNoise->config.amplitude);
63715}
63716
63717static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel)
63718{
63719 return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel));
63720}
63721
63722static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
63723{
63724 ma_uint64 iFrame;
63725 ma_uint32 iChannel;
63726 const ma_uint32 channels = pNoise->config.channels;
63727 MA_ASSUME(channels > 0);
63728
63729 if (pNoise->config.format == ma_format_f32) {
63730 float* pFramesOutF32 = (float*)pFramesOut;
63731 if (pNoise->config.duplicateChannels) {
63732 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63733 float s = ma_noise_f32_brownian(pNoise, 0);
63734 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63735 pFramesOutF32[iFrame*channels + iChannel] = s;
63736 }
63737 }
63738 } else {
63739 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63740 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63741 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel);
63742 }
63743 }
63744 }
63745 } else if (pNoise->config.format == ma_format_s16) {
63746 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
63747 if (pNoise->config.duplicateChannels) {
63748 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63749 ma_int16 s = ma_noise_s16_brownian(pNoise, 0);
63750 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63751 pFramesOutS16[iFrame*channels + iChannel] = s;
63752 }
63753 }
63754 } else {
63755 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63756 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63757 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel);
63758 }
63759 }
63760 }
63761 } else {
63762 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
63763 const ma_uint32 bpf = bps * channels;
63764
63765 if (pNoise->config.duplicateChannels) {
63766 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63767 float s = ma_noise_f32_brownian(pNoise, 0);
63768 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63769 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
63770 }
63771 }
63772 } else {
63773 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
63774 for (iChannel = 0; iChannel < channels; iChannel += 1) {
63775 float s = ma_noise_f32_brownian(pNoise, iChannel);
63776 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
63777 }
63778 }
63779 }
63780 }
63781
63782 return frameCount;
63783}
63784
63785MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
63786{
63787 ma_uint64 framesRead = 0;
63788
63789 if (pFramesRead != NULL) {
63790 *pFramesRead = 0;
63791 }
63792
63793 if (frameCount == 0) {
63794 return MA_INVALID_ARGS;
63795 }
63796
63797 if (pNoise == NULL) {
63798 return MA_INVALID_ARGS;
63799 }
63800
63801 /* 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. */
63802 if (pFramesOut == NULL) {
63803 framesRead = frameCount;
63804 } else {
63805 switch (pNoise->config.type) {
63806 case ma_noise_type_white: framesRead = ma_noise_read_pcm_frames__white (pNoise, pFramesOut, frameCount); break;
63807 case ma_noise_type_pink: framesRead = ma_noise_read_pcm_frames__pink (pNoise, pFramesOut, frameCount); break;
63808 case ma_noise_type_brownian: framesRead = ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); break;
63809 default: return MA_INVALID_OPERATION; /* Unknown noise type. */
63810 }
63811 }
63812
63813 if (pFramesRead != NULL) {
63814 *pFramesRead = framesRead;
63815 }
63816
63817 return MA_SUCCESS;
63818}
63819#endif /* MA_NO_GENERATION */
63820
63821
63822
63823#ifndef MA_NO_RESOURCE_MANAGER
63824#ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS
63825#define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 1000
63826#endif
63827
63828#ifndef MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY
63829#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY 1024
63830#endif
63831
63833{
63835
63836 MA_ZERO_OBJECT(&notifications);
63837
63838 return notifications;
63839}
63840
63841static void ma_resource_manager_pipeline_notifications_signal_all_notifications(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)
63842{
63843 if (pPipelineNotifications == NULL) {
63844 return;
63845 }
63846
63847 if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); }
63848 if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); }
63849}
63850
63851static void ma_resource_manager_pipeline_notifications_acquire_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)
63852{
63853 if (pPipelineNotifications == NULL) {
63854 return;
63855 }
63856
63857 if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); }
63858 if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); }
63859}
63860
63861static void ma_resource_manager_pipeline_notifications_release_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)
63862{
63863 if (pPipelineNotifications == NULL) {
63864 return;
63865 }
63866
63867 if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); }
63868 if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); }
63869}
63870
63871
63872
63873#ifndef MA_DEFAULT_HASH_SEED
63874#define MA_DEFAULT_HASH_SEED 42
63875#endif
63876
63877/* MurmurHash3. Based on code from https://github.com/PeterScott/murmur3/blob/master/murmur3.c (public domain). */
63878#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
63879 #pragma GCC diagnostic push
63880 #if __GNUC__ >= 7
63881 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
63882 #endif
63883#endif
63884
63885static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r)
63886{
63887 return (x << r) | (x >> (32 - r));
63888}
63889
63890static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i)
63891{
63892 if (ma_is_little_endian()) {
63893 return blocks[i];
63894 } else {
63895 return ma_swap_endian_uint32(blocks[i]);
63896 }
63897}
63898
63899static MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h)
63900{
63901 h ^= h >> 16;
63902 h *= 0x85ebca6b;
63903 h ^= h >> 13;
63904 h *= 0xc2b2ae35;
63905 h ^= h >> 16;
63906
63907 return h;
63908}
63909
63910static ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed)
63911{
63912 const ma_uint8* data = (const ma_uint8*)key;
63913 const ma_uint32* blocks;
63914 const ma_uint8* tail;
63915 const int nblocks = len / 4;
63916 ma_uint32 h1 = seed;
63917 ma_uint32 c1 = 0xcc9e2d51;
63918 ma_uint32 c2 = 0x1b873593;
63919 ma_uint32 k1;
63920 int i;
63921
63922 blocks = (const ma_uint32 *)(data + nblocks*4);
63923
63924 for(i = -nblocks; i; i++) {
63925 k1 = ma_hash_getblock(blocks,i);
63926
63927 k1 *= c1;
63928 k1 = ma_rotl32(k1, 15);
63929 k1 *= c2;
63930
63931 h1 ^= k1;
63932 h1 = ma_rotl32(h1, 13);
63933 h1 = h1*5 + 0xe6546b64;
63934 }
63935
63936
63937 tail = (const ma_uint8*)(data + nblocks*4);
63938
63939 k1 = 0;
63940 switch(len & 3) {
63941 case 3: k1 ^= tail[2] << 16;
63942 case 2: k1 ^= tail[1] << 8;
63943 case 1: k1 ^= tail[0];
63944 k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1;
63945 };
63946
63947
63948 h1 ^= len;
63949 h1 = ma_hash_fmix32(h1);
63950
63951 return h1;
63952}
63953
63954#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
63955 #pragma GCC diagnostic push
63956#endif
63957/* End MurmurHash3 */
63958
63959static ma_uint32 ma_hash_string_32(const char* str)
63960{
63961 return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED);
63962}
63963
63964static ma_uint32 ma_hash_string_w_32(const wchar_t* str)
63965{
63966 return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED);
63967}
63968
63969
63970
63971
63972/*
63973Basic BST Functions
63974*/
63975static ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode)
63976{
63978
63979 MA_ASSERT(pResourceManager != NULL);
63980 MA_ASSERT(ppDataBufferNode != NULL);
63981
63982 pCurrentNode = pResourceManager->pRootDataBufferNode;
63983 while (pCurrentNode != NULL) {
63984 if (hashedName32 == pCurrentNode->hashedName32) {
63985 break; /* Found. */
63986 } else if (hashedName32 < pCurrentNode->hashedName32) {
63987 pCurrentNode = pCurrentNode->pChildLo;
63988 } else {
63989 pCurrentNode = pCurrentNode->pChildHi;
63990 }
63991 }
63992
63993 *ppDataBufferNode = pCurrentNode;
63994
63995 if (pCurrentNode == NULL) {
63996 return MA_DOES_NOT_EXIST;
63997 } else {
63998 return MA_SUCCESS;
63999 }
64000}
64001
64002static ma_result ma_resource_manager_data_buffer_node_insert_point(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppInsertPoint)
64003{
64004 ma_result result = MA_SUCCESS;
64006
64007 MA_ASSERT(pResourceManager != NULL);
64008 MA_ASSERT(ppInsertPoint != NULL);
64009
64010 *ppInsertPoint = NULL;
64011
64012 if (pResourceManager->pRootDataBufferNode == NULL) {
64013 return MA_SUCCESS; /* No items. */
64014 }
64015
64016 /* 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. */
64017 pCurrentNode = pResourceManager->pRootDataBufferNode;
64018 while (pCurrentNode != NULL) {
64019 if (hashedName32 == pCurrentNode->hashedName32) {
64020 result = MA_ALREADY_EXISTS;
64021 break;
64022 } else {
64023 if (hashedName32 < pCurrentNode->hashedName32) {
64024 if (pCurrentNode->pChildLo == NULL) {
64025 result = MA_SUCCESS;
64026 break;
64027 } else {
64028 pCurrentNode = pCurrentNode->pChildLo;
64029 }
64030 } else {
64031 if (pCurrentNode->pChildHi == NULL) {
64032 result = MA_SUCCESS;
64033 break;
64034 } else {
64035 pCurrentNode = pCurrentNode->pChildHi;
64036 }
64037 }
64038 }
64039 }
64040
64041 *ppInsertPoint = pCurrentNode;
64042 return result;
64043}
64044
64045static 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)
64046{
64047 MA_ASSERT(pResourceManager != NULL);
64048 MA_ASSERT(pDataBufferNode != NULL);
64049
64050 /* The key must have been set before calling this function. */
64051 MA_ASSERT(pDataBufferNode->hashedName32 != 0);
64052
64053 if (pInsertPoint == NULL) {
64054 /* It's the first node. */
64055 pResourceManager->pRootDataBufferNode = pDataBufferNode;
64056 } else {
64057 /* It's not the first node. It needs to be inserted. */
64058 if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) {
64059 MA_ASSERT(pInsertPoint->pChildLo == NULL);
64060 pInsertPoint->pChildLo = pDataBufferNode;
64061 } else {
64062 MA_ASSERT(pInsertPoint->pChildHi == NULL);
64063 pInsertPoint->pChildHi = pDataBufferNode;
64064 }
64065 }
64066
64067 pDataBufferNode->pParent = pInsertPoint;
64068
64069 return MA_SUCCESS;
64070}
64071
64072#if 0 /* Unused for now. */
64073static ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
64074{
64075 ma_result result;
64077
64078 MA_ASSERT(pResourceManager != NULL);
64079 MA_ASSERT(pDataBufferNode != NULL);
64080
64081 result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint);
64082 if (result != MA_SUCCESS) {
64083 return MA_INVALID_ARGS;
64084 }
64085
64086 return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint);
64087}
64088#endif
64089
64090static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode)
64091{
64093
64094 MA_ASSERT(pDataBufferNode != NULL);
64095
64096 pCurrentNode = pDataBufferNode;
64097 while (pCurrentNode->pChildLo != NULL) {
64098 pCurrentNode = pCurrentNode->pChildLo;
64099 }
64100
64101 return pCurrentNode;
64102}
64103
64104static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode)
64105{
64107
64108 MA_ASSERT(pDataBufferNode != NULL);
64109
64110 pCurrentNode = pDataBufferNode;
64111 while (pCurrentNode->pChildHi != NULL) {
64112 pCurrentNode = pCurrentNode->pChildHi;
64113 }
64114
64115 return pCurrentNode;
64116}
64117
64118static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_successor(ma_resource_manager_data_buffer_node* pDataBufferNode)
64119{
64120 MA_ASSERT(pDataBufferNode != NULL);
64121 MA_ASSERT(pDataBufferNode->pChildHi != NULL);
64122
64123 return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi);
64124}
64125
64126static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode)
64127{
64128 MA_ASSERT(pDataBufferNode != NULL);
64129 MA_ASSERT(pDataBufferNode->pChildLo != NULL);
64130
64131 return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo);
64132}
64133
64134static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
64135{
64136 MA_ASSERT(pResourceManager != NULL);
64137 MA_ASSERT(pDataBufferNode != NULL);
64138
64139 if (pDataBufferNode->pChildLo == NULL) {
64140 if (pDataBufferNode->pChildHi == NULL) {
64141 /* Simple case - deleting a buffer with no children. */
64142 if (pDataBufferNode->pParent == NULL) {
64143 MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); /* There is only a single buffer in the tree which should be equal to the root node. */
64144 pResourceManager->pRootDataBufferNode = NULL;
64145 } else {
64146 if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
64147 pDataBufferNode->pParent->pChildLo = NULL;
64148 } else {
64149 pDataBufferNode->pParent->pChildHi = NULL;
64150 }
64151 }
64152 } else {
64153 /* Node has one child - pChildHi != NULL. */
64154 pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent;
64155
64156 if (pDataBufferNode->pParent == NULL) {
64157 MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);
64158 pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi;
64159 } else {
64160 if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
64161 pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi;
64162 } else {
64163 pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi;
64164 }
64165 }
64166 }
64167 } else {
64168 if (pDataBufferNode->pChildHi == NULL) {
64169 /* Node has one child - pChildLo != NULL. */
64170 pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent;
64171
64172 if (pDataBufferNode->pParent == NULL) {
64173 MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);
64174 pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo;
64175 } else {
64176 if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
64177 pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo;
64178 } else {
64179 pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo;
64180 }
64181 }
64182 } else {
64183 /* Complex case - deleting a node with two children. */
64184 ma_resource_manager_data_buffer_node* pReplacementDataBufferNode;
64185
64186 /* 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. */
64187 pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode);
64188 MA_ASSERT(pReplacementDataBufferNode != NULL);
64189
64190 /*
64191 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
64192 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
64193 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
64194 replacement node and reinserting it into the same position as the deleted node.
64195 */
64196 MA_ASSERT(pReplacementDataBufferNode->pParent != NULL); /* The replacement node should never be the root which means it should always have a parent. */
64197 MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL); /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */
64198
64199 if (pReplacementDataBufferNode->pChildHi == NULL) {
64200 if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) {
64201 pReplacementDataBufferNode->pParent->pChildLo = NULL;
64202 } else {
64203 pReplacementDataBufferNode->pParent->pChildHi = NULL;
64204 }
64205 } else {
64206 pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent;
64207 if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) {
64208 pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi;
64209 } else {
64210 pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi;
64211 }
64212 }
64213
64214
64215 /* 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 */
64216 if (pDataBufferNode->pParent != NULL) {
64217 if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
64218 pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode;
64219 } else {
64220 pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode;
64221 }
64222 }
64223
64224 /* Now need to update the replacement node's pointers. */
64225 pReplacementDataBufferNode->pParent = pDataBufferNode->pParent;
64226 pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo;
64227 pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi;
64228
64229 /* Now the children of the replacement node need to have their parent pointers updated. */
64230 if (pReplacementDataBufferNode->pChildLo != NULL) {
64231 pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode;
64232 }
64233 if (pReplacementDataBufferNode->pChildHi != NULL) {
64234 pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode;
64235 }
64236
64237 /* Now the root node needs to be updated. */
64238 if (pResourceManager->pRootDataBufferNode == pDataBufferNode) {
64239 pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode;
64240 }
64241 }
64242 }
64243
64244 return MA_SUCCESS;
64245}
64246
64247#if 0 /* Unused for now. */
64248static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32)
64249{
64250 ma_result result;
64251 ma_resource_manager_data_buffer_node* pDataBufferNode;
64252
64253 result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode);
64254 if (result != MA_SUCCESS) {
64255 return result; /* Could not find the data buffer. */
64256 }
64257
64258 return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode);
64259}
64260#endif
64261
64262static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode)
64263{
64264 return (ma_resource_manager_data_supply_type)c89atomic_load_i32(&pDataBufferNode->data.type);
64265}
64266
64267static 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)
64268{
64269 c89atomic_exchange_i32(&pDataBufferNode->data.type, supplyType);
64270}
64271
64272static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount)
64273{
64274 ma_uint32 refCount;
64275
64276 MA_ASSERT(pResourceManager != NULL);
64277 MA_ASSERT(pDataBufferNode != NULL);
64278
64279 (void)pResourceManager;
64280
64281 refCount = c89atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1;
64282
64283 if (pNewRefCount != NULL) {
64284 *pNewRefCount = refCount;
64285 }
64286
64287 return MA_SUCCESS;
64288}
64289
64290static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount)
64291{
64292 ma_uint32 refCount;
64293
64294 MA_ASSERT(pResourceManager != NULL);
64295 MA_ASSERT(pDataBufferNode != NULL);
64296
64297 (void)pResourceManager;
64298
64299 refCount = c89atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1;
64300
64301 if (pNewRefCount != NULL) {
64302 *pNewRefCount = refCount;
64303 }
64304
64305 return MA_SUCCESS;
64306}
64307
64308static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
64309{
64310 MA_ASSERT(pResourceManager != NULL);
64311 MA_ASSERT(pDataBufferNode != NULL);
64312
64313 if (pDataBufferNode->isDataOwnedByResourceManager) {
64314 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) {
64315 ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks);
64316 pDataBufferNode->data.backend.encoded.pData = NULL;
64317 pDataBufferNode->data.backend.encoded.sizeInBytes = 0;
64318 } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) {
64319 ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks);
64320 pDataBufferNode->data.backend.decoded.pData = NULL;
64321 pDataBufferNode->data.backend.decoded.totalFrameCount = 0;
64322 } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) {
64324 } else {
64325 /* Should never hit this if the node was successfully initialized. */
64326 MA_ASSERT(pDataBufferNode->result != MA_SUCCESS);
64327 }
64328 }
64329
64330 /* The data buffer itself needs to be freed. */
64331 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
64332}
64333
64334static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode)
64335{
64336 MA_ASSERT(pDataBufferNode != NULL);
64337
64338 return (ma_result)c89atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */
64339}
64340
64341
64342static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager)
64343{
64344 MA_ASSERT(pResourceManager != NULL);
64345
64346 return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0;
64347}
64348
64349
64350typedef struct
64351{
64352 union
64353 {
64356 } backend; /* Must be the first member. */
64357 ma_resource_manager* pResourceManager;
64358} ma_resource_manager_inline_notification;
64359
64360static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification)
64361{
64362 MA_ASSERT(pResourceManager != NULL);
64363 MA_ASSERT(pNotification != NULL);
64364
64365 pNotification->pResourceManager = pResourceManager;
64366
64367 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
64368 return ma_async_notification_event_init(&pNotification->backend.e);
64369 } else {
64370 return ma_async_notification_poll_init(&pNotification->backend.p);
64371 }
64372}
64373
64374static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification)
64375{
64376 MA_ASSERT(pNotification != NULL);
64377
64378 if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) {
64379 ma_async_notification_event_uninit(&pNotification->backend.e);
64380 } else {
64381 /* No need to uninitialize a polling notification. */
64382 }
64383}
64384
64385static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification)
64386{
64387 MA_ASSERT(pNotification != NULL);
64388
64389 if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) {
64390 ma_async_notification_event_wait(&pNotification->backend.e);
64391 } else {
64392 while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) {
64393 ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager);
64394 if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) {
64395 break;
64396 }
64397 }
64398 }
64399}
64400
64401static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification)
64402{
64403 ma_resource_manager_inline_notification_wait(pNotification);
64404 ma_resource_manager_inline_notification_uninit(pNotification);
64405}
64406
64407
64408static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager)
64409{
64410 MA_ASSERT(pResourceManager != NULL);
64411
64412 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
64413 #ifndef MA_NO_THREADING
64414 {
64415 ma_mutex_lock(&pResourceManager->dataBufferBSTLock);
64416 }
64417 #else
64418 {
64419 MA_ASSERT(MA_FALSE); /* Should never hit this. */
64420 }
64421 #endif
64422 } else {
64423 /* Threading not enabled. Do nothing. */
64424 }
64425}
64426
64427static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager)
64428{
64429 MA_ASSERT(pResourceManager != NULL);
64430
64431 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
64432 #ifndef MA_NO_THREADING
64433 {
64434 ma_mutex_unlock(&pResourceManager->dataBufferBSTLock);
64435 }
64436 #else
64437 {
64438 MA_ASSERT(MA_FALSE); /* Should never hit this. */
64439 }
64440 #endif
64441 } else {
64442 /* Threading not enabled. Do nothing. */
64443 }
64444}
64445
64446#ifndef MA_NO_THREADING
64447static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData)
64448{
64449 ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData;
64450 MA_ASSERT(pResourceManager != NULL);
64451
64452 for (;;) {
64453 ma_result result;
64454 ma_job job;
64455
64456 result = ma_resource_manager_next_job(pResourceManager, &job);
64457 if (result != MA_SUCCESS) {
64458 break;
64459 }
64460
64461 /* Terminate if we got a quit message. */
64462 if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) {
64463 break;
64464 }
64465
64466 ma_job_process(&job);
64467 }
64468
64469 return (ma_thread_result)0;
64470}
64471#endif
64472
64474{
64476
64477 MA_ZERO_OBJECT(&config);
64479 config.decodedChannels = 0;
64480 config.decodedSampleRate = 0;
64481 config.jobThreadCount = 1; /* A single miniaudio-managed job thread by default. */
64482 config.jobQueueCapacity = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY;
64483
64484 /* Flags. */
64485 config.flags = 0;
64486 #ifdef MA_NO_THREADING
64487 {
64488 /* Threading is disabled at compile time so disable threading at runtime as well by default. */
64490 config.jobThreadCount = 0;
64491 }
64492 #endif
64493
64494 return config;
64495}
64496
64497
64499{
64500 ma_result result;
64501 ma_job_queue_config jobQueueConfig;
64502
64503 if (pResourceManager == NULL) {
64504 return MA_INVALID_ARGS;
64505 }
64506
64507 MA_ZERO_OBJECT(pResourceManager);
64508
64509 if (pConfig == NULL) {
64510 return MA_INVALID_ARGS;
64511 }
64512
64513 #ifndef MA_NO_THREADING
64514 {
64515 if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) {
64516 return MA_INVALID_ARGS; /* Requesting too many job threads. */
64517 }
64518 }
64519 #endif
64520
64521 pResourceManager->config = *pConfig;
64522 ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks);
64523
64524 /* Get the log set up early so we can start using it as soon as possible. */
64525 if (pResourceManager->config.pLog == NULL) {
64526 result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log);
64527 if (result == MA_SUCCESS) {
64528 pResourceManager->config.pLog = &pResourceManager->log;
64529 } else {
64530 pResourceManager->config.pLog = NULL; /* Logging is unavailable. */
64531 }
64532 }
64533
64534 if (pResourceManager->config.pVFS == NULL) {
64535 result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks);
64536 if (result != MA_SUCCESS) {
64537 return result; /* Failed to initialize the default file system. */
64538 }
64539
64540 pResourceManager->config.pVFS = &pResourceManager->defaultVFS;
64541 }
64542
64543 /* If threading has been disabled at compile time, enfore it at run time as well. */
64544 #ifdef MA_NO_THREADING
64545 {
64547 }
64548 #endif
64549
64550 /* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */
64551 if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) {
64553
64554 /* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */
64555 if (pResourceManager->config.jobThreadCount > 0) {
64556 return MA_INVALID_ARGS;
64557 }
64558 }
64559
64560 /* Job queue. */
64561 jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity;
64562 jobQueueConfig.flags = 0;
64563 if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) {
64564 if (pResourceManager->config.jobThreadCount > 0) {
64565 return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */
64566 }
64567
64568 jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING;
64569 }
64570
64571 result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue);
64572 if (result != MA_SUCCESS) {
64573 return result;
64574 }
64575
64576
64577 /* Custom decoding backends. */
64578 if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) {
64579 size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount;
64580
64581 pResourceManager->config.ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks);
64582 if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) {
64583 ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
64584 return MA_OUT_OF_MEMORY;
64585 }
64586
64587 MA_COPY_MEMORY(pResourceManager->config.ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes);
64588
64591 }
64592
64593
64594
64595 /* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */
64596 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
64597 #ifndef MA_NO_THREADING
64598 {
64599 ma_uint32 iJobThread;
64600
64601 /* Data buffer lock. */
64602 result = ma_mutex_init(&pResourceManager->dataBufferBSTLock);
64603 if (result != MA_SUCCESS) {
64604 ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
64605 return result;
64606 }
64607
64608 /* Create the job threads last to ensure the threads has access to valid data. */
64609 for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {
64610 result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, 0, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks);
64611 if (result != MA_SUCCESS) {
64612 ma_mutex_uninit(&pResourceManager->dataBufferBSTLock);
64613 ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
64614 return result;
64615 }
64616 }
64617 }
64618 #else
64619 {
64620 /* Threading is disabled at compile time. We should never get here because validation checks should have already been performed. */
64621 MA_ASSERT(MA_FALSE);
64622 }
64623 #endif
64624 }
64625
64626 return MA_SUCCESS;
64627}
64628
64629
64630static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager)
64631{
64632 MA_ASSERT(pResourceManager);
64633
64634 /* If everything was done properly, there shouldn't be any active data buffers. */
64635 while (pResourceManager->pRootDataBufferNode != NULL) {
64636 ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode;
64637 ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
64638
64639 /* The data buffer has been removed from the BST, so now we need to free it's data. */
64640 ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
64641 }
64642}
64643
64645{
64646 if (pResourceManager == NULL) {
64647 return;
64648 }
64649
64650 /*
64651 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
64652 queue which means it will never not be returned after being encounted for the first time which means all threads will eventually receive it.
64653 */
64654 ma_resource_manager_post_job_quit(pResourceManager);
64655
64656 /* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */
64657 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
64658 #ifndef MA_NO_THREADING
64659 {
64660 ma_uint32 iJobThread;
64661
64662 for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {
64663 ma_thread_wait(&pResourceManager->jobThreads[iJobThread]);
64664 }
64665 }
64666 #else
64667 {
64668 MA_ASSERT(MA_FALSE); /* Should never hit this. */
64669 }
64670 #endif
64671 }
64672
64673 /* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */
64674 ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager);
64675
64676 /* The job queue is no longer needed. */
64677 ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
64678
64679 /* We're no longer doing anything with data buffers so the lock can now be uninitialized. */
64680 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
64681 #ifndef MA_NO_THREADING
64682 {
64683 ma_mutex_uninit(&pResourceManager->dataBufferBSTLock);
64684 }
64685 #else
64686 {
64687 MA_ASSERT(MA_FALSE); /* Should never hit this. */
64688 }
64689 #endif
64690 }
64691
64692 ma_free(pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks);
64693
64694 if (pResourceManager->config.pLog == &pResourceManager->log) {
64695 ma_log_uninit(&pResourceManager->log);
64696 }
64697}
64698
64700{
64701 if (pResourceManager == NULL) {
64702 return NULL;
64703 }
64704
64705 return pResourceManager->config.pLog;
64706}
64707
64708
64709
64711{
64713
64714 MA_ZERO_OBJECT(&config);
64715 config.rangeEndInPCMFrames = ~((ma_uint64)0);
64716 config.loopPointEndInPCMFrames = ~((ma_uint64)0);
64717
64718 return config;
64719}
64720
64721
64722static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager)
64723{
64724 ma_decoder_config config;
64725
64726 config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate);
64727 config.allocationCallbacks = pResourceManager->config.allocationCallbacks;
64729 config.customBackendCount = pResourceManager->config.customDecodingBackendCount;
64731
64732 return config;
64733}
64734
64735static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder)
64736{
64737 ma_result result;
64738 ma_decoder_config config;
64739
64740 MA_ASSERT(pResourceManager != NULL);
64741 MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
64742 MA_ASSERT(pDecoder != NULL);
64743
64744 config = ma_resource_manager__init_decoder_config(pResourceManager);
64745
64746 if (pFilePath != NULL) {
64747 result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder);
64748 if (result != MA_SUCCESS) {
64749 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));
64750 return result;
64751 }
64752 } else {
64753 result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder);
64754 if (result != MA_SUCCESS) {
64755 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
64756 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));
64757 #endif
64758 return result;
64759 }
64760 }
64761
64762 return MA_SUCCESS;
64763}
64764
64765static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer)
64766{
64767 switch (pDataBuffer->pNode->data.type)
64768 {
64772
64774 default:
64775 {
64776 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");
64777 return NULL;
64778 };
64779 };
64780}
64781
64782static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, ma_async_notification* pInitNotification, ma_fence* pInitFence)
64783{
64784 ma_result result;
64785
64786 MA_ASSERT(pDataBuffer != NULL);
64787 MA_ASSERT(pDataBuffer->isConnectorInitialized == MA_FALSE);
64788
64789 /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */
64790 result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode);
64791 if (result != MA_SUCCESS && result != MA_BUSY) {
64792 return result; /* The data buffer is in an erroneous state. */
64793 }
64794
64795 /*
64796 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
64797 "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
64798 an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead.
64799 */
64800 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
64801 {
64802 case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */
64803 {
64804 ma_decoder_config config;
64805 config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager);
64806 result = ma_decoder_init_memory(pDataBuffer->pNode->data.backend.encoded.pData, pDataBuffer->pNode->data.backend.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder);
64807 } break;
64808
64809 case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */
64810 {
64813 result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer);
64814 } break;
64815
64816 case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */
64817 {
64820 result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer);
64821 } break;
64822
64824 default:
64825 {
64826 /* Unknown data supply type. Should never happen. Need to post an error here. */
64827 return MA_INVALID_ARGS;
64828 };
64829 }
64830
64831 /*
64832 Initialization of the connector is when we can fire the init notification. This will give the application access to
64833 the format/channels/rate of the data source.
64834 */
64835 if (result == MA_SUCCESS) {
64836 /*
64837 Make sure the looping state is set before returning in order to handle the case where the
64838 loop state was set on the data buffer before the connector was initialized.
64839 */
64840 ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), ma_resource_manager_data_buffer_is_looping(pDataBuffer));
64841
64842 pDataBuffer->isConnectorInitialized = MA_TRUE;
64843
64844 if (pInitNotification != NULL) {
64845 ma_async_notification_signal(pInitNotification);
64846 }
64847
64848 if (pInitFence != NULL) {
64849 ma_fence_release(pInitFence);
64850 }
64851 }
64852
64853 /* 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. */
64854 return result;
64855}
64856
64857static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer)
64858{
64859 MA_ASSERT(pResourceManager != NULL);
64860 MA_ASSERT(pDataBuffer != NULL);
64861
64862 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
64863 {
64864 case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */
64865 {
64866 ma_decoder_uninit(&pDataBuffer->connector.decoder);
64867 } break;
64868
64869 case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */
64870 {
64872 } break;
64873
64874 case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */
64875 {
64877 } break;
64878
64880 default:
64881 {
64882 /* Unknown data supply type. Should never happen. Need to post an error here. */
64883 return MA_INVALID_ARGS;
64884 };
64885 }
64886
64887 return MA_SUCCESS;
64888}
64889
64890static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode)
64891{
64892 MA_ASSERT(pDataBufferNode != NULL);
64893 return c89atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1);
64894}
64895
64896static 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)
64897{
64898 ma_result result;
64899 size_t dataSizeInBytes;
64900 void* pData;
64901
64902 MA_ASSERT(pResourceManager != NULL);
64903 MA_ASSERT(pDataBufferNode != NULL);
64904 MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
64905
64906 result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks);
64907 if (result != MA_SUCCESS) {
64908 if (pFilePath != NULL) {
64909 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));
64910 } else {
64911 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
64912 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));
64913 #endif
64914 }
64915
64916 return result;
64917 }
64918
64919 pDataBufferNode->data.backend.encoded.pData = pData;
64920 pDataBufferNode->data.backend.encoded.sizeInBytes = dataSizeInBytes;
64921 ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded); /* <-- Must be set last. */
64922
64923 return MA_SUCCESS;
64924}
64925
64926static 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_decoder** ppDecoder)
64927{
64928 ma_result result = MA_SUCCESS;
64929 ma_decoder* pDecoder;
64930 ma_uint64 totalFrameCount;
64931
64932 MA_ASSERT(pResourceManager != NULL);
64933 MA_ASSERT(pDataBufferNode != NULL);
64934 MA_ASSERT(ppDecoder != NULL);
64935 MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
64936
64937 *ppDecoder = NULL; /* For safety. */
64938
64939 pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks);
64940 if (pDecoder == NULL) {
64941 return MA_OUT_OF_MEMORY;
64942 }
64943
64944 result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder);
64945 if (result != MA_SUCCESS) {
64946 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
64947 return result;
64948 }
64949
64950 /*
64951 At this point we have the decoder and we now need to initialize the data supply. This will
64952 be either a decoded buffer, or a decoded paged buffer. A regular buffer is just one big heap
64953 allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter
64954 is used when the length of a sound is unknown until a full decode has been performed.
64955 */
64956 result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount);
64957 if (result != MA_SUCCESS) {
64958 return result;
64959 }
64960
64961 if (totalFrameCount > 0) {
64962 /* It's a known length. The data supply is a regular decoded buffer. */
64963 ma_uint64 dataSizeInBytes;
64964 void* pData;
64965
64966 dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
64967 if (dataSizeInBytes > MA_SIZE_MAX) {
64968 ma_decoder_uninit(pDecoder);
64969 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
64970 return MA_TOO_BIG;
64971 }
64972
64973 pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks);
64974 if (pData == NULL) {
64975 ma_decoder_uninit(pDecoder);
64976 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
64977 return MA_OUT_OF_MEMORY;
64978 }
64979
64980 /* The buffer needs to be initialized to silence in case the caller reads from it. */
64981 ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels);
64982
64983 /* Data has been allocated and the data supply can now be initialized. */
64984 pDataBufferNode->data.backend.decoded.pData = pData;
64985 pDataBufferNode->data.backend.decoded.totalFrameCount = totalFrameCount;
64986 pDataBufferNode->data.backend.decoded.format = pDecoder->outputFormat;
64987 pDataBufferNode->data.backend.decoded.channels = pDecoder->outputChannels;
64988 pDataBufferNode->data.backend.decoded.sampleRate = pDecoder->outputSampleRate;
64989 pDataBufferNode->data.backend.decoded.decodedFrameCount = 0;
64990 ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded); /* <-- Must be set last. */
64991 } else {
64992 /*
64993 It's an unknown length. The data supply is a paged decoded buffer. Setting this up is
64994 actually easier than the non-paged decoded buffer because we just need to initialize
64995 a ma_paged_audio_buffer object.
64996 */
64997 result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.backend.decodedPaged.data);
64998 if (result != MA_SUCCESS) {
64999 ma_decoder_uninit(pDecoder);
65000 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
65001 return result;
65002 }
65003
65004 pDataBufferNode->data.backend.decodedPaged.sampleRate = pDecoder->outputSampleRate;
65005 pDataBufferNode->data.backend.decodedPaged.decodedFrameCount = 0;
65006 ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged); /* <-- Must be set last. */
65007 }
65008
65009 *ppDecoder = pDecoder;
65010
65011 return MA_SUCCESS;
65012}
65013
65014static 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)
65015{
65016 ma_result result = MA_SUCCESS;
65017 ma_uint64 pageSizeInFrames;
65018 ma_uint64 framesToTryReading;
65019 ma_uint64 framesRead;
65020
65021 MA_ASSERT(pResourceManager != NULL);
65022 MA_ASSERT(pDataBufferNode != NULL);
65023 MA_ASSERT(pDecoder != NULL);
65024
65025 /* We need to know the size of a page in frames to know how many frames to decode. */
65026 pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000);
65027 framesToTryReading = pageSizeInFrames;
65028
65029 /*
65030 Here is where we do the decoding of the next page. We'll run a slightly different path depending
65031 on whether or not we're using a flat or paged buffer because the allocation of the page differs
65032 between the two. For a flat buffer it's an offset to an already-allocated buffer. For a paged
65033 buffer, we need to allocate a new page and attach it to the linked list.
65034 */
65035 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode))
65036 {
65038 {
65039 /* The destination buffer is an offset to the existing buffer. Don't read more than we originally retrieved when we first initialized the decoder. */
65040 void* pDst;
65041 ma_uint64 framesRemaining = pDataBufferNode->data.backend.decoded.totalFrameCount - pDataBufferNode->data.backend.decoded.decodedFrameCount;
65042 if (framesToTryReading > framesRemaining) {
65043 framesToTryReading = framesRemaining;
65044 }
65045
65046 if (framesToTryReading > 0) {
65047 pDst = ma_offset_ptr(
65048 pDataBufferNode->data.backend.decoded.pData,
65049 pDataBufferNode->data.backend.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.backend.decoded.format, pDataBufferNode->data.backend.decoded.channels)
65050 );
65051 MA_ASSERT(pDst != NULL);
65052
65053 result = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading, &framesRead);
65054 if (framesRead > 0) {
65055 pDataBufferNode->data.backend.decoded.decodedFrameCount += framesRead;
65056 }
65057 } else {
65058 framesRead = 0;
65059 }
65060 } break;
65061
65063 {
65064 /* The destination buffer is a freshly allocated page. */
65066
65067 result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.backend.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage);
65068 if (result != MA_SUCCESS) {
65069 return result;
65070 }
65071
65072 result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead);
65073 if (framesRead > 0) {
65074 pPage->sizeInFrames = framesRead;
65075
65076 result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage);
65077 if (result == MA_SUCCESS) {
65078 pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead;
65079 } else {
65080 /* Failed to append the page. Just abort and set the status to MA_AT_END. */
65081 ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks);
65082 result = MA_AT_END;
65083 }
65084 } else {
65085 /* No frames were read. Free the page and just set the status to MA_AT_END. */
65086 ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks);
65087 result = MA_AT_END;
65088 }
65089 } break;
65090
65093 default:
65094 {
65095 /* Unexpected data supply type. */
65096 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));
65097 return MA_ERROR;
65098 };
65099 }
65100
65101 if (result == MA_SUCCESS && framesRead == 0) {
65102 result = MA_AT_END;
65103 }
65104
65105 return result;
65106}
65107
65108static 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)
65109{
65110 ma_result result = MA_SUCCESS;
65111 ma_resource_manager_data_buffer_node* pDataBufferNode = NULL;
65113
65114 if (ppDataBufferNode != NULL) {
65115 *ppDataBufferNode = NULL;
65116 }
65117
65118 result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint);
65119 if (result == MA_ALREADY_EXISTS) {
65120 /* The node already exists. We just need to increment the reference count. */
65121 pDataBufferNode = pInsertPoint;
65122
65123 result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL);
65124 if (result != MA_SUCCESS) {
65125 return result; /* Should never happen. Failed to increment the reference count. */
65126 }
65127
65128 result = MA_ALREADY_EXISTS;
65129 goto done;
65130 } else {
65131 /*
65132 The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This
65133 needs to be done inside the critical section to ensure an uninitialization of the node
65134 does not occur before initialization on another thread.
65135 */
65136 pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks);
65137 if (pDataBufferNode == NULL) {
65138 return MA_OUT_OF_MEMORY;
65139 }
65140
65141 MA_ZERO_OBJECT(pDataBufferNode);
65142 pDataBufferNode->hashedName32 = hashedName32;
65143 pDataBufferNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */
65144
65145 if (pExistingData == NULL) {
65146 pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */
65147 pDataBufferNode->result = MA_BUSY; /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */
65148 pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE;
65149 } else {
65150 pDataBufferNode->data = *pExistingData;
65151 pDataBufferNode->result = MA_SUCCESS; /* Not loading asynchronously, so just set the status */
65152 pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE;
65153 }
65154
65155 result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint);
65156 if (result != MA_SUCCESS) {
65157 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
65158 return result; /* Should never happen. Failed to insert the data buffer into the BST. */
65159 }
65160
65161 /*
65162 Here is where we'll post the job, but only if we're loading asynchronously. If we're
65163 loading synchronously we'll defer loading to a later stage, outside of the critical
65164 section.
65165 */
65166 if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) {
65167 /* Loading asynchronously. Post the job. */
65168 ma_job job;
65169 char* pFilePathCopy = NULL;
65170 wchar_t* pFilePathWCopy = NULL;
65171
65172 /* 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. */
65173 if (pFilePath != NULL) {
65174 pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks);
65175 } else {
65176 pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks);
65177 }
65178
65179 if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {
65180 ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
65181 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
65182 return MA_OUT_OF_MEMORY;
65183 }
65184
65186 ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification);
65187 }
65188
65189 /* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */
65190 if (pInitFence != NULL) { ma_fence_acquire(pInitFence); }
65191 if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); }
65192
65193 /* We now have everything we need to post the job to the job thread. */
65195 job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
65205
65206 result = ma_resource_manager_post_job(pResourceManager, &job);
65207 if (result != MA_SUCCESS) {
65208 /* Failed to post job. Probably ran out of memory. */
65209 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));
65210
65211 /*
65212 Fences were acquired before posting the job, but since the job was not able to
65213 be posted, we need to make sure we release them so nothing gets stuck waiting.
65214 */
65215 if (pInitFence != NULL) { ma_fence_release(pInitFence); }
65216 if (pDoneFence != NULL) { ma_fence_release(pDoneFence); }
65217
65219 ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification);
65220 }
65221
65222 ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks);
65223 ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks);
65224
65225 ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
65226 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
65227
65228 return result;
65229 }
65230 }
65231 }
65232
65233done:
65234 if (ppDataBufferNode != NULL) {
65235 *ppDataBufferNode = pDataBufferNode;
65236 }
65237
65238 return result;
65239}
65240
65241static 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)
65242{
65243 ma_result result = MA_SUCCESS;
65244 ma_bool32 nodeAlreadyExists = MA_FALSE;
65245 ma_resource_manager_data_buffer_node* pDataBufferNode = NULL;
65246 ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */
65247
65248 if (ppDataBufferNode != NULL) {
65249 *ppDataBufferNode = NULL; /* Safety. */
65250 }
65251
65252 if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) {
65253 return MA_INVALID_ARGS;
65254 }
65255
65256 /* If we're specifying existing data, it must be valid. */
65257 if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) {
65258 return MA_INVALID_ARGS;
65259 }
65260
65261 /* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */
65262 if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
65263 flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC;
65264 }
65265
65266 if (hashedName32 == 0) {
65267 if (pFilePath != NULL) {
65268 hashedName32 = ma_hash_string_32(pFilePath);
65269 } else {
65270 hashedName32 = ma_hash_string_w_32(pFilePathW);
65271 }
65272 }
65273
65274 /*
65275 Here is where we either increment the node's reference count or allocate a new one and add it
65276 to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is
65277 posted inside the critical section just in case the caller immediately uninitializes the node
65278 as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the
65279 node is not uninitialized before initialization.
65280 */
65281 ma_resource_manager_data_buffer_bst_lock(pResourceManager);
65282 {
65283 result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode);
65284 }
65285 ma_resource_manager_data_buffer_bst_unlock(pResourceManager);
65286
65287 if (result == MA_ALREADY_EXISTS) {
65288 nodeAlreadyExists = MA_TRUE;
65289 result = MA_SUCCESS;
65290 } else {
65291 if (result != MA_SUCCESS) {
65292 return result;
65293 }
65294 }
65295
65296 /*
65297 If we're loading synchronously, we'll need to load everything now. When loading asynchronously,
65298 a job will have been posted inside the BST critical section so that an uninitialization can be
65299 allocated an appropriate execution order thereby preventing it from being uninitialized before
65300 the node is initialized by the decoding thread(s).
65301 */
65302 if (nodeAlreadyExists == MA_FALSE) { /* Don't need to try loading anything if the node already exists. */
65303 if (pFilePath == NULL && pFilePathW == NULL) {
65304 /*
65305 If this path is hit, it means a buffer is being copied (i.e. initialized from only the
65306 hashed name), but that node has been freed in the meantime, probably from some other
65307 thread. This is an invalid operation.
65308 */
65309 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");
65310 result = MA_INVALID_OPERATION;
65311 goto done;
65312 }
65313
65314 if (pDataBufferNode->isDataOwnedByResourceManager) {
65315 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) {
65316 /* Loading synchronously. Load the sound in it's entirety here. */
65318 /* No decoding. This is the simple case - just store the file contents in memory. */
65319 result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW);
65320 if (result != MA_SUCCESS) {
65321 goto done;
65322 }
65323 } else {
65324 /* Decoding. We do this the same way as we do when loading asynchronously. */
65325 ma_decoder* pDecoder;
65326 result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, &pDecoder);
65327 if (result != MA_SUCCESS) {
65328 goto done;
65329 }
65330
65331 /* We have the decoder, now decode page by page just like we do when loading asynchronously. */
65332 for (;;) {
65333 /* Decode next page. */
65334 result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder);
65335 if (result != MA_SUCCESS) {
65336 break; /* Will return MA_AT_END when the last page has been decoded. */
65337 }
65338 }
65339
65340 /* Reaching the end needs to be considered successful. */
65341 if (result == MA_AT_END) {
65342 result = MA_SUCCESS;
65343 }
65344
65345 /*
65346 At this point the data buffer is either fully decoded or some error occurred. Either
65347 way, the decoder is no longer necessary.
65348 */
65349 ma_decoder_uninit(pDecoder);
65350 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
65351 }
65352
65353 /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */
65354 c89atomic_exchange_i32(&pDataBufferNode->result, result);
65355 } else {
65356 /* Loading asynchronously. We may need to wait for initialization. */
65358 ma_resource_manager_inline_notification_wait(&initNotification);
65359 }
65360 }
65361 } else {
65362 /* The data is not managed by the resource manager so there's nothing else to do. */
65363 MA_ASSERT(pExistingData != NULL);
65364 }
65365 }
65366
65367done:
65368 /* If we failed to initialize the data buffer we need to free it. */
65369 if (result != MA_SUCCESS) {
65370 if (nodeAlreadyExists == MA_FALSE) {
65371 ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
65372 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
65373 }
65374 }
65375
65376 /*
65377 The init notification needs to be uninitialized. This will be used if the node does not already
65378 exist, and we've specified ASYNC | WAIT_INIT.
65379 */
65380 if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) {
65382 ma_resource_manager_inline_notification_uninit(&initNotification);
65383 }
65384 }
65385
65386 if (ppDataBufferNode != NULL) {
65387 *ppDataBufferNode = pDataBufferNode;
65388 }
65389
65390 return result;
65391}
65392
65393static 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)
65394{
65395 ma_result result = MA_SUCCESS;
65396 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. */
65397 ma_uint32 hashedName32 = 0;
65398
65399 if (pResourceManager == NULL) {
65400 return MA_INVALID_ARGS;
65401 }
65402
65403 if (pDataBufferNode == NULL) {
65404 if (pName == NULL && pNameW == NULL) {
65405 return MA_INVALID_ARGS;
65406 }
65407
65408 if (pName != NULL) {
65409 hashedName32 = ma_hash_string_32(pName);
65410 } else {
65411 hashedName32 = ma_hash_string_w_32(pNameW);
65412 }
65413 }
65414
65415 /*
65416 The first thing to do is decrement the reference counter of the node. Then, if the reference
65417 count is zero, we need to free the node. If the node is still in the process of loading, we'll
65418 need to post a job to the job queue to free the node. Otherwise we'll just do it here.
65419 */
65420 ma_resource_manager_data_buffer_bst_lock(pResourceManager);
65421 {
65422 /* Might need to find the node. Must be done inside the critical section. */
65423 if (pDataBufferNode == NULL) {
65424 result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode);
65425 if (result != MA_SUCCESS) {
65426 goto stage2; /* Couldn't find the node. */
65427 }
65428 }
65429
65430 result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount);
65431 if (result != MA_SUCCESS) {
65432 goto stage2; /* Should never happen. */
65433 }
65434
65435 if (refCount == 0) {
65436 result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
65437 if (result != MA_SUCCESS) {
65438 goto stage2; /* An error occurred when trying to remove the data buffer. This should never happen. */
65439 }
65440 }
65441 }
65442 ma_resource_manager_data_buffer_bst_unlock(pResourceManager);
65443
65444stage2:
65445 if (result != MA_SUCCESS) {
65446 return result;
65447 }
65448
65449 /*
65450 Here is where we need to free the node. We don't want to do this inside the critical section
65451 above because we want to keep that as small as possible for multi-threaded efficiency.
65452 */
65453 if (refCount == 0) {
65454 if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) {
65455 /* The sound is still loading. We need to delay the freeing of the node to a safe time. */
65456 ma_job job;
65457
65458 /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */
65459 c89atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE);
65460
65462 job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
65465
65466 result = ma_resource_manager_post_job(pResourceManager, &job);
65467 if (result != MA_SUCCESS) {
65468 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));
65469 return result;
65470 }
65471
65472 /* If we don't support threading, process the job queue here. */
65473 if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
65474 while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) {
65475 result = ma_resource_manager_process_next_job(pResourceManager);
65476 if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) {
65477 result = MA_SUCCESS;
65478 break;
65479 }
65480 }
65481 } else {
65482 /* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */
65483 }
65484 } else {
65485 /* The sound isn't loading so we can just free the node here. */
65486 ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
65487 }
65488 }
65489
65490 return result;
65491}
65492
65493
65494
65495static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer)
65496{
65497 MA_ASSERT(pDataBuffer != NULL);
65498 return c89atomic_fetch_add_32(&pDataBuffer->executionCounter, 1);
65499}
65500
65501static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
65502{
65503 return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead);
65504}
65505
65506static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
65507{
65509}
65510
65511static 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)
65512{
65513 return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
65514}
65515
65516static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
65517{
65519}
65520
65521static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
65522{
65524}
65525
65526static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
65527{
65529}
65530
65531static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable =
65532{
65533 ma_resource_manager_data_buffer_cb__read_pcm_frames,
65534 ma_resource_manager_data_buffer_cb__seek_to_pcm_frame,
65535 ma_resource_manager_data_buffer_cb__get_data_format,
65536 ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames,
65537 ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames,
65538 ma_resource_manager_data_buffer_cb__set_looping,
65539 0
65540};
65541
65542static 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)
65543{
65544 ma_result result = MA_SUCCESS;
65545 ma_resource_manager_data_buffer_node* pDataBufferNode;
65546 ma_data_source_config dataSourceConfig;
65547 ma_bool32 async;
65548 ma_uint32 flags;
65550
65551 if (pDataBuffer == NULL) {
65552 if (pConfig != NULL && pConfig->pNotifications != NULL) {
65553 ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications);
65554 }
65555
65556 return MA_INVALID_ARGS;
65557 }
65558
65559 MA_ZERO_OBJECT(pDataBuffer);
65560
65561 if (pConfig == NULL) {
65562 return MA_INVALID_ARGS;
65563 }
65564
65565 if (pConfig->pNotifications != NULL) {
65566 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. */
65567 } else {
65568 MA_ZERO_OBJECT(&notifications);
65569 }
65570
65571 /* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */
65572 flags = pConfig->flags;
65573 if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
65574 flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC;
65575 }
65576
65577 async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0;
65578
65579 /*
65580 Fences need to be acquired before doing anything. These must be aquired and released outside of
65581 the node to ensure there's no holes where ma_fence_wait() could prematurely return before the
65582 data buffer has completed initialization.
65583
65584 When loading asynchronously, the node acquisition routine below will acquire the fences on this
65585 thread and then release them on the async thread when the operation is complete.
65586
65587 These fences are always released at the "done" tag at the end of this function. They'll be
65588 acquired a second if loading asynchronously. This double acquisition system is just done to
65589 simplify code maintanence.
65590 */
65591 ma_resource_manager_pipeline_notifications_acquire_all_fences(&notifications);
65592 {
65593 /* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */
65594 result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode);
65595 if (result != MA_SUCCESS) {
65596 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
65597 goto done;
65598 }
65599
65600 dataSourceConfig = ma_data_source_config_init();
65601 dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable;
65602
65603 result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds);
65604 if (result != MA_SUCCESS) {
65605 ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
65606 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
65607 goto done;
65608 }
65609
65610 pDataBuffer->pResourceManager = pResourceManager;
65611 pDataBuffer->pNode = pDataBufferNode;
65612 pDataBuffer->flags = flags;
65613 pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */
65614
65615 /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */
65616 if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) {
65617 /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */
65618 result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, NULL, NULL);
65619 c89atomic_exchange_i32(&pDataBuffer->result, result);
65620
65621 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
65622 goto done;
65623 } else {
65624 /* 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. */
65625 ma_job job;
65626 ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */
65627
65629 ma_resource_manager_inline_notification_init(pResourceManager, &initNotification);
65630 }
65631
65632 /*
65633 The status of the data buffer needs to be set to MA_BUSY before posting the job so that the
65634 worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other
65635 than MA_BUSY, it'll assume an error and fall through to an early exit.
65636 */
65637 c89atomic_exchange_i32(&pDataBuffer->result, MA_BUSY);
65638
65639 /* Acquire fences a second time. These will be released by the async thread. */
65640 ma_resource_manager_pipeline_notifications_acquire_all_fences(&notifications);
65641
65643 job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
65649
65650 result = ma_resource_manager_post_job(pResourceManager, &job);
65651 if (result != MA_SUCCESS) {
65652 /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */
65653 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));
65654 c89atomic_exchange_i32(&pDataBuffer->result, result);
65655
65656 /* Release the fences after the result has been set on the data buffer. */
65657 ma_resource_manager_pipeline_notifications_release_all_fences(&notifications);
65658 } else {
65660 ma_resource_manager_inline_notification_wait(&initNotification);
65661
65662 if (notifications.init.pNotification != NULL) {
65664 }
65665
65666 /* NOTE: Do not release the init fence here. It will have been done by the job. */
65667
65668 /* Make sure we return an error if initialization failed on the async thread. */
65669 result = ma_resource_manager_data_buffer_result(pDataBuffer);
65670 if (result == MA_BUSY) {
65671 result = MA_SUCCESS;
65672 }
65673 }
65674 }
65675
65677 ma_resource_manager_inline_notification_uninit(&initNotification);
65678 }
65679 }
65680
65681 if (result != MA_SUCCESS) {
65682 ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
65683 goto done;
65684 }
65685 }
65686done:
65687 if (result == MA_SUCCESS) {
65688 if (pConfig->initialSeekPointInPCMFrames > 0) {
65690 }
65691 }
65692
65693 ma_resource_manager_pipeline_notifications_release_all_fences(&notifications);
65694
65695 return result;
65696}
65697
65699{
65700 return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer);
65701}
65702
65704{
65706
65708 config.pFilePath = pFilePath;
65709 config.flags = flags;
65710 config.pNotifications = pNotifications;
65711
65712 return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer);
65713}
65714
65716{
65718
65720 config.pFilePathW = pFilePath;
65721 config.flags = flags;
65722 config.pNotifications = pNotifications;
65723
65724 return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer);
65725}
65726
65728{
65730
65731 if (pExistingDataBuffer == NULL) {
65732 return MA_INVALID_ARGS;
65733 }
65734
65735 MA_ASSERT(pExistingDataBuffer->pNode != NULL); /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */
65736
65738 config.flags = pExistingDataBuffer->flags;
65739
65740 return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer);
65741}
65742
65743static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer)
65744{
65745 MA_ASSERT(pDataBuffer != NULL);
65746
65747 /* The connector should be uninitialized first. */
65748 ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer);
65749
65750 /* With the connector uninitialized we can unacquire the node. */
65751 ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL);
65752
65753 /* The base data source needs to be uninitialized as well. */
65754 ma_data_source_uninit(&pDataBuffer->ds);
65755
65756 return MA_SUCCESS;
65757}
65758
65760{
65761 ma_result result;
65762
65763 if (pDataBuffer == NULL) {
65764 return MA_INVALID_ARGS;
65765 }
65766
65768 /* The data buffer can be deleted synchronously. */
65769 return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
65770 } else {
65771 /*
65772 The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will
65773 be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event
65774 to get processed before returning.
65775 */
65776 ma_resource_manager_inline_notification notification;
65777 ma_job job;
65778
65779 /*
65780 We need to mark the node as unavailable so we don't try reading from it anymore, but also to
65781 let the loading thread know that it needs to abort it's loading procedure.
65782 */
65783 c89atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE);
65784
65785 result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, &notification);
65786 if (result != MA_SUCCESS) {
65787 return result; /* Failed to create the notification. This should rarely, if ever, happen. */
65788 }
65789
65791 job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
65795
65796 result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job);
65797 if (result != MA_SUCCESS) {
65798 ma_resource_manager_inline_notification_uninit(&notification);
65799 return result;
65800 }
65801
65802 ma_resource_manager_inline_notification_wait_and_uninit(&notification);
65803 }
65804
65805 return result;
65806}
65807
65809{
65810 ma_result result = MA_SUCCESS;
65811 ma_uint64 framesRead = 0;
65812 ma_bool32 isDecodedBufferBusy = MA_FALSE;
65813
65814 /* Safety. */
65815 if (pFramesRead != NULL) {
65816 *pFramesRead = 0;
65817 }
65818
65819 if (frameCount == 0) {
65820 return MA_INVALID_ARGS;
65821 }
65822
65823 /*
65824 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
65825 it's been uninitialized or is in the process of uninitializing.
65826 */
65827 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
65828
65829 /* If the node is not initialized we need to abort with a busy code. */
65830 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
65831 return MA_BUSY; /* Still loading. */
65832 }
65833
65834 if (pDataBuffer->seekToCursorOnNextRead) {
65835 pDataBuffer->seekToCursorOnNextRead = MA_FALSE;
65836
65837 result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames);
65838 if (result != MA_SUCCESS) {
65839 return result;
65840 }
65841 }
65842
65843 /*
65844 For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot
65845 exceed this amount. We'll read as much as we can, and then return MA_BUSY.
65846 */
65847 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) {
65848 ma_uint64 availableFrames;
65849
65850 isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY);
65851
65852 if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) {
65853 /* Don't try reading more than the available frame count. */
65854 if (frameCount > availableFrames) {
65855 frameCount = availableFrames;
65856
65857 /*
65858 If there's no frames available we want to set the status to MA_AT_END. The logic below
65859 will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this
65860 is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count
65861 is 0 because that'll result in a situation where it's possible MA_AT_END won't get
65862 returned.
65863 */
65864 if (frameCount == 0) {
65865 result = MA_AT_END;
65866 }
65867 } else {
65868 isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */
65869 }
65870 }
65871 }
65872
65873 /* Don't attempt to read anything if we've got no frames available. */
65874 if (frameCount > 0) {
65875 result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead);
65876 }
65877
65878 /*
65879 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
65880 as at the end and terminate decoding.
65881 */
65882 if (result == MA_AT_END) {
65883 if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) {
65884 result = MA_BUSY;
65885 }
65886 }
65887
65888 if (isDecodedBufferBusy) {
65889 result = MA_BUSY;
65890 }
65891
65892 if (pFramesRead != NULL) {
65893 *pFramesRead = framesRead;
65894 }
65895
65896 if (result == MA_SUCCESS && framesRead == 0) {
65897 result = MA_AT_END;
65898 }
65899
65900 return result;
65901}
65902
65904{
65905 ma_result result;
65906
65907 /* We cannot be using the data source after it's been uninitialized. */
65908 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
65909
65910 /* If we haven't yet got a connector we need to abort. */
65911 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
65912 pDataBuffer->seekTargetInPCMFrames = frameIndex;
65913 pDataBuffer->seekToCursorOnNextRead = MA_TRUE;
65914 return MA_BUSY; /* Still loading. */
65915 }
65916
65917 result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex);
65918 if (result != MA_SUCCESS) {
65919 return result;
65920 }
65921
65922 pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0; /* <-- For identification purposes. */
65923 pDataBuffer->seekToCursorOnNextRead = MA_FALSE;
65924
65925 return MA_SUCCESS;
65926}
65927
65928MA_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)
65929{
65930 /* We cannot be using the data source after it's been uninitialized. */
65931 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
65932
65933 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
65934 {
65936 {
65937 return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
65938 };
65939
65941 {
65942 *pFormat = pDataBuffer->pNode->data.backend.decoded.format;
65943 *pChannels = pDataBuffer->pNode->data.backend.decoded.channels;
65944 *pSampleRate = pDataBuffer->pNode->data.backend.decoded.sampleRate;
65946 return MA_SUCCESS;
65947 };
65948
65950 {
65951 *pFormat = pDataBuffer->pNode->data.backend.decodedPaged.data.format;
65952 *pChannels = pDataBuffer->pNode->data.backend.decodedPaged.data.channels;
65953 *pSampleRate = pDataBuffer->pNode->data.backend.decodedPaged.sampleRate;
65955 return MA_SUCCESS;
65956 };
65957
65959 {
65960 return MA_BUSY; /* Still loading. */
65961 };
65962
65963 default:
65964 {
65965 /* Unknown supply type. Should never hit this. */
65966 return MA_INVALID_ARGS;
65967 }
65968 }
65969}
65970
65972{
65973 /* We cannot be using the data source after it's been uninitialized. */
65974 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
65975
65976 if (pDataBuffer == NULL || pCursor == NULL) {
65977 return MA_INVALID_ARGS;
65978 }
65979
65980 *pCursor = 0;
65981
65982 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
65983 {
65985 {
65986 return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor);
65987 };
65988
65990 {
65991 return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor);
65992 };
65993
65995 {
65997 };
65998
66000 {
66001 return MA_BUSY;
66002 };
66003
66004 default:
66005 {
66006 return MA_INVALID_ARGS;
66007 }
66008 }
66009}
66010
66012{
66013 /* We cannot be using the data source after it's been uninitialized. */
66014 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
66015
66016 if (pDataBuffer == NULL || pLength == NULL) {
66017 return MA_INVALID_ARGS;
66018 }
66019
66020 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
66021 return MA_BUSY; /* Still loading. */
66022 }
66023
66024 return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength);
66025}
66026
66028{
66029 if (pDataBuffer == NULL) {
66030 return MA_INVALID_ARGS;
66031 }
66032
66033 return (ma_result)c89atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */
66034}
66035
66037{
66038 if (pDataBuffer == NULL) {
66039 return MA_INVALID_ARGS;
66040 }
66041
66042 c89atomic_exchange_32(&pDataBuffer->isLooping, isLooping);
66043
66044 /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */
66045 ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping);
66046
66047 return MA_SUCCESS;
66048}
66049
66051{
66052 if (pDataBuffer == NULL) {
66053 return MA_FALSE;
66054 }
66055
66056 return c89atomic_load_32((ma_bool32*)&pDataBuffer->isLooping);
66057}
66058
66060{
66061 if (pAvailableFrames == NULL) {
66062 return MA_INVALID_ARGS;
66063 }
66064
66065 *pAvailableFrames = 0;
66066
66067 if (pDataBuffer == NULL) {
66068 return MA_INVALID_ARGS;
66069 }
66070
66071 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
66072 if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) {
66073 return MA_BUSY;
66074 } else {
66075 return MA_INVALID_OPERATION; /* No connector. */
66076 }
66077 }
66078
66079 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
66080 {
66082 {
66083 return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames);
66084 };
66085
66087 {
66088 return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames);
66089 };
66090
66092 {
66093 ma_uint64 cursor;
66095
66096 if (pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount > cursor) {
66097 *pAvailableFrames = pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount - cursor;
66098 } else {
66099 *pAvailableFrames = 0;
66100 }
66101
66102 return MA_SUCCESS;
66103 };
66104
66106 default:
66107 {
66108 /* Unknown supply type. Should never hit this. */
66109 return MA_INVALID_ARGS;
66110 }
66111 }
66112}
66113
66114MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags)
66115{
66116 return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL);
66117}
66118
66119MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags)
66120{
66121 return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL);
66122}
66123
66124
66125static ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData)
66126{
66127 return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL);
66128}
66129
66130static 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)
66131{
66134 data.backend.decoded.pData = pData;
66135 data.backend.decoded.totalFrameCount = frameCount;
66136 data.backend.decoded.format = format;
66137 data.backend.decoded.channels = channels;
66138 data.backend.decoded.sampleRate = sampleRate;
66139
66140 return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data);
66141}
66142
66143MA_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)
66144{
66145 return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate);
66146}
66147
66148MA_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)
66149{
66150 return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate);
66151}
66152
66153
66154static 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)
66155{
66158 data.backend.encoded.pData = pData;
66159 data.backend.encoded.sizeInBytes = sizeInBytes;
66160
66161 return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data);
66162}
66163
66164MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes)
66165{
66166 return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes);
66167}
66168
66169MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes)
66170{
66171 return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes);
66172}
66173
66174
66175MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath)
66176{
66177 return ma_resource_manager_unregister_data(pResourceManager, pFilePath);
66178}
66179
66180MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath)
66181{
66182 return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath);
66183}
66184
66185MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName)
66186{
66187 return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL);
66188}
66189
66190MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName)
66191{
66192 return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName);
66193}
66194
66195
66196static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream)
66197{
66198 MA_ASSERT(pDataStream != NULL);
66199 return c89atomic_fetch_add_32(&pDataStream->executionCounter, 1);
66200}
66201
66202static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream)
66203{
66204 MA_ASSERT(pDataStream != NULL);
66205 return c89atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd);
66206}
66207
66208static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream)
66209{
66210 MA_ASSERT(pDataStream != NULL);
66211 return c89atomic_load_32((ma_uint32*)&pDataStream->seekCounter);
66212}
66213
66214
66215static ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
66216{
66217 return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead);
66218}
66219
66220static ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
66221{
66223}
66224
66225static 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)
66226{
66227 return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
66228}
66229
66230static ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
66231{
66233}
66234
66235static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
66236{
66238}
66239
66240static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
66241{
66243}
66244
66245static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable =
66246{
66247 ma_resource_manager_data_stream_cb__read_pcm_frames,
66248 ma_resource_manager_data_stream_cb__seek_to_pcm_frame,
66249 ma_resource_manager_data_stream_cb__get_data_format,
66250 ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames,
66251 ma_resource_manager_data_stream_cb__get_length_in_pcm_frames,
66252 ma_resource_manager_data_stream_cb__set_looping,
66254};
66255
66256static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor)
66257{
66258 /* Loop if possible. */
66259 if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) {
66260 absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames;
66261 }
66262
66263 c89atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor);
66264}
66265
66267{
66268 ma_result result;
66269 ma_data_source_config dataSourceConfig;
66270 char* pFilePathCopy = NULL;
66271 wchar_t* pFilePathWCopy = NULL;
66272 ma_job job;
66273 ma_bool32 waitBeforeReturning = MA_FALSE;
66274 ma_resource_manager_inline_notification waitNotification;
66276
66277 if (pDataStream == NULL) {
66278 if (pConfig != NULL && pConfig->pNotifications != NULL) {
66279 ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications);
66280 }
66281
66282 return MA_INVALID_ARGS;
66283 }
66284
66285 MA_ZERO_OBJECT(pDataStream);
66286
66287 if (pConfig == NULL) {
66288 return MA_INVALID_ARGS;
66289 }
66290
66291 if (pConfig->pNotifications != NULL) {
66292 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. */
66293 } else {
66294 MA_ZERO_OBJECT(&notifications);
66295 }
66296
66297 dataSourceConfig = ma_data_source_config_init();
66298 dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable;
66299
66300 result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds);
66301 if (result != MA_SUCCESS) {
66302 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
66303 return result;
66304 }
66305
66306 pDataStream->pResourceManager = pResourceManager;
66307 pDataStream->flags = pConfig->flags;
66308 pDataStream->result = MA_BUSY;
66309
66312 ma_data_source_set_looping(pDataStream, pConfig->isLooping);
66313
66314 if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) {
66315 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
66316 return MA_INVALID_ARGS;
66317 }
66318
66319 /* 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. */
66320
66321 /* 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. */
66322 if (pConfig->pFilePath != NULL) {
66323 pFilePathCopy = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks);
66324 } else {
66325 pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks);
66326 }
66327
66328 if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {
66329 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
66330 return MA_OUT_OF_MEMORY;
66331 }
66332
66333 /*
66334 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
66335 can return immediately. Likewise, we'll also check for MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT and do the same.
66336 */
66338 waitBeforeReturning = MA_TRUE;
66339 ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification);
66340 }
66341
66342 ma_resource_manager_pipeline_notifications_acquire_all_fences(&notifications);
66343
66344 /* Set the absolute cursor to our initial seek position so retrieval of the cursor returns a good value. */
66345 ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames);
66346
66347 /* 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. */
66349 job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
66351 job.data.resourceManager.loadDataStream.pFilePath = pFilePathCopy;
66352 job.data.resourceManager.loadDataStream.pFilePathW = pFilePathWCopy;
66354 job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification;
66356 result = ma_resource_manager_post_job(pResourceManager, &job);
66357 if (result != MA_SUCCESS) {
66358 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
66359 ma_resource_manager_pipeline_notifications_release_all_fences(&notifications);
66360
66361 if (waitBeforeReturning) {
66362 ma_resource_manager_inline_notification_uninit(&waitNotification);
66363 }
66364
66365 ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks);
66366 ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks);
66367 return result;
66368 }
66369
66370 /* Wait if needed. */
66371 if (waitBeforeReturning) {
66372 ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification);
66373
66374 if (notifications.init.pNotification != NULL) {
66376 }
66377
66378 /* NOTE: Do not release pInitFence here. That will be done by the job. */
66379 }
66380
66381 return MA_SUCCESS;
66382}
66383
66385{
66387
66389 config.pFilePath = pFilePath;
66390 config.flags = flags;
66391 config.pNotifications = pNotifications;
66392
66393 return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream);
66394}
66395
66397{
66399
66401 config.pFilePathW = pFilePath;
66402 config.flags = flags;
66403 config.pNotifications = pNotifications;
66404
66405 return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream);
66406}
66407
66409{
66410 ma_resource_manager_inline_notification freeEvent;
66411 ma_job job;
66412
66413 if (pDataStream == NULL) {
66414 return MA_INVALID_ARGS;
66415 }
66416
66417 /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */
66418 c89atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE);
66419
66420 /*
66421 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
66422 to wait for it to complete before returning which means we need an event.
66423 */
66424 ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent);
66425
66427 job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
66432
66433 /* We need to wait for the job to finish processing before we return. */
66434 ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent);
66435
66436 return MA_SUCCESS;
66437}
66438
66439
66440static ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream)
66441{
66442 MA_ASSERT(pDataStream != NULL);
66443 MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE);
66444
66445 return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000);
66446}
66447
66448static void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor)
66449{
66450 MA_ASSERT(pDataStream != NULL);
66451 MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE);
66452 MA_ASSERT(pageIndex == 0 || pageIndex == 1);
66453
66454 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));
66455}
66456
66457static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex)
66458{
66459 ma_result result = MA_SUCCESS;
66460 ma_uint64 pageSizeInFrames;
66461 ma_uint64 totalFramesReadForThisPage = 0;
66462 void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0);
66463
66464 pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream);
66465
66466 /* The decoder needs to inherit the stream's looping and range state. */
66467 {
66468 ma_uint64 rangeBeg;
66469 ma_uint64 rangeEnd;
66470 ma_uint64 loopPointBeg;
66471 ma_uint64 loopPointEnd;
66472
66474
66475 ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd);
66476 ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd);
66477
66478 ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd);
66479 ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd);
66480 }
66481
66482 /* Just read straight from the decoder. It will deal with ranges and looping for us. */
66483 result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage);
66484 if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) {
66485 c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE);
66486 }
66487
66488 c89atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage);
66489 c89atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE);
66490}
66491
66492static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream)
66493{
66494 ma_uint32 iPage;
66495
66496 MA_ASSERT(pDataStream != NULL);
66497
66498 for (iPage = 0; iPage < 2; iPage += 1) {
66499 ma_resource_manager_data_stream_fill_page(pDataStream, iPage);
66500 }
66501}
66502
66503
66504static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount)
66505{
66506 ma_uint64 framesAvailable;
66507 ma_uint64 frameCount = 0;
66508
66509 /* We cannot be using the data source after it's been uninitialized. */
66510 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
66511
66512 if (pFrameCount != NULL) {
66513 frameCount = *pFrameCount;
66514 *pFrameCount = 0;
66515 }
66516 if (ppFramesOut != NULL) {
66517 *ppFramesOut = NULL;
66518 }
66519
66520 if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) {
66521 return MA_INVALID_ARGS;
66522 }
66523
66525 return MA_INVALID_OPERATION;
66526 }
66527
66528 /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */
66529 if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {
66530 return MA_BUSY;
66531 }
66532
66533 /* If the page we're on is invalid it means we've caught up to the job thread. */
66534 if (c89atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) {
66535 framesAvailable = 0;
66536 } else {
66537 /*
66538 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
66539 that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler.
66540 */
66541 ma_uint32 currentPageFrameCount = c89atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]);
66542 MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor);
66543
66544 framesAvailable = currentPageFrameCount - pDataStream->relativeCursor;
66545 }
66546
66547 /* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */
66548 if (framesAvailable == 0) {
66549 if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) {
66550 return MA_AT_END;
66551 } else {
66552 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. */
66553 }
66554 }
66555
66556 MA_ASSERT(framesAvailable > 0);
66557
66558 if (frameCount > framesAvailable) {
66559 frameCount = framesAvailable;
66560 }
66561
66562 *ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor);
66563 *pFrameCount = frameCount;
66564
66565 return MA_SUCCESS;
66566}
66567
66568static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount)
66569{
66570 ma_uint32 newRelativeCursor;
66571 ma_uint32 pageSizeInFrames;
66572 ma_job job;
66573
66574 /* We cannot be using the data source after it's been uninitialized. */
66575 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
66576
66577 if (pDataStream == NULL) {
66578 return MA_INVALID_ARGS;
66579 }
66580
66582 return MA_INVALID_OPERATION;
66583 }
66584
66585 /* The frame count should always fit inside a 32-bit integer. */
66586 if (frameCount > 0xFFFFFFFF) {
66587 return MA_INVALID_ARGS;
66588 }
66589
66590 pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream);
66591
66592 /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */
66593 ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, c89atomic_load_64(&pDataStream->absoluteCursor) + frameCount);
66594
66595 /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */
66596 newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount;
66597
66598 /* 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. */
66599 if (newRelativeCursor >= pageSizeInFrames) {
66600 newRelativeCursor -= pageSizeInFrames;
66601
66602 /* Here is where we post the job start decoding. */
66604 job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
66607
66608 /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */
66609 c89atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE);
66610
66611 /* Before posting the job we need to make sure we set some state. */
66612 pDataStream->relativeCursor = newRelativeCursor;
66613 pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01;
66614 return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
66615 } else {
66616 /* We haven't moved into a new page so we can just move the cursor forward. */
66617 pDataStream->relativeCursor = newRelativeCursor;
66618 return MA_SUCCESS;
66619 }
66620}
66621
66622
66624{
66625 ma_result result = MA_SUCCESS;
66626 ma_uint64 totalFramesProcessed;
66627 ma_format format;
66628 ma_uint32 channels;
66629
66630 /* Safety. */
66631 if (pFramesRead != NULL) {
66632 *pFramesRead = 0;
66633 }
66634
66635 if (frameCount == 0) {
66636 return MA_INVALID_ARGS;
66637 }
66638
66639 /* We cannot be using the data source after it's been uninitialized. */
66640 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
66641
66642 if (pDataStream == NULL) {
66643 return MA_INVALID_ARGS;
66644 }
66645
66647 return MA_INVALID_OPERATION;
66648 }
66649
66650 /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */
66651 if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {
66652 return MA_BUSY;
66653 }
66654
66655 ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0);
66656
66657 /* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */
66658 totalFramesProcessed = 0;
66659 while (totalFramesProcessed < frameCount) {
66660 void* pMappedFrames;
66661 ma_uint64 mappedFrameCount;
66662
66663 mappedFrameCount = frameCount - totalFramesProcessed;
66664 result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount);
66665 if (result != MA_SUCCESS) {
66666 break;
66667 }
66668
66669 /* 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. */
66670 if (pFramesOut != NULL) {
66671 ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels);
66672 }
66673
66674 totalFramesProcessed += mappedFrameCount;
66675
66676 result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount);
66677 if (result != MA_SUCCESS) {
66678 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. */
66679 }
66680 }
66681
66682 if (pFramesRead != NULL) {
66683 *pFramesRead = totalFramesProcessed;
66684 }
66685
66686 if (result == MA_SUCCESS && totalFramesProcessed == 0) {
66687 result = MA_AT_END;
66688 }
66689
66690 return result;
66691}
66692
66694{
66695 ma_job job;
66696 ma_result streamResult;
66697
66698 streamResult = ma_resource_manager_data_stream_result(pDataStream);
66699
66700 /* We cannot be using the data source after it's been uninitialized. */
66701 MA_ASSERT(streamResult != MA_UNAVAILABLE);
66702
66703 if (pDataStream == NULL) {
66704 return MA_INVALID_ARGS;
66705 }
66706
66707 if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) {
66708 return MA_INVALID_OPERATION;
66709 }
66710
66711 /* 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. */
66712 c89atomic_fetch_add_32(&pDataStream->seekCounter, 1);
66713
66714 /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */
66715 ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex);
66716
66717 /*
66718 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
66719 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
66720 the first page.
66721 */
66722 pDataStream->relativeCursor = 0;
66723 pDataStream->currentPageIndex = 0;
66724 c89atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE);
66725 c89atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE);
66726
66727 /* 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. */
66728 c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE);
66729
66730 /*
66731 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
66732 are invalid and any content contained within them will be discarded and replaced with newly decoded data.
66733 */
66735 job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
66738 return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
66739}
66740
66741MA_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)
66742{
66743 /* We cannot be using the data source after it's been uninitialized. */
66744 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
66745
66746 if (pFormat != NULL) {
66747 *pFormat = ma_format_unknown;
66748 }
66749
66750 if (pChannels != NULL) {
66751 *pChannels = 0;
66752 }
66753
66754 if (pSampleRate != NULL) {
66755 *pSampleRate = 0;
66756 }
66757
66758 if (pChannelMap != NULL) {
66759 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
66760 }
66761
66762 if (pDataStream == NULL) {
66763 return MA_INVALID_ARGS;
66764 }
66765
66767 return MA_INVALID_OPERATION;
66768 }
66769
66770 /*
66771 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
66772 such that the application is responsible for ensuring it's not called while uninitializing so it should be safe.
66773 */
66774 return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
66775}
66776
66778{
66779 ma_result result;
66780
66781 if (pCursor == NULL) {
66782 return MA_INVALID_ARGS;
66783 }
66784
66785 *pCursor = 0;
66786
66787 /* We cannot be using the data source after it's been uninitialized. */
66788 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
66789
66790 if (pDataStream == NULL) {
66791 return MA_INVALID_ARGS;
66792 }
66793
66794 /*
66795 If the stream is in an erroneous state we need to return an invalid operation. We can allow
66796 this to be called when the data stream is in a busy state because the caller may have asked
66797 for an initial seek position and it's convenient to return that as the cursor position.
66798 */
66799 result = ma_resource_manager_data_stream_result(pDataStream);
66800 if (result != MA_SUCCESS && result != MA_BUSY) {
66801 return MA_INVALID_OPERATION;
66802 }
66803
66804 *pCursor = c89atomic_load_64(&pDataStream->absoluteCursor);
66805
66806 return MA_SUCCESS;
66807}
66808
66810{
66811 ma_result streamResult;
66812
66813 if (pLength == NULL) {
66814 return MA_INVALID_ARGS;
66815 }
66816
66817 *pLength = 0;
66818
66819 streamResult = ma_resource_manager_data_stream_result(pDataStream);
66820
66821 /* We cannot be using the data source after it's been uninitialized. */
66822 MA_ASSERT(streamResult != MA_UNAVAILABLE);
66823
66824 if (pDataStream == NULL) {
66825 return MA_INVALID_ARGS;
66826 }
66827
66828 if (streamResult != MA_SUCCESS) {
66829 return streamResult;
66830 }
66831
66832 /*
66833 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
66834 calculated when we initialized it on the job thread.
66835 */
66836 *pLength = pDataStream->totalLengthInPCMFrames;
66837 if (*pLength == 0) {
66838 return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */
66839 }
66840
66841 return MA_SUCCESS;
66842}
66843
66845{
66846 if (pDataStream == NULL) {
66847 return MA_INVALID_ARGS;
66848 }
66849
66850 return (ma_result)c89atomic_load_i32(&pDataStream->result);
66851}
66852
66854{
66855 if (pDataStream == NULL) {
66856 return MA_INVALID_ARGS;
66857 }
66858
66859 c89atomic_exchange_32(&pDataStream->isLooping, isLooping);
66860
66861 return MA_SUCCESS;
66862}
66863
66865{
66866 if (pDataStream == NULL) {
66867 return MA_FALSE;
66868 }
66869
66870 return c89atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */
66871}
66872
66874{
66875 ma_uint32 pageIndex0;
66876 ma_uint32 pageIndex1;
66877 ma_uint32 relativeCursor;
66878 ma_uint64 availableFrames;
66879
66880 if (pAvailableFrames == NULL) {
66881 return MA_INVALID_ARGS;
66882 }
66883
66884 *pAvailableFrames = 0;
66885
66886 if (pDataStream == NULL) {
66887 return MA_INVALID_ARGS;
66888 }
66889
66890 pageIndex0 = pDataStream->currentPageIndex;
66891 pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01;
66892 relativeCursor = pDataStream->relativeCursor;
66893
66894 availableFrames = 0;
66895 if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex0])) {
66896 availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor;
66897 if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex1])) {
66898 availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]);
66899 }
66900 }
66901
66902 *pAvailableFrames = availableFrames;
66903 return MA_SUCCESS;
66904}
66905
66906
66907static 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)
66908{
66909 if (pDataSource == NULL) {
66910 return MA_INVALID_ARGS;
66911 }
66912
66913 MA_ZERO_OBJECT(pDataSource);
66914
66915 if (pConfig == NULL) {
66916 return MA_INVALID_ARGS;
66917 }
66918
66919 if (pResourceManager == NULL) {
66920 return MA_INVALID_ARGS;
66921 }
66922
66923 pDataSource->flags = pConfig->flags;
66924
66925 return MA_SUCCESS;
66926}
66927
66929{
66930 ma_result result;
66931
66932 result = ma_resource_manager_data_source_preinit(pResourceManager, pConfig, pDataSource);
66933 if (result != MA_SUCCESS) {
66934 return result;
66935 }
66936
66937 /* The data source itself is just a data stream or a data buffer. */
66938 if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
66939 return ma_resource_manager_data_stream_init_ex(pResourceManager, pConfig, &pDataSource->backend.stream);
66940 } else {
66941 return ma_resource_manager_data_buffer_init_ex(pResourceManager, pConfig, &pDataSource->backend.buffer);
66942 }
66943}
66944
66946{
66948
66950 config.pFilePath = pName;
66951 config.flags = flags;
66952 config.pNotifications = pNotifications;
66953
66954 return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource);
66955}
66956
66958{
66960
66962 config.pFilePathW = pName;
66963 config.flags = flags;
66964 config.pNotifications = pNotifications;
66965
66966 return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource);
66967}
66968
66970{
66971 ma_result result;
66973
66974 if (pExistingDataSource == NULL) {
66975 return MA_INVALID_ARGS;
66976 }
66977
66979 config.flags = pExistingDataSource->flags;
66980
66981 result = ma_resource_manager_data_source_preinit(pResourceManager, &config, pDataSource);
66982 if (result != MA_SUCCESS) {
66983 return result;
66984 }
66985
66986 /* Copying can only be done from data buffers. Streams cannot be copied. */
66987 if ((pExistingDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
66988 return MA_INVALID_OPERATION;
66989 }
66990
66991 return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->backend.buffer, &pDataSource->backend.buffer);
66992}
66993
66995{
66996 if (pDataSource == NULL) {
66997 return MA_INVALID_ARGS;
66998 }
66999
67000 /* All we need to is uninitialize the underlying data buffer or data stream. */
67001 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
67003 } else {
67005 }
67006}
67007
67009{
67010 /* Safety. */
67011 if (pFramesRead != NULL) {
67012 *pFramesRead = 0;
67013 }
67014
67015 if (pDataSource == NULL) {
67016 return MA_INVALID_ARGS;
67017 }
67018
67019 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
67020 return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->backend.stream, pFramesOut, frameCount, pFramesRead);
67021 } else {
67022 return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->backend.buffer, pFramesOut, frameCount, pFramesRead);
67023 }
67024}
67025
67027{
67028 if (pDataSource == NULL) {
67029 return MA_INVALID_ARGS;
67030 }
67031
67032 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
67033 return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->backend.stream, frameIndex);
67034 } else {
67035 return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->backend.buffer, frameIndex);
67036 }
67037}
67038
67039MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount)
67040{
67041 if (pDataSource == NULL) {
67042 return MA_INVALID_ARGS;
67043 }
67044
67045 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
67046 return ma_resource_manager_data_stream_map(&pDataSource->backend.stream, ppFramesOut, pFrameCount);
67047 } else {
67048 return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */
67049 }
67050}
67051
67052MA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source* pDataSource, ma_uint64 frameCount)
67053{
67054 if (pDataSource == NULL) {
67055 return MA_INVALID_ARGS;
67056 }
67057
67058 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
67059 return ma_resource_manager_data_stream_unmap(&pDataSource->backend.stream, frameCount);
67060 } else {
67061 return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */
67062 }
67063}
67064
67065MA_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)
67066{
67067 if (pDataSource == NULL) {
67068 return MA_INVALID_ARGS;
67069 }
67070
67071 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
67072 return ma_resource_manager_data_stream_get_data_format(&pDataSource->backend.stream, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
67073 } else {
67074 return ma_resource_manager_data_buffer_get_data_format(&pDataSource->backend.buffer, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
67075 }
67076}
67077
67079{
67080 if (pDataSource == NULL) {
67081 return MA_INVALID_ARGS;
67082 }
67083
67084 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
67086 } else {
67088 }
67089}
67090
67092{
67093 if (pDataSource == NULL) {
67094 return MA_INVALID_ARGS;
67095 }
67096
67097 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
67099 } else {
67101 }
67102}
67103
67105{
67106 if (pDataSource == NULL) {
67107 return MA_INVALID_ARGS;
67108 }
67109
67110 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
67112 } else {
67114 }
67115}
67116
67118{
67119 if (pDataSource == NULL) {
67120 return MA_INVALID_ARGS;
67121 }
67122
67123 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
67124 return ma_resource_manager_data_stream_set_looping(&pDataSource->backend.stream, isLooping);
67125 } else {
67126 return ma_resource_manager_data_buffer_set_looping(&pDataSource->backend.buffer, isLooping);
67127 }
67128}
67129
67131{
67132 if (pDataSource == NULL) {
67133 return MA_FALSE;
67134 }
67135
67136 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
67138 } else {
67140 }
67141}
67142
67144{
67145 if (pAvailableFrames == NULL) {
67146 return MA_INVALID_ARGS;
67147 }
67148
67149 *pAvailableFrames = 0;
67150
67151 if (pDataSource == NULL) {
67152 return MA_INVALID_ARGS;
67153 }
67154
67155 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
67156 return ma_resource_manager_data_stream_get_available_frames(&pDataSource->backend.stream, pAvailableFrames);
67157 } else {
67158 return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->backend.buffer, pAvailableFrames);
67159 }
67160}
67161
67162
67164{
67165 if (pResourceManager == NULL) {
67166 return MA_INVALID_ARGS;
67167 }
67168
67169 return ma_job_queue_post(&pResourceManager->jobQueue, pJob);
67170}
67171
67173{
67175 return ma_resource_manager_post_job(pResourceManager, &job);
67176}
67177
67179{
67180 if (pResourceManager == NULL) {
67181 return MA_INVALID_ARGS;
67182 }
67183
67184 return ma_job_queue_next(&pResourceManager->jobQueue, pJob);
67185}
67186
67187
67188static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob)
67189{
67190 ma_result result = MA_SUCCESS;
67191 ma_resource_manager* pResourceManager;
67192 ma_resource_manager_data_buffer_node* pDataBufferNode;
67193
67194 MA_ASSERT(pJob != NULL);
67195
67197 MA_ASSERT(pResourceManager != NULL);
67198
67200 MA_ASSERT(pDataBufferNode != NULL);
67201 MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */
67202
67203 /* 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. */
67204 if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) {
67205 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. */
67206 }
67207
67208 /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */
67209 if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) {
67210 result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */
67211 goto done;
67212 }
67213
67214 /*
67215 We're ready to start loading. Essentially what we're doing here is initializing the data supply
67216 of the node. Once this is complete, data buffers can have their connectors initialized which
67217 will allow then to have audio data read from them.
67218
67219 Note that when the data supply type has been moved away from "unknown", that is when other threads
67220 will determine that the node is available for data delivery and the data buffer connectors can be
67221 initialized. Therefore, it's important that it is set after the data supply has been initialized.
67222 */
67224 /*
67225 Decoding. This is the complex case because we're not going to be doing the entire decoding
67226 process here. Instead it's going to be split of multiple jobs and loaded in pages. The
67227 reason for this is to evenly distribute decoding time across multiple sounds, rather than
67228 having one huge sound hog all the available processing resources.
67229
67230 The first thing we do is initialize a decoder. This is allocated on the heap and is passed
67231 around to the paging jobs. When the last paging job has completed it's processing, it'll
67232 free the decoder for us.
67233
67234 This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job
67235 which is where the actual decoding work will be done. However, once this job is complete,
67236 the node will be in a state where data buffer connectors can be initialized.
67237 */
67238 ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */
67239 ma_job pageDataBufferNodeJob;
67240
67241 /* Allocate the decoder by initializing a decoded data supply. */
67242 result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pDecoder);
67243
67244 /*
67245 Don't ever propagate an MA_BUSY result code or else the resource manager will think the
67246 node is just busy decoding rather than in an error state. This should never happen, but
67247 including this logic for safety just in case.
67248 */
67249 if (result == MA_BUSY) {
67250 result = MA_ERROR;
67251 }
67252
67253 if (result != MA_SUCCESS) {
67255 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));
67256 } else {
67257 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
67258 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));
67259 #endif
67260 }
67261
67262 goto done;
67263 }
67264
67265 /*
67266 At this point the node's data supply is initialized and other threads can start initializing
67267 their data buffer connectors. However, no data will actually be available until we start to
67268 actually decode it. To do this, we need to post a paging job which is where the decoding
67269 work is done.
67270
67271 Note that if an error occurred at an earlier point, this section will have been skipped.
67272 */
67274 pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
67275 pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager;
67276 pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode;
67277 pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder;
67280
67281 /* The job has been set up so it can now be posted. */
67282 result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob);
67283
67284 /*
67285 When we get here, we want to make sure the result code is set to MA_BUSY. The reason for
67286 this is that the result will be copied over to the node's internal result variable. In
67287 this case, since the decoding is still in-progress, we need to make sure the result code
67288 is set to MA_BUSY.
67289 */
67290 if (result != MA_SUCCESS) {
67291 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));
67292 ma_decoder_uninit(pDecoder);
67293 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
67294 } else {
67295 result = MA_BUSY;
67296 }
67297 } else {
67298 /* No decoding. This is the simple case. We need only read the file content into memory and we're done. */
67299 result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW);
67300 }
67301
67302
67303done:
67304 /* File paths are no longer needed. */
67307
67308 /*
67309 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
67310 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
67311 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
67312 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
67313 other error code would cause the buffer to look like it's in a state that it's not.
67314 */
67315 c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result);
67316
67317 /* At this point initialization is complete and we can signal the notification if any. */
67320 }
67323 }
67324
67325 /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */
67326 if (result != MA_BUSY) {
67329 }
67332 }
67333 }
67334
67335 /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */
67336 c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
67337 return result;
67338}
67339
67340static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob)
67341{
67342 ma_resource_manager* pResourceManager;
67343 ma_resource_manager_data_buffer_node* pDataBufferNode;
67344
67345 MA_ASSERT(pJob != NULL);
67346
67348 MA_ASSERT(pResourceManager != NULL);
67349
67351 MA_ASSERT(pDataBufferNode != NULL);
67352
67353 if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) {
67354 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
67355 }
67356
67357 ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
67358
67359 /* The event needs to be signalled last. */
67362 }
67363
67366 }
67367
67368 c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
67369 return MA_SUCCESS;
67370}
67371
67372static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob)
67373{
67374 ma_result result = MA_SUCCESS;
67375 ma_resource_manager* pResourceManager;
67376 ma_resource_manager_data_buffer_node* pDataBufferNode;
67377
67378 MA_ASSERT(pJob != NULL);
67379
67381 MA_ASSERT(pResourceManager != NULL);
67382
67384 MA_ASSERT(pDataBufferNode != NULL);
67385
67386 if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) {
67387 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
67388 }
67389
67390 /* Don't do any more decoding if the data buffer has started the uninitialization process. */
67391 result = ma_resource_manager_data_buffer_node_result(pDataBufferNode);
67392 if (result != MA_BUSY) {
67393 goto done;
67394 }
67395
67396 /* We're ready to decode the next page. */
67397 result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder);
67398
67399 /*
67400 If we have a success code by this point, we want to post another job. We're going to set the
67401 result back to MA_BUSY to make it clear that there's still more to load.
67402 */
67403 if (result == MA_SUCCESS) {
67404 ma_job newJob;
67405 newJob = *pJob; /* Everything is the same as the input job, except the execution order. */
67406 newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); /* We need a fresh execution order. */
67407
67408 result = ma_resource_manager_post_job(pResourceManager, &newJob);
67409
67410 /* Since the sound isn't yet fully decoded we want the status to be set to busy. */
67411 if (result == MA_SUCCESS) {
67412 result = MA_BUSY;
67413 }
67414 }
67415
67416done:
67417 /* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */
67418 if (result != MA_BUSY) {
67421 }
67422
67423 /* If we reached the end we need to treat it as successful. */
67424 if (result == MA_AT_END) {
67425 result = MA_SUCCESS;
67426 }
67427
67428 /* Make sure we set the result of node in case some error occurred. */
67429 c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result);
67430
67431 /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */
67432 if (result != MA_BUSY) {
67435 }
67436
67439 }
67440 }
67441
67442 c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
67443 return result;
67444}
67445
67446
67447static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob)
67448{
67449 ma_result result = MA_SUCCESS;
67450 ma_resource_manager* pResourceManager;
67453 ma_bool32 isConnectorInitialized = MA_FALSE;
67454
67455 /*
67456 All we're doing here is checking if the node has finished loading. If not, we just re-post the job
67457 and keep waiting. Otherwise we increment the execution counter and set the buffer's result code.
67458 */
67459 MA_ASSERT(pJob != NULL);
67460
67462 MA_ASSERT(pDataBuffer != NULL);
67463
67464 pResourceManager = pDataBuffer->pResourceManager;
67465
67466 if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) {
67467 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. */
67468 }
67469
67470 /*
67471 First thing we need to do is check whether or not the data buffer is getting deleted. If so we
67472 just abort, but making sure we increment the execution pointer.
67473 */
67474 result = ma_resource_manager_data_buffer_result(pDataBuffer);
67475 if (result != MA_BUSY) {
67476 goto done; /* <-- This will ensure the exucution pointer is incremented. */
67477 } else {
67478 result = MA_SUCCESS; /* <-- Make sure this is reset. */
67479 }
67480
67481 /* Try initializing the connector if we haven't already. */
67482 isConnectorInitialized = pDataBuffer->isConnectorInitialized;
67483 if (isConnectorInitialized == MA_FALSE) {
67484 dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode);
67485
67486 if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) {
67487 /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */
67488 result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence);
67489 if (result != MA_SUCCESS) {
67490 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));
67491 goto done;
67492 }
67493 } else {
67494 /* 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. */
67495 }
67496 } else {
67497 /* The connector is already initialized. Nothing to do here. */
67498 }
67499
67500 /*
67501 If the data node is still loading, we need to repost the job and *not* increment the execution
67502 pointer (i.e. we need to not fall through to the "done" label).
67503
67504 There is a hole between here and the where the data connector is initialized where the data
67505 buffer node may have finished initializing. We need to check for this by checking the result of
67506 the data buffer node and whether or not we had an unknown data supply type at the time of
67507 trying to initialize the data connector.
67508 */
67509 result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode);
67510 if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) {
67511 return ma_resource_manager_post_job(pResourceManager, pJob);
67512 }
67513
67514done:
67515 /* Only move away from a busy code so that we don't trash any existing error codes. */
67516 c89atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result);
67517
67518 /* Only signal the other threads after the result has been set just for cleanliness sake. */
67521 }
67524 }
67525
67526 /*
67527 If at this point the data buffer has not had it's connector initialized, it means the
67528 notification event was never signalled which means we need to signal it here.
67529 */
67530 if (pDataBuffer->isConnectorInitialized == MA_FALSE && result != MA_SUCCESS) {
67533 }
67536 }
67537 }
67538
67539 c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1);
67540 return result;
67541}
67542
67543static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob)
67544{
67545 ma_resource_manager* pResourceManager;
67547
67548 MA_ASSERT(pJob != NULL);
67549
67551 MA_ASSERT(pDataBuffer != NULL);
67552
67553 pResourceManager = pDataBuffer->pResourceManager;
67554
67555 if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) {
67556 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
67557 }
67558
67559 ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
67560
67561 /* The event needs to be signalled last. */
67564 }
67565
67568 }
67569
67570 c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1);
67571 return MA_SUCCESS;
67572}
67573
67574static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob)
67575{
67576 ma_result result = MA_SUCCESS;
67577 ma_decoder_config decoderConfig;
67578 ma_uint32 pageBufferSizeInBytes;
67579 ma_resource_manager* pResourceManager;
67581
67582 MA_ASSERT(pJob != NULL);
67583
67585 MA_ASSERT(pDataStream != NULL);
67586
67587 pResourceManager = pDataStream->pResourceManager;
67588
67589 if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) {
67590 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
67591 }
67592
67593 if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) {
67594 result = MA_INVALID_OPERATION; /* Most likely the data stream is being uninitialized. */
67595 goto done;
67596 }
67597
67598 /* We need to initialize the decoder first so we can determine the size of the pages. */
67599 decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager);
67600
67602 result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder);
67603 } else {
67604 result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder);
67605 }
67606 if (result != MA_SUCCESS) {
67607 goto done;
67608 }
67609
67610 /* Retrieve the total length of the file before marking the decoder are loaded. */
67611 result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames);
67612 if (result != MA_SUCCESS) {
67613 goto done; /* Failed to retrieve the length. */
67614 }
67615
67616 /*
67617 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
67618 and we don't want to have another thread trying to access the decoder while it's scanning.
67619 */
67620 pDataStream->isDecoderInitialized = MA_TRUE;
67621
67622 /* We have the decoder so we can now initialize our page buffer. */
67623 pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels);
67624
67625 pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks);
67626 if (pDataStream->pPageData == NULL) {
67627 ma_decoder_uninit(&pDataStream->decoder);
67628 result = MA_OUT_OF_MEMORY;
67629 goto done;
67630 }
67631
67632 /* Seek to our initial seek point before filling the initial pages. */
67634
67635 /* We have our decoder and our page buffer, so now we need to fill our pages. */
67636 ma_resource_manager_data_stream_fill_pages(pDataStream);
67637
67638 /* And now we're done. We want to make sure the result is MA_SUCCESS. */
67639 result = MA_SUCCESS;
67640
67641done:
67644
67645 /* 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). */
67646 c89atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result);
67647
67648 /* Only signal the other threads after the result has been set just for cleanliness sake. */
67651 }
67654 }
67655
67656 c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);
67657 return result;
67658}
67659
67660static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob)
67661{
67662 ma_resource_manager* pResourceManager;
67664
67665 MA_ASSERT(pJob != NULL);
67666
67668 MA_ASSERT(pDataStream != NULL);
67669
67670 pResourceManager = pDataStream->pResourceManager;
67671
67672 if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) {
67673 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
67674 }
67675
67676 /* If our status is not MA_UNAVAILABLE we have a bug somewhere. */
67677 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE);
67678
67679 if (pDataStream->isDecoderInitialized) {
67680 ma_decoder_uninit(&pDataStream->decoder);
67681 }
67682
67683 if (pDataStream->pPageData != NULL) {
67684 ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks);
67685 pDataStream->pPageData = NULL; /* Just in case... */
67686 }
67687
67688 ma_data_source_uninit(&pDataStream->ds);
67689
67690 /* The event needs to be signalled last. */
67693 }
67696 }
67697
67698 /*c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/
67699 return MA_SUCCESS;
67700}
67701
67702static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob)
67703{
67704 ma_result result = MA_SUCCESS;
67705 ma_resource_manager* pResourceManager;
67707
67708 MA_ASSERT(pJob != NULL);
67709
67711 MA_ASSERT(pDataStream != NULL);
67712
67713 pResourceManager = pDataStream->pResourceManager;
67714
67715 if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) {
67716 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
67717 }
67718
67719 /* For streams, the status should be MA_SUCCESS. */
67721 result = MA_INVALID_OPERATION;
67722 goto done;
67723 }
67724
67725 ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex);
67726
67727done:
67728 c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);
67729 return result;
67730}
67731
67732static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob)
67733{
67734 ma_result result = MA_SUCCESS;
67735 ma_resource_manager* pResourceManager;
67737
67738 MA_ASSERT(pJob != NULL);
67739
67741 MA_ASSERT(pDataStream != NULL);
67742
67743 pResourceManager = pDataStream->pResourceManager;
67744
67745 if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) {
67746 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
67747 }
67748
67749 /* For streams the status should be MA_SUCCESS for this to do anything. */
67750 if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) {
67751 result = MA_INVALID_OPERATION;
67752 goto done;
67753 }
67754
67755 /*
67756 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
67757 instead of initializing the decoder, we seek to a frame.
67758 */
67760
67761 /* After seeking we'll need to reload the pages. */
67762 ma_resource_manager_data_stream_fill_pages(pDataStream);
67763
67764 /* We need to let the public API know that we're done seeking. */
67765 c89atomic_fetch_sub_32(&pDataStream->seekCounter, 1);
67766
67767done:
67768 c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);
67769 return result;
67770}
67771
67773{
67774 if (pResourceManager == NULL || pJob == NULL) {
67775 return MA_INVALID_ARGS;
67776 }
67777
67778 return ma_job_process(pJob);
67779}
67780
67782{
67783 ma_result result;
67784 ma_job job;
67785
67786 if (pResourceManager == NULL) {
67787 return MA_INVALID_ARGS;
67788 }
67789
67790 /* This will return MA_CANCELLED if the next job is a quit job. */
67791 result = ma_resource_manager_next_job(pResourceManager, &job);
67792 if (result != MA_SUCCESS) {
67793 return result;
67794 }
67795
67796 return ma_job_process(&job);
67797}
67798#else
67799/* 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. */
67800static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
67801static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
67802static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
67803static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); }
67804static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); }
67805static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
67806static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
67807static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
67808static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
67809#endif /* MA_NO_RESOURCE_MANAGER */
67810
67811
67812#ifndef MA_NO_NODE_GRAPH
67813/* 10ms @ 48K = 480. Must never exceed 65535. */
67814#ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS
67815#define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480
67816#endif
67817
67818
67819static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime);
67820
67821MA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
67822{
67823 #ifndef MA_NO_GENERATION
67824 {
67825 ma_waveform_config waveformConfig;
67826 ma_waveform waveform;
67827
67828 waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400);
67829 ma_waveform_init(&waveformConfig, &waveform);
67830 ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL);
67831 }
67832 #else
67833 {
67834 (void)pFramesOut;
67835 (void)frameCount;
67836 (void)format;
67837 (void)channels;
67838 (void)sampleRate;
67839 #if defined(MA_DEBUG_OUTPUT)
67840 {
67841 #if _MSC_VER
67842 #pragma message ("ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.")
67843 #endif
67844 }
67845 #endif
67846 }
67847 #endif
67848}
67849
67850
67851
67852static ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume)
67853{
67854 ma_uint64 iSample;
67855 ma_uint64 sampleCount;
67856
67857 if (pDst == NULL || pSrc == NULL || channels == 0) {
67858 return MA_INVALID_ARGS;
67859 }
67860
67861 if (volume == 0) {
67862 return MA_SUCCESS; /* No changes if the volume is 0. */
67863 }
67864
67865 sampleCount = frameCount * channels;
67866
67867 if (volume == 1) {
67868 for (iSample = 0; iSample < sampleCount; iSample += 1) {
67869 pDst[iSample] += pSrc[iSample];
67870 }
67871 } else {
67872 for (iSample = 0; iSample < sampleCount; iSample += 1) {
67873 pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume);
67874 }
67875 }
67876
67877 return MA_SUCCESS;
67878}
67879
67880
67882{
67883 ma_node_graph_config config;
67884
67885 MA_ZERO_OBJECT(&config);
67886 config.channels = channels;
67887 config.nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS;
67888
67889 return config;
67890}
67891
67892
67893static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading)
67894{
67895 MA_ASSERT(pNodeGraph != NULL);
67896 c89atomic_exchange_32(&pNodeGraph->isReading, isReading);
67897}
67898
67899#if 0
67900static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph)
67901{
67902 MA_ASSERT(pNodeGraph != NULL);
67903 return c89atomic_load_32(&pNodeGraph->isReading);
67904}
67905#endif
67906
67907
67908static void ma_node_graph_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
67909{
67910 ma_node_graph* pNodeGraph = (ma_node_graph*)pNode;
67911 ma_uint64 framesRead;
67912
67913 ma_node_graph_read_pcm_frames(pNodeGraph, ppFramesOut[0], *pFrameCountOut, &framesRead);
67914
67915 *pFrameCountOut = (ma_uint32)framesRead; /* Safe cast. */
67916
67917 (void)ppFramesIn;
67918 (void)pFrameCountIn;
67919}
67920
67921static ma_node_vtable g_node_graph_node_vtable =
67922{
67923 ma_node_graph_node_process_pcm_frames,
67924 NULL, /* onGetRequiredInputFrameCount */
67925 0, /* 0 input buses. */
67926 1, /* 1 output bus. */
67927 0 /* Flags. */
67928};
67929
67930static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
67931{
67932 MA_ASSERT(pNode != NULL);
67933 MA_ASSERT(ma_node_get_input_bus_count(pNode) == 1);
67934 MA_ASSERT(ma_node_get_output_bus_count(pNode) == 1);
67935
67936 /* Input channel count needs to be the same as the output channel count. */
67937 MA_ASSERT(ma_node_get_input_channels(pNode, 0) == ma_node_get_output_channels(pNode, 0));
67938
67939 /* We don't need to do anything here because it's a passthrough. */
67940 (void)pNode;
67941 (void)ppFramesIn;
67942 (void)pFrameCountIn;
67943 (void)ppFramesOut;
67944 (void)pFrameCountOut;
67945
67946#if 0
67947 /* The data has already been mixed. We just need to move it to the output buffer. */
67948 if (ppFramesIn != NULL) {
67949 ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0));
67950 }
67951#endif
67952}
67953
67954static ma_node_vtable g_node_graph_endpoint_vtable =
67955{
67956 ma_node_graph_endpoint_process_pcm_frames,
67957 NULL, /* onGetRequiredInputFrameCount */
67958 1, /* 1 input bus. */
67959 1, /* 1 output bus. */
67960 MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */
67961};
67962
67963MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph)
67964{
67965 ma_result result;
67966 ma_node_config baseConfig;
67967 ma_node_config endpointConfig;
67968
67969 if (pNodeGraph == NULL) {
67970 return MA_INVALID_ARGS;
67971 }
67972
67973 MA_ZERO_OBJECT(pNodeGraph);
67974 pNodeGraph->nodeCacheCapInFrames = pConfig->nodeCacheCapInFrames;
67975 if (pNodeGraph->nodeCacheCapInFrames == 0) {
67976 pNodeGraph->nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS;
67977 }
67978
67979
67980 /* Base node so we can use the node graph as a node into another graph. */
67981 baseConfig = ma_node_config_init();
67982 baseConfig.vtable = &g_node_graph_node_vtable;
67983 baseConfig.pOutputChannels = &pConfig->channels;
67984
67985 result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pNodeGraph->base);
67986 if (result != MA_SUCCESS) {
67987 return result;
67988 }
67989
67990
67991 /* Endpoint. */
67992 endpointConfig = ma_node_config_init();
67993 endpointConfig.vtable = &g_node_graph_endpoint_vtable;
67994 endpointConfig.pInputChannels = &pConfig->channels;
67995 endpointConfig.pOutputChannels = &pConfig->channels;
67996
67997 result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint);
67998 if (result != MA_SUCCESS) {
67999 ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks);
68000 return result;
68001 }
68002
68003 return MA_SUCCESS;
68004}
68005
68006MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks)
68007{
68008 if (pNodeGraph == NULL) {
68009 return;
68010 }
68011
68012 ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks);
68013}
68014
68016{
68017 if (pNodeGraph == NULL) {
68018 return NULL;
68019 }
68020
68021 return &pNodeGraph->endpoint;
68022}
68023
68024MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
68025{
68026 ma_result result = MA_SUCCESS;
68027 ma_uint64 totalFramesRead;
68028 ma_uint32 channels;
68029
68030 if (pFramesRead != NULL) {
68031 *pFramesRead = 0; /* Safety. */
68032 }
68033
68034 if (pNodeGraph == NULL) {
68035 return MA_INVALID_ARGS;
68036 }
68037
68038 channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0);
68039
68040
68041 /* We'll be nice and try to do a full read of all frameCount frames. */
68042 totalFramesRead = 0;
68043 while (totalFramesRead < frameCount) {
68044 ma_uint32 framesJustRead;
68045 ma_uint64 framesToRead = frameCount - totalFramesRead;
68046
68047 if (framesToRead > 0xFFFFFFFF) {
68048 framesToRead = 0xFFFFFFFF;
68049 }
68050
68051 ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE);
68052 {
68053 result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint));
68054 }
68055 ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE);
68056
68057 totalFramesRead += framesJustRead;
68058
68059 if (result != MA_SUCCESS) {
68060 break;
68061 }
68062
68063 /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */
68064 if (framesJustRead == 0) {
68065 break;
68066 }
68067 }
68068
68069 /* 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. */
68070 if (totalFramesRead < frameCount) {
68071 ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels);
68072 }
68073
68074 if (pFramesRead != NULL) {
68075 *pFramesRead = totalFramesRead;
68076 }
68077
68078 return result;
68079}
68080
68082{
68083 if (pNodeGraph == NULL) {
68084 return 0;
68085 }
68086
68087 return ma_node_get_output_channels(&pNodeGraph->endpoint, 0);
68088}
68089
68091{
68092 if (pNodeGraph == NULL) {
68093 return 0;
68094 }
68095
68096 return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */
68097}
68098
68100{
68101 if (pNodeGraph == NULL) {
68102 return MA_INVALID_ARGS;
68103 }
68104
68105 return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */
68106}
68107
68108
68109#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. */
68110
68111static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus)
68112{
68113 MA_ASSERT(pOutputBus != NULL);
68114 MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT);
68115 MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode));
68116 MA_ASSERT(channels < 256);
68117
68118 MA_ZERO_OBJECT(pOutputBus);
68119
68120 if (channels == 0) {
68121 return MA_INVALID_ARGS;
68122 }
68123
68124 pOutputBus->pNode = pNode;
68125 pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex;
68126 pOutputBus->channels = (ma_uint8)channels;
68127 pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */
68128 pOutputBus->volume = 1;
68129
68130 return MA_SUCCESS;
68131}
68132
68133static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus)
68134{
68135 ma_spinlock_lock(&pOutputBus->lock);
68136}
68137
68138static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus)
68139{
68140 ma_spinlock_unlock(&pOutputBus->lock);
68141}
68142
68143
68144static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus)
68145{
68146 return pOutputBus->channels;
68147}
68148
68149
68150static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead)
68151{
68152 if (hasRead) {
68153 c89atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ);
68154 } else {
68155 c89atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ);
68156 }
68157}
68158
68159static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus)
68160{
68161 return (c89atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0;
68162}
68163
68164
68165static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached)
68166{
68167 c89atomic_exchange_32(&pOutputBus->isAttached, isAttached);
68168}
68169
68170static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus)
68171{
68172 return c89atomic_load_32(&pOutputBus->isAttached);
68173}
68174
68175
68176static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume)
68177{
68178 MA_ASSERT(pOutputBus != NULL);
68179
68180 if (volume < 0.0f) {
68181 volume = 0.0f;
68182 }
68183
68184 c89atomic_exchange_f32(&pOutputBus->volume, volume);
68185
68186 return MA_SUCCESS;
68187}
68188
68189static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus)
68190{
68191 return c89atomic_load_f32((float*)&pOutputBus->volume);
68192}
68193
68194
68195static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus)
68196{
68197 MA_ASSERT(pInputBus != NULL);
68198 MA_ASSERT(channels < 256);
68199
68200 MA_ZERO_OBJECT(pInputBus);
68201
68202 if (channels == 0) {
68203 return MA_INVALID_ARGS;
68204 }
68205
68206 pInputBus->channels = (ma_uint8)channels;
68207
68208 return MA_SUCCESS;
68209}
68210
68211static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus)
68212{
68213 ma_spinlock_lock(&pInputBus->lock);
68214}
68215
68216static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus)
68217{
68218 ma_spinlock_unlock(&pInputBus->lock);
68219}
68220
68221
68222static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus)
68223{
68224 c89atomic_fetch_add_32(&pInputBus->nextCounter, 1);
68225}
68226
68227static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus)
68228{
68229 c89atomic_fetch_sub_32(&pInputBus->nextCounter, 1);
68230}
68231
68232static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus)
68233{
68234 return c89atomic_load_32(&pInputBus->nextCounter);
68235}
68236
68237
68238static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus)
68239{
68240 return pInputBus->channels;
68241}
68242
68243
68244static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
68245{
68246 MA_ASSERT(pInputBus != NULL);
68247 MA_ASSERT(pOutputBus != NULL);
68248
68249 /*
68250 Mark the output bus as detached first. This will prevent future iterations on the audio thread
68251 from iterating this output bus.
68252 */
68253 ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE);
68254
68255 /*
68256 We cannot use the output bus lock here since it'll be getting used at a higher level, but we do
68257 still need to use the input bus lock since we'll be updating pointers on two different output
68258 buses. The same rules apply here as the attaching case. Although we're using a lock here, we're
68259 *not* using a lock when iterating over the list in the audio thread. We therefore need to craft
68260 this in a way such that the iteration on the audio thread doesn't break.
68261
68262 The the first thing to do is swap out the "next" pointer of the previous output bus with the
68263 new "next" output bus. This is the operation that matters for iteration on the audio thread.
68264 After that, the previous pointer on the new "next" pointer needs to be updated, after which
68265 point the linked list will be in a good state.
68266 */
68267 ma_node_input_bus_lock(pInputBus);
68268 {
68269 ma_node_output_bus* pOldPrev = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pPrev);
68270 ma_node_output_bus* pOldNext = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext);
68271
68272 if (pOldPrev != NULL) {
68273 c89atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */
68274 }
68275 if (pOldNext != NULL) {
68276 c89atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */
68277 }
68278 }
68279 ma_node_input_bus_unlock(pInputBus);
68280
68281 /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */
68282 c89atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */
68283 c89atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */
68284 pOutputBus->pInputNode = NULL;
68285 pOutputBus->inputNodeInputBusIndex = 0;
68286
68287
68288 /*
68289 For thread-safety reasons, we don't want to be returning from this straight away. We need to
68290 wait for the audio thread to finish with the output bus. There's two things we need to wait
68291 for. The first is the part that selects the next output bus in the list, and the other is the
68292 part that reads from the output bus. Basically all we're doing is waiting for the input bus
68293 to stop referencing the output bus.
68294
68295 We're doing this part last because we want the section above to run while the audio thread
68296 is finishing up with the output bus, just for efficiency reasons. We marked the output bus as
68297 detached right at the top of this function which is going to prevent the audio thread from
68298 iterating the output bus again.
68299 */
68300
68301 /* Part 1: Wait for the current iteration to complete. */
68302 while (ma_node_input_bus_get_next_counter(pInputBus) > 0) {
68303 ma_yield();
68304 }
68305
68306 /* Part 2: Wait for any reads to complete. */
68307 while (c89atomic_load_32(&pOutputBus->refCount) > 0) {
68308 ma_yield();
68309 }
68310
68311 /*
68312 At this point we're done detaching and we can be guaranteed that the audio thread is not going
68313 to attempt to reference this output bus again (until attached again).
68314 */
68315}
68316
68317#if 0 /* Not used at the moment, but leaving here in case I need it later. */
68318static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
68319{
68320 MA_ASSERT(pInputBus != NULL);
68321 MA_ASSERT(pOutputBus != NULL);
68322
68323 ma_node_output_bus_lock(pOutputBus);
68324 {
68325 ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus);
68326 }
68327 ma_node_output_bus_unlock(pOutputBus);
68328}
68329#endif
68330
68331static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex)
68332{
68333 MA_ASSERT(pInputBus != NULL);
68334 MA_ASSERT(pOutputBus != NULL);
68335
68336 ma_node_output_bus_lock(pOutputBus);
68337 {
68338 ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pInputNode);
68339
68340 /* Detach from any existing attachment first if necessary. */
68341 if (pOldInputNode != NULL) {
68342 ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus);
68343 }
68344
68345 /*
68346 At this point we can be sure the output bus is not attached to anything. The linked list in the
68347 old input bus has been updated so that pOutputBus will not get iterated again.
68348 */
68349 pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */
68350 pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex; /* As above. */
68351
68352 /*
68353 Now we need to attach the output bus to the linked list. This involves updating two pointers on
68354 two different output buses so I'm going to go ahead and keep this simple and just use a lock.
68355 There are ways to do this without a lock, but it's just too hard to maintain for it's value.
68356
68357 Although we're locking here, it's important to remember that we're *not* locking when iterating
68358 and reading audio data since that'll be running on the audio thread. As a result we need to be
68359 careful how we craft this so that we don't break iteration. What we're going to do is always
68360 attach the new item so that it becomes the first item in the list. That way, as we're iterating
68361 we won't break any links in the list and iteration will continue safely. The detaching case will
68362 also be crafted in a way as to not break list iteration. It's important to remember to use
68363 atomic exchanges here since no locking is happening on the audio thread during iteration.
68364 */
68365 ma_node_input_bus_lock(pInputBus);
68366 {
68367 ma_node_output_bus* pNewPrev = &pInputBus->head;
68368 ma_node_output_bus* pNewNext = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext);
68369
68370 /* Update the local output bus. */
68371 c89atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev);
68372 c89atomic_exchange_ptr(&pOutputBus->pNext, pNewNext);
68373
68374 /* Update the other output buses to point back to the local output bus. */
68375 c89atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */
68376
68377 /* Do the previous pointer last. This is only used for detachment. */
68378 if (pNewNext != NULL) {
68379 c89atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus);
68380 }
68381 }
68382 ma_node_input_bus_unlock(pInputBus);
68383
68384 /*
68385 Mark the node as attached last. This is used to controlling whether or the output bus will be
68386 iterated on the audio thread. Mainly required for detachment purposes.
68387 */
68388 ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE);
68389 }
68390 ma_node_output_bus_unlock(pOutputBus);
68391}
68392
68393static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
68394{
68395 ma_node_output_bus* pNext;
68396
68397 MA_ASSERT(pInputBus != NULL);
68398
68399 if (pOutputBus == NULL) {
68400 return NULL;
68401 }
68402
68403 ma_node_input_bus_next_begin(pInputBus);
68404 {
68405 pNext = pOutputBus;
68406 for (;;) {
68407 pNext = (ma_node_output_bus*)c89atomic_load_ptr(&pNext->pNext);
68408 if (pNext == NULL) {
68409 break; /* Reached the end. */
68410 }
68411
68412 if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) {
68413 continue; /* The node is not attached. Keep checking. */
68414 }
68415
68416 /* The next node has been selected. */
68417 break;
68418 }
68419
68420 /* We need to increment the reference count of the selected node. */
68421 if (pNext != NULL) {
68422 c89atomic_fetch_add_32(&pNext->refCount, 1);
68423 }
68424
68425 /* The previous node is no longer being referenced. */
68426 c89atomic_fetch_sub_32(&pOutputBus->refCount, 1);
68427 }
68428 ma_node_input_bus_next_end(pInputBus);
68429
68430 return pNext;
68431}
68432
68433static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus)
68434{
68435 return ma_node_input_bus_next(pInputBus, &pInputBus->head);
68436}
68437
68438
68439
68440static 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)
68441{
68442 ma_result result = MA_SUCCESS;
68443 ma_node_output_bus* pOutputBus;
68444 ma_node_output_bus* pFirst;
68445 ma_uint32 inputChannels;
68446 ma_bool32 doesOutputBufferHaveContent = MA_FALSE;
68447
68448 /*
68449 This will be called from the audio thread which means we can't be doing any locking. Basically,
68450 this function will not perfom any locking, whereas attaching and detaching will, but crafted in
68451 such a way that we don't need to perform any locking here. The important thing to remember is
68452 to always iterate in a forward direction.
68453
68454 In order to process any data we need to first read from all input buses. That's where this
68455 function comes in. This iterates over each of the attachments and accumulates/mixes them. We
68456 also convert the channels to the nodes output channel count before mixing. We want to do this
68457 channel conversion so that the caller of this function can invoke the processing callback
68458 without having to do it themselves.
68459
68460 When we iterate over each of the attachments on the input bus, we need to read as much data as
68461 we can from each of them so that we don't end up with holes between each of the attachments. To
68462 do this, we need to read from each attachment in a loop and read as many frames as we can, up
68463 to `frameCount`.
68464 */
68465 MA_ASSERT(pInputNode != NULL);
68466 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. */
68467
68468 *pFramesRead = 0; /* Safety. */
68469
68470 inputChannels = ma_node_input_bus_get_channels(pInputBus);
68471
68472 /*
68473 We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They
68474 are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first()
68475 once per iteration, however we have an optimization to checks whether or not it's the first item in
68476 the list. We therefore need to store a pointer to the first item rather than repeatedly calling
68477 ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it
68478 after calling ma_node_input_bus_next(), which we won't be.
68479 */
68480 pFirst = ma_node_input_bus_first(pInputBus);
68481 if (pFirst == NULL) {
68482 return MA_SUCCESS; /* No attachments. Read nothing. */
68483 }
68484
68485 for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) {
68486 ma_uint32 framesProcessed = 0;
68487 ma_bool32 isSilentOutput = MA_FALSE;
68488
68489 MA_ASSERT(pOutputBus->pNode != NULL);
68490
68491 isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0;
68492
68493 if (pFramesOut != NULL) {
68494 /* Read. */
68495 float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)];
68496 ma_uint32 tempCapInFrames = ma_countof(temp) / inputChannels;
68497
68498 while (framesProcessed < frameCount) {
68499 float* pRunningFramesOut;
68500 ma_uint32 framesToRead;
68501 ma_uint32 framesJustRead;
68502
68503 framesToRead = frameCount - framesProcessed;
68504 if (framesToRead > tempCapInFrames) {
68505 framesToRead = tempCapInFrames;
68506 }
68507
68508 pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels);
68509
68510 if (doesOutputBufferHaveContent == MA_FALSE) {
68511 /* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */
68512 result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed);
68513 } else {
68514 /* Slow path. Not the first attachment. Mixing required. */
68515 result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, temp, framesToRead, &framesJustRead, globalTime + framesProcessed);
68516 if (result == MA_SUCCESS || result == MA_AT_END) {
68517 if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */
68518 ma_mix_pcm_frames_f32(pRunningFramesOut, temp, framesJustRead, inputChannels, /*volume*/1);
68519 }
68520 }
68521 }
68522
68523 framesProcessed += framesJustRead;
68524
68525 /* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */
68526 if (result != MA_SUCCESS) {
68527 break;
68528 }
68529
68530 /* If we didn't read anything, abort so we don't get stuck in a loop. */
68531 if (framesJustRead == 0) {
68532 break;
68533 }
68534 }
68535
68536 /* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */
68537 if (pOutputBus == pFirst && framesProcessed < frameCount) {
68538 ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels);
68539 }
68540
68541 if (isSilentOutput == MA_FALSE) {
68542 doesOutputBufferHaveContent = MA_TRUE;
68543 }
68544 } else {
68545 /* Seek. */
68546 ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime);
68547 }
68548 }
68549
68550 /* If we didn't output anything, output silence. */
68551 if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) {
68552 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, inputChannels);
68553 }
68554
68555 /* In this path we always "process" the entire amount. */
68556 *pFramesRead = frameCount;
68557
68558 return result;
68559}
68560
68561
68563{
68564 ma_node_config config;
68565
68566 MA_ZERO_OBJECT(&config);
68567 config.initialState = ma_node_state_started; /* Nodes are started by default. */
68570
68571 return config;
68572}
68573
68574
68575
68576static ma_result ma_node_detach_full(ma_node* pNode);
68577
68578static float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex)
68579{
68580 ma_node_base* pNodeBase = (ma_node_base*)pNode;
68581 ma_uint32 iInputBus;
68582 float* pBasePtr;
68583
68584 MA_ASSERT(pNodeBase != NULL);
68585
68586 /* Input data is stored at the front of the buffer. */
68587 pBasePtr = pNodeBase->pCachedData;
68588 for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) {
68589 pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]);
68590 }
68591
68592 return pBasePtr;
68593}
68594
68595static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex)
68596{
68597 ma_node_base* pNodeBase = (ma_node_base*)pNode;
68598 ma_uint32 iInputBus;
68599 ma_uint32 iOutputBus;
68600 float* pBasePtr;
68601
68602 MA_ASSERT(pNodeBase != NULL);
68603
68604 /* Cached output data starts after the input data. */
68605 pBasePtr = pNodeBase->pCachedData;
68606 for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) {
68607 pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]);
68608 }
68609
68610 for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) {
68611 pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]);
68612 }
68613
68614 return pBasePtr;
68615}
68616
68617
68618typedef struct
68619{
68620 size_t sizeInBytes;
68621 size_t inputBusOffset;
68622 size_t outputBusOffset;
68623 size_t cachedDataOffset;
68624 ma_uint32 inputBusCount; /* So it doesn't have to be calculated twice. */
68625 ma_uint32 outputBusCount; /* So it doesn't have to be calculated twice. */
68626} ma_node_heap_layout;
68627
68628static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount)
68629{
68630 ma_uint32 inputBusCount;
68631 ma_uint32 outputBusCount;
68632
68633 MA_ASSERT(pConfig != NULL);
68634 MA_ASSERT(pInputBusCount != NULL);
68635 MA_ASSERT(pOutputBusCount != NULL);
68636
68637 /* 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. */
68639 inputBusCount = pConfig->inputBusCount;
68640 } else {
68641 inputBusCount = pConfig->vtable->inputBusCount;
68642
68643 if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) {
68644 return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */
68645 }
68646 }
68647
68649 outputBusCount = pConfig->outputBusCount;
68650 } else {
68651 outputBusCount = pConfig->vtable->outputBusCount;
68652
68653 if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) {
68654 return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */
68655 }
68656 }
68657
68658 /* Bus counts must be within limits. */
68659 if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) {
68660 return MA_INVALID_ARGS;
68661 }
68662
68663
68664 /* We must have channel counts for each bus. */
68665 if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) {
68666 return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */
68667 }
68668
68669
68670 /* Some special rules for passthrough nodes. */
68671 if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {
68672 if (pConfig->vtable->inputBusCount != 1 || pConfig->vtable->outputBusCount != 1) {
68673 return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 input bus and 1 output bus. */
68674 }
68675
68676 if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) {
68677 return MA_INVALID_ARGS; /* Passthrough nodes must have the same number of channels between input and output nodes. */
68678 }
68679 }
68680
68681
68682 *pInputBusCount = inputBusCount;
68683 *pOutputBusCount = outputBusCount;
68684
68685 return MA_SUCCESS;
68686}
68687
68688static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout)
68689{
68690 ma_result result;
68691 ma_uint32 inputBusCount;
68692 ma_uint32 outputBusCount;
68693
68694 MA_ASSERT(pHeapLayout != NULL);
68695
68696 MA_ZERO_OBJECT(pHeapLayout);
68697
68698 if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) {
68699 return MA_INVALID_ARGS;
68700 }
68701
68702 result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount);
68703 if (result != MA_SUCCESS) {
68704 return result;
68705 }
68706
68707 pHeapLayout->sizeInBytes = 0;
68708
68709 /* Input buses. */
68710 if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) {
68711 pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes;
68712 pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount);
68713 } else {
68714 pHeapLayout->inputBusOffset = MA_SIZE_MAX; /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */
68715 }
68716
68717 /* Output buses. */
68718 if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) {
68719 pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes;
68720 pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount);
68721 } else {
68722 pHeapLayout->outputBusOffset = MA_SIZE_MAX;
68723 }
68724
68725 /*
68726 Cached audio data.
68727
68728 We need to allocate memory for a caching both input and output data. We have an optimization
68729 where no caching is necessary for specific conditions:
68730
68731 - The node has 0 inputs and 1 output.
68732
68733 When a node meets the above conditions, no cache is allocated.
68734
68735 The size choice for this buffer is a little bit finicky. We don't want to be too wasteful by
68736 allocating too much, but at the same time we want it be large enough so that enough frames can
68737 be processed for each call to ma_node_read_pcm_frames() so that it keeps things efficient. For
68738 now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile
68739 time. It might also be worth investigating whether or not this can be configured at run time.
68740 */
68741 if (inputBusCount == 0 && outputBusCount == 1) {
68742 /* Fast path. No cache needed. */
68743 pHeapLayout->cachedDataOffset = MA_SIZE_MAX;
68744 } else {
68745 /* Slow path. Cache needed. */
68746 size_t cachedDataSizeInBytes = 0;
68747 ma_uint32 iBus;
68748
68749 for (iBus = 0; iBus < inputBusCount; iBus += 1) {
68750 cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]);
68751 }
68752
68753 for (iBus = 0; iBus < outputBusCount; iBus += 1) {
68754 cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]);
68755 }
68756
68757 pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes;
68758 pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes);
68759 }
68760
68761
68762 /*
68763 Not technically part of the heap, but we can output the input and output bus counts so we can
68764 avoid a redundant call to ma_node_translate_bus_counts().
68765 */
68766 pHeapLayout->inputBusCount = inputBusCount;
68767 pHeapLayout->outputBusCount = outputBusCount;
68768
68769 /* Make sure allocation size is aligned. */
68770 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
68771
68772 return MA_SUCCESS;
68773}
68774
68775MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes)
68776{
68777 ma_result result;
68778 ma_node_heap_layout heapLayout;
68779
68780 if (pHeapSizeInBytes == NULL) {
68781 return MA_INVALID_ARGS;
68782 }
68783
68784 *pHeapSizeInBytes = 0;
68785
68786 result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout);
68787 if (result != MA_SUCCESS) {
68788 return result;
68789 }
68790
68791 *pHeapSizeInBytes = heapLayout.sizeInBytes;
68792
68793 return MA_SUCCESS;
68794}
68795
68796MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode)
68797{
68798 ma_node_base* pNodeBase = (ma_node_base*)pNode;
68799 ma_result result;
68800 ma_node_heap_layout heapLayout;
68801 ma_uint32 iInputBus;
68802 ma_uint32 iOutputBus;
68803
68804 if (pNodeBase == NULL) {
68805 return MA_INVALID_ARGS;
68806 }
68807
68808 MA_ZERO_OBJECT(pNodeBase);
68809
68810 result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout);
68811 if (result != MA_SUCCESS) {
68812 return result;
68813 }
68814
68815 pNodeBase->_pHeap = pHeap;
68816 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
68817
68818 pNodeBase->pNodeGraph = pNodeGraph;
68819 pNodeBase->vtable = pConfig->vtable;
68820 pNodeBase->state = pConfig->initialState;
68821 pNodeBase->stateTimes[ma_node_state_started] = 0;
68822 pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */
68823 pNodeBase->inputBusCount = heapLayout.inputBusCount;
68824 pNodeBase->outputBusCount = heapLayout.outputBusCount;
68825
68826 if (heapLayout.inputBusOffset != MA_SIZE_MAX) {
68827 pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset);
68828 } else {
68829 pNodeBase->pInputBuses = pNodeBase->_inputBuses;
68830 }
68831
68832 if (heapLayout.outputBusOffset != MA_SIZE_MAX) {
68833 pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset);
68834 } else {
68835 pNodeBase->pOutputBuses = pNodeBase->_outputBuses;
68836 }
68837
68838 if (heapLayout.cachedDataOffset != MA_SIZE_MAX) {
68839 pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset);
68840 pNodeBase->cachedDataCapInFramesPerBus = pNodeGraph->nodeCacheCapInFrames;
68841 } else {
68842 pNodeBase->pCachedData = NULL;
68843 }
68844
68845
68846
68847 /* We need to run an initialization step for each input and output bus. */
68848 for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) {
68849 result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]);
68850 if (result != MA_SUCCESS) {
68851 return result;
68852 }
68853 }
68854
68855 for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) {
68856 result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]);
68857 if (result != MA_SUCCESS) {
68858 return result;
68859 }
68860 }
68861
68862
68863 /* The cached data needs to be initialized to silence (or a sine wave tone if we're debugging). */
68864 if (pNodeBase->pCachedData != NULL) {
68865 ma_uint32 iBus;
68866
68867 #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. */
68868 /* For safety we'll go ahead and default the buffer to silence. */
68869 for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) {
68870 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]));
68871 }
68872 for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) {
68873 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]));
68874 }
68875 #else
68876 /* For debugging. Default to a sine wave. */
68877 for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) {
68878 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);
68879 }
68880 for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) {
68881 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);
68882 }
68883 #endif
68884 }
68885
68886 return MA_SUCCESS;
68887}
68888
68889MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode)
68890{
68891 ma_result result;
68892 size_t heapSizeInBytes;
68893 void* pHeap;
68894
68895 result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes);
68896 if (result != MA_SUCCESS) {
68897 return result;
68898 }
68899
68900 if (heapSizeInBytes > 0) {
68901 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
68902 if (pHeap == NULL) {
68903 return MA_OUT_OF_MEMORY;
68904 }
68905 } else {
68906 pHeap = NULL;
68907 }
68908
68909 result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode);
68910 if (result != MA_SUCCESS) {
68911 ma_free(pHeap, pAllocationCallbacks);
68912 return result;
68913 }
68914
68915 ((ma_node_base*)pNode)->_ownsHeap = MA_TRUE;
68916 return MA_SUCCESS;
68917}
68918
68919MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
68920{
68921 ma_node_base* pNodeBase = (ma_node_base*)pNode;
68922
68923 if (pNodeBase == NULL) {
68924 return;
68925 }
68926
68927 /*
68928 The first thing we need to do is fully detach the node. This will detach all inputs and
68929 outputs. We need to do this first because it will sever the connection with the node graph and
68930 allow us to complete uninitialization without needing to worry about thread-safety with the
68931 audio thread. The detachment process will wait for any local processing of the node to finish.
68932 */
68933 ma_node_detach_full(pNode);
68934
68935 /*
68936 At this point the node should be completely unreferenced by the node graph and we can finish up
68937 the uninitialization process without needing to worry about thread-safety.
68938 */
68939 if (pNodeBase->_ownsHeap) {
68940 ma_free(pNodeBase->_pHeap, pAllocationCallbacks);
68941 }
68942}
68943
68945{
68946 if (pNode == NULL) {
68947 return NULL;
68948 }
68949
68950 return ((const ma_node_base*)pNode)->pNodeGraph;
68951}
68952
68954{
68955 if (pNode == NULL) {
68956 return 0;
68957 }
68958
68959 return ((ma_node_base*)pNode)->inputBusCount;
68960}
68961
68963{
68964 if (pNode == NULL) {
68965 return 0;
68966 }
68967
68968 return ((ma_node_base*)pNode)->outputBusCount;
68969}
68970
68971
68972MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex)
68973{
68974 const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
68975
68976 if (pNode == NULL) {
68977 return 0;
68978 }
68979
68980 if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) {
68981 return 0; /* Invalid bus index. */
68982 }
68983
68984 return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]);
68985}
68986
68987MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex)
68988{
68989 const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
68990
68991 if (pNode == NULL) {
68992 return 0;
68993 }
68994
68995 if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
68996 return 0; /* Invalid bus index. */
68997 }
68998
68999 return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]);
69000}
69001
69002
69003static ma_result ma_node_detach_full(ma_node* pNode)
69004{
69005 ma_node_base* pNodeBase = (ma_node_base*)pNode;
69006 ma_uint32 iInputBus;
69007
69008 if (pNodeBase == NULL) {
69009 return MA_INVALID_ARGS;
69010 }
69011
69012 /*
69013 Make sure the node is completely detached first. This will not return until the output bus is
69014 guaranteed to no longer be referenced by the audio thread.
69015 */
69017
69018 /*
69019 At this point all output buses will have been detached from the graph and we can be guaranteed
69020 that none of it's input nodes will be getting processed by the graph. We can detach these
69021 without needing to worry about the audio thread touching them.
69022 */
69023 for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) {
69024 ma_node_input_bus* pInputBus;
69025 ma_node_output_bus* pOutputBus;
69026
69027 pInputBus = &pNodeBase->pInputBuses[iInputBus];
69028
69029 /*
69030 This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those
69031 functions are specifically for the audio thread. We'll instead just manually iterate using standard
69032 linked list logic. We don't need to worry about the audio thread referencing these because the step
69033 above severed the connection to the graph.
69034 */
69035 for (pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext)) {
69036 ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */
69037 }
69038 }
69039
69040 return MA_SUCCESS;
69041}
69042
69044{
69045 ma_result result = MA_SUCCESS;
69046 ma_node_base* pNodeBase = (ma_node_base*)pNode;
69047 ma_node_base* pInputNodeBase;
69048
69049 if (pNode == NULL) {
69050 return MA_INVALID_ARGS;
69051 }
69052
69053 if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
69054 return MA_INVALID_ARGS; /* Invalid output bus index. */
69055 }
69056
69057 /* We need to lock the output bus because we need to inspect the input node and grab it's input bus. */
69058 ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]);
69059 {
69060 pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode;
69061 if (pInputNodeBase != NULL) {
69062 ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]);
69063 }
69064 }
69065 ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]);
69066
69067 return result;
69068}
69069
69071{
69072 ma_uint32 iOutputBus;
69073
69074 if (pNode == NULL) {
69075 return MA_INVALID_ARGS;
69076 }
69077
69078 for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) {
69079 ma_node_detach_output_bus(pNode, iOutputBus);
69080 }
69081
69082 return MA_SUCCESS;
69083}
69084
69085MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex)
69086{
69087 ma_node_base* pNodeBase = (ma_node_base*)pNode;
69088 ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode;
69089
69090 if (pNodeBase == NULL || pOtherNodeBase == NULL) {
69091 return MA_INVALID_ARGS;
69092 }
69093
69094 if (pNodeBase == pOtherNodeBase) {
69095 return MA_INVALID_OPERATION; /* Cannot attach a node to itself. */
69096 }
69097
69098 if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) {
69099 return MA_INVALID_OPERATION; /* Invalid bus index. */
69100 }
69101
69102 /* The output channel count of the output node must be the same as the input channel count of the input node. */
69103 if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) {
69104 return MA_INVALID_OPERATION; /* Channel count is incompatible. */
69105 }
69106
69107 /* This will deal with detaching if the output bus is already attached to something. */
69108 ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex);
69109
69110 return MA_SUCCESS;
69111}
69112
69113MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume)
69114{
69115 ma_node_base* pNodeBase = (ma_node_base*)pNode;
69116
69117 if (pNodeBase == NULL) {
69118 return MA_INVALID_ARGS;
69119 }
69120
69121 if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
69122 return MA_INVALID_ARGS; /* Invalid bus index. */
69123 }
69124
69125 return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume);
69126}
69127
69128MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex)
69129{
69130 const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
69131
69132 if (pNodeBase == NULL) {
69133 return 0;
69134 }
69135
69136 if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
69137 return 0; /* Invalid bus index. */
69138 }
69139
69140 return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]);
69141}
69142
69144{
69145 ma_node_base* pNodeBase = (ma_node_base*)pNode;
69146
69147 if (pNodeBase == NULL) {
69148 return MA_INVALID_ARGS;
69149 }
69150
69151 c89atomic_exchange_i32(&pNodeBase->state, state);
69152
69153 return MA_SUCCESS;
69154}
69155
69157{
69158 const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
69159
69160 if (pNodeBase == NULL) {
69161 return ma_node_state_stopped;
69162 }
69163
69164 return (ma_node_state)c89atomic_load_i32(&pNodeBase->state);
69165}
69166
69168{
69169 if (pNode == NULL) {
69170 return MA_INVALID_ARGS;
69171 }
69172
69173 /* Validation check for safety since we'll be using this as an index into stateTimes[]. */
69174 if (state != ma_node_state_started && state != ma_node_state_stopped) {
69175 return MA_INVALID_ARGS;
69176 }
69177
69178 c89atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime);
69179
69180 return MA_SUCCESS;
69181}
69182
69184{
69185 if (pNode == NULL) {
69186 return 0;
69187 }
69188
69189 /* Validation check for safety since we'll be using this as an index into stateTimes[]. */
69190 if (state != ma_node_state_started && state != ma_node_state_stopped) {
69191 return 0;
69192 }
69193
69194 return c89atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]);
69195}
69196
69198{
69199 if (pNode == NULL) {
69200 return ma_node_state_stopped;
69201 }
69202
69203 return ma_node_get_state_by_time_range(pNode, globalTime, globalTime);
69204}
69205
69206MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd)
69207{
69208 ma_node_state state;
69209
69210 if (pNode == NULL) {
69211 return ma_node_state_stopped;
69212 }
69213
69214 state = ma_node_get_state(pNode);
69215
69216 /* An explicitly stopped node is always stopped. */
69217 if (state == ma_node_state_stopped) {
69218 return ma_node_state_stopped;
69219 }
69220
69221 /*
69222 Getting here means the node is marked as started, but it may still not be truly started due to
69223 it's start time not having been reached yet. Also, the stop time may have also been reached in
69224 which case it'll be considered stopped.
69225 */
69226 if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) {
69227 return ma_node_state_stopped; /* Start time has not yet been reached. */
69228 }
69229
69230 if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) {
69231 return ma_node_state_stopped; /* Stop time has been reached. */
69232 }
69233
69234 /* Getting here means the node is marked as started and is within it's start/stop times. */
69235 return ma_node_state_started;
69236}
69237
69239{
69240 if (pNode == NULL) {
69241 return 0;
69242 }
69243
69244 return c89atomic_load_64(&((ma_node_base*)pNode)->localTime);
69245}
69246
69248{
69249 if (pNode == NULL) {
69250 return MA_INVALID_ARGS;
69251 }
69252
69253 c89atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime);
69254
69255 return MA_SUCCESS;
69256}
69257
69258
69259
69260static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
69261{
69262 ma_node_base* pNodeBase = (ma_node_base*)pNode;
69263
69264 MA_ASSERT(pNode != NULL);
69265
69266 if (pNodeBase->vtable->onProcess) {
69267 pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
69268 }
69269}
69270
69271static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime)
69272{
69273 ma_node_base* pNodeBase = (ma_node_base*)pNode;
69274 ma_result result = MA_SUCCESS;
69275 ma_uint32 iInputBus;
69276 ma_uint32 iOutputBus;
69277 ma_uint32 inputBusCount;
69278 ma_uint32 outputBusCount;
69279 ma_uint32 totalFramesRead = 0;
69280 float* ppFramesIn[MA_MAX_NODE_BUS_COUNT];
69281 float* ppFramesOut[MA_MAX_NODE_BUS_COUNT];
69282 ma_uint64 globalTimeBeg;
69283 ma_uint64 globalTimeEnd;
69284 ma_uint64 startTime;
69285 ma_uint64 stopTime;
69286 ma_uint32 timeOffsetBeg;
69287 ma_uint32 timeOffsetEnd;
69288 ma_uint32 frameCountIn;
69289 ma_uint32 frameCountOut;
69290
69291 /*
69292 pFramesRead is mandatory. It must be used to determine how many frames were read. It's normal and
69293 expected that the number of frames read may be different to that requested. Therefore, the caller
69294 must look at this value to correctly determine how many frames were read.
69295 */
69296 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. */
69297 if (pFramesRead == NULL) {
69298 return MA_INVALID_ARGS;
69299 }
69300
69301 *pFramesRead = 0; /* Safety. */
69302
69303 if (pNodeBase == NULL) {
69304 return MA_INVALID_ARGS;
69305 }
69306
69307 if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) {
69308 return MA_INVALID_ARGS; /* Invalid output bus index. */
69309 }
69310
69311 /* Don't do anything if we're in a stopped state. */
69312 if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) {
69313 return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */
69314 }
69315
69316
69317 globalTimeBeg = globalTime;
69318 globalTimeEnd = globalTime + frameCount;
69321
69322 /*
69323 At this point we know that we are inside our start/stop times. However, we may need to adjust
69324 our frame count and output pointer to accomodate since we could be straddling the time period
69325 that this function is getting called for.
69326
69327 It's possible (and likely) that the start time does not line up with the output buffer. We
69328 therefore need to offset it by a number of frames to accomodate. The same thing applies for
69329 the stop time.
69330 */
69331 timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0;
69332 timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0;
69333
69334 /* Trim based on the start offset. We need to silence the start of the buffer. */
69335 if (timeOffsetBeg > 0) {
69336 ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex));
69337 pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex);
69338 frameCount -= timeOffsetBeg;
69339 }
69340
69341 /* 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. */
69342 if (timeOffsetEnd > 0) {
69343 frameCount -= timeOffsetEnd;
69344 }
69345
69346
69347 /* We run on different paths depending on the bus counts. */
69348 inputBusCount = ma_node_get_input_bus_count(pNode);
69349 outputBusCount = ma_node_get_output_bus_count(pNode);
69350
69351 /*
69352 Run a simplified path when there are no inputs and one output. In this case there's nothing to
69353 actually read and we can go straight to output. This is a very common scenario because the vast
69354 majority of data source nodes will use this setup so this optimization I think is worthwhile.
69355 */
69356 if (inputBusCount == 0 && outputBusCount == 1) {
69357 /* Fast path. No need to read from input and no need for any caching. */
69358 frameCountIn = 0;
69359 frameCountOut = frameCount; /* Just read as much as we can. The callback will return what was actually read. */
69360
69361 ppFramesOut[0] = pFramesOut;
69362 ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut);
69363 totalFramesRead = frameCountOut;
69364 } else {
69365 /* Slow path. Need to read input data. */
69366 if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {
69367 /*
69368 Fast path. We're running a passthrough. We need to read directly into the output buffer, but
69369 still fire the callback so that event handling and trigger nodes can do their thing. Since
69370 it's a passthrough there's no need for any kind of caching logic.
69371 */
69372 MA_ASSERT(outputBusCount == inputBusCount);
69373 MA_ASSERT(outputBusCount == 1);
69374 MA_ASSERT(outputBusIndex == 0);
69375
69376 /* We just read directly from input bus to output buffer, and then afterwards fire the callback. */
69377 ppFramesOut[0] = pFramesOut;
69378 ppFramesIn[0] = ppFramesOut[0];
69379
69380 result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime);
69381 if (result == MA_SUCCESS) {
69382 /* Even though it's a passthrough, we still need to fire the callback. */
69383 frameCountIn = totalFramesRead;
69384 frameCountOut = totalFramesRead;
69385
69386 if (totalFramesRead > 0) {
69387 ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */
69388 }
69389
69390 /*
69391 A passthrough should never have modified the input and output frame counts. If you're
69392 triggering these assers you need to fix your processing callback.
69393 */
69394 MA_ASSERT(frameCountIn == totalFramesRead);
69395 MA_ASSERT(frameCountOut == totalFramesRead);
69396 }
69397 } else {
69398 /* Slow path. Need to do caching. */
69399 ma_uint32 framesToProcessIn;
69400 ma_uint32 framesToProcessOut;
69401 ma_bool32 consumeNullInput = MA_FALSE;
69402
69403 /*
69404 We use frameCount as a basis for the number of frames to read since that's what's being
69405 requested, however we still need to clamp it to whatever can fit in the cache.
69406
69407 This will also be used as the basis for determining how many input frames to read. This is
69408 not ideal because it can result in too many input frames being read which introduces latency.
69409 To solve this, nodes can implement an optional callback called onGetRequiredInputFrameCount
69410 which is used as hint to miniaudio as to how many input frames it needs to read at a time. This
69411 callback is completely optional, and if it's not set, miniaudio will assume `frameCount`.
69412
69413 This function will be called multiple times for each period of time, once for each output node.
69414 We cannot read from each input node each time this function is called. Instead we need to check
69415 whether or not this is first output bus to be read from for this time period, and if so, read
69416 from our input data.
69417
69418 To determine whether or not we're ready to read data, we check a flag. There will be one flag
69419 for each output. When the flag is set, it means data has been read previously and that we're
69420 ready to advance time forward for our input nodes by reading fresh data.
69421 */
69422 framesToProcessOut = frameCount;
69423 if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) {
69424 framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus;
69425 }
69426
69427 framesToProcessIn = frameCount;
69428 if (pNodeBase->vtable->onGetRequiredInputFrameCount) {
69429 pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut, &framesToProcessIn); /* <-- It does not matter if this fails. */
69430 }
69431 if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) {
69432 framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus;
69433 }
69434
69435
69436 MA_ASSERT(framesToProcessIn <= 0xFFFF);
69437 MA_ASSERT(framesToProcessOut <= 0xFFFF);
69438
69439 if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) {
69440 /* Getting here means we need to do another round of processing. */
69441 pNodeBase->cachedFrameCountOut = 0;
69442
69443 for (;;) {
69444 frameCountOut = 0;
69445
69446 /*
69447 We need to prepare our output frame pointers for processing. In the same iteration we need
69448 to mark every output bus as unread so that future calls to this function for different buses
69449 for the current time period don't pull in data when they should instead be reading from cache.
69450 */
69451 for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) {
69452 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. */
69453 ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus);
69454 }
69455
69456 /* We only need to read from input buses if there isn't already some data in the cache. */
69457 if (pNodeBase->cachedFrameCountIn == 0) {
69458 ma_uint32 maxFramesReadIn = 0;
69459
69460 /* Here is where we pull in data from the input buses. This is what will trigger an advance in time. */
69461 for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) {
69462 ma_uint32 framesRead;
69463
69464 /* The first thing to do is get the offset within our bulk allocation to store this input data. */
69465 ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus);
69466
69467 /* 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. */
69468 result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime);
69469 if (result != MA_SUCCESS) {
69470 /* It doesn't really matter if we fail because we'll just fill with silence. */
69471 framesRead = 0; /* Just for safety, but I don't think it's really needed. */
69472 }
69473
69474 /* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */
69475 /* Any leftover frames need to silenced for safety. */
69476 if (framesRead < framesToProcessIn) {
69477 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));
69478 }
69479
69480 maxFramesReadIn = ma_max(maxFramesReadIn, framesRead);
69481 }
69482
69483 /* This was a fresh load of input data so reset our consumption counter. */
69484 pNodeBase->consumedFrameCountIn = 0;
69485
69486 /*
69487 We don't want to keep processing if there's nothing to process, so set the number of cached
69488 input frames to the maximum number we read from each attachment (the lesser will be padded
69489 with silence). If we didn't read anything, this will be set to 0 and the entire buffer will
69490 have been assigned to silence. This being equal to 0 is an important property for us because
69491 it allows us to detect when NULL can be passed into the processing callback for the input
69492 buffer for the purpose of continuous processing.
69493 */
69494 pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn;
69495 } else {
69496 /* We don't need to read anything, but we do need to prepare our input frame pointers. */
69497 for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) {
69498 ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus));
69499 }
69500 }
69501
69502 /*
69503 At this point we have our input data so now we need to do some processing. Sneaky little
69504 optimization here - we can set the pointer to the output buffer for this output bus so
69505 that the final copy into the output buffer is done directly by onProcess().
69506 */
69507 if (pFramesOut != NULL) {
69508 ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex));
69509 }
69510
69511
69512 /* Give the processing function the entire capacity of the output buffer. */
69513 frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut);
69514
69515 /*
69516 We need to treat nodes with continuous processing a little differently. For these ones,
69517 we always want to fire the callback with the requested number of frames, regardless of
69518 pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass
69519 in NULL for the input buffer to the callback.
69520 */
69521 if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) {
69522 /* We're using continuous processing. Make sure we specify the whole frame count at all times. */
69523 frameCountIn = framesToProcessIn; /* Give the processing function as much input data as we've got in the buffer, including any silenced padding from short reads. */
69524
69525 if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) {
69526 consumeNullInput = MA_TRUE;
69527 } else {
69528 consumeNullInput = MA_FALSE;
69529 }
69530
69531 /*
69532 Since we're using continuous processing we're always passing in a full frame count
69533 regardless of how much input data was read. If this is greater than what we read as
69534 input, we'll end up with an underflow. We instead need to make sure our cached frame
69535 count is set to the number of frames we'll be passing to the data callback. Not
69536 doing this will result in an underflow when we "consume" the cached data later on.
69537
69538 Note that this check needs to be done after the "consumeNullInput" check above because
69539 we use the property of cachedFrameCountIn being 0 to determine whether or not we
69540 should be passing in a null pointer to the processing callback for when the node is
69541 configured with MA_NODE_FLAG_ALLOW_NULL_INPUT.
69542 */
69543 if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) {
69544 pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn;
69545 }
69546 } else {
69547 frameCountIn = pNodeBase->cachedFrameCountIn; /* Give the processing function as much valid input data as we've got. */
69548 consumeNullInput = MA_FALSE;
69549 }
69550
69551 /*
69552 Process data slightly differently depending on whether or not we're consuming NULL
69553 input (checked just above).
69554 */
69555 if (consumeNullInput) {
69556 ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut);
69557 } else {
69558 /*
69559 We want to skip processing if there's no input data, but we can only do that safely if
69560 we know that there is no chance of any output frames being produced. If continuous
69561 processing is being used, this won't be a problem because the input frame count will
69562 always be non-0. However, if continuous processing is *not* enabled and input and output
69563 data is processed at different rates, we still need to process that last input frame
69564 because there could be a few excess output frames needing to be produced from cached
69565 data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for
69566 determining whether or not we need to process the node even when there are no input
69567 frames available right now.
69568 */
69569 if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) {
69570 ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */
69571 } else {
69572 frameCountOut = 0; /* No data was processed. */
69573 }
69574 }
69575
69576 /*
69577 Thanks to our sneaky optimization above we don't need to do any data copying directly into
69578 the output buffer - the onProcess() callback just did that for us. We do, however, need to
69579 apply the number of input and output frames that were processed. Note that due to continuous
69580 processing above, we need to do explicit checks here. If we just consumed a NULL input
69581 buffer it means that no actual input data was processed from the internal buffers and we
69582 don't want to be modifying any counters.
69583 */
69584 if (consumeNullInput == MA_FALSE) {
69585 pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn;
69586 pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn;
69587 }
69588
69589 /* The cached output frame count is always equal to what we just read. */
69590 pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut;
69591
69592 /* 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. */
69593 if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) {
69594 break;
69595 }
69596 }
69597 } else {
69598 /*
69599 We're not needing to read anything from the input buffer so just read directly from our
69600 already-processed data.
69601 */
69602 if (pFramesOut != NULL) {
69603 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));
69604 }
69605 }
69606
69607 /* The number of frames read is always equal to the number of cached output frames. */
69608 totalFramesRead = pNodeBase->cachedFrameCountOut;
69609
69610 /* Now that we've read the data, make sure our read flag is set. */
69611 ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE);
69612 }
69613 }
69614
69615 /* Apply volume, if necessary. */
69616 ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]));
69617
69618 /* Advance our local time forward. */
69619 c89atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead);
69620
69621 *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */
69622 return result;
69623}
69624
69625
69626
69627
69628/* Data source node. */
69630{
69632
69633 MA_ZERO_OBJECT(&config);
69635 config.pDataSource = pDataSource;
69636
69637 return config;
69638}
69639
69640
69641static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
69642{
69643 ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode;
69644 ma_format format;
69645 ma_uint32 channels;
69646 ma_uint32 frameCount;
69647 ma_uint64 framesRead = 0;
69648
69649 MA_ASSERT(pDataSourceNode != NULL);
69650 MA_ASSERT(pDataSourceNode->pDataSource != NULL);
69651 MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode) == 0);
69652 MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1);
69653
69654 /* We don't want to read from ppFramesIn at all. Instead we read from the data source. */
69655 (void)ppFramesIn;
69656 (void)pFrameCountIn;
69657
69658 frameCount = *pFrameCountOut;
69659
69660 /* miniaudio should never be calling this with a frame count of zero. */
69661 MA_ASSERT(frameCount > 0);
69662
69663 if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL, NULL, 0) == MA_SUCCESS) { /* <-- Don't care about sample rate here. */
69664 /* The node graph system requires samples be in floating point format. This is checked in ma_data_source_node_init(). */
69665 MA_ASSERT(format == ma_format_f32);
69666 (void)format; /* Just to silence some static analysis tools. */
69667
69668 ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead);
69669 }
69670
69671 *pFrameCountOut = (ma_uint32)framesRead;
69672}
69673
69674static ma_node_vtable g_ma_data_source_node_vtable =
69675{
69676 ma_data_source_node_process_pcm_frames,
69677 NULL, /* onGetRequiredInputFrameCount */
69678 0, /* 0 input buses. */
69679 1, /* 1 output bus. */
69680 0
69681};
69682
69683MA_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)
69684{
69685 ma_result result;
69686 ma_format format; /* For validating the format, which must be ma_format_f32. */
69687 ma_uint32 channels; /* For specifying the channel count of the output bus. */
69688 ma_node_config baseConfig;
69689
69690 if (pDataSourceNode == NULL) {
69691 return MA_INVALID_ARGS;
69692 }
69693
69694 MA_ZERO_OBJECT(pDataSourceNode);
69695
69696 if (pConfig == NULL) {
69697 return MA_INVALID_ARGS;
69698 }
69699
69700 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. */
69701 if (result != MA_SUCCESS) {
69702 return result;
69703 }
69704
69705 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. */
69706 if (format != ma_format_f32) {
69707 return MA_INVALID_ARGS; /* Invalid format. */
69708 }
69709
69710 /* The channel count is defined by the data source. If the caller has manually changed the channels we just ignore it. */
69711 baseConfig = pConfig->nodeConfig;
69712 baseConfig.vtable = &g_ma_data_source_node_vtable; /* Explicitly set the vtable here to prevent callers from setting it incorrectly. */
69713
69714 /*
69715 The channel count is defined by the data source. It is invalid for the caller to manually set
69716 the channel counts in the config. `ma_data_source_node_config_init()` will have defaulted the
69717 channel count pointer to NULL which is how it must remain. If you trigger any of these asserts
69718 it means you're explicitly setting the channel count. Instead, configure the output channel
69719 count of your data source to be the necessary channel count.
69720 */
69721 if (baseConfig.pOutputChannels != NULL) {
69722 return MA_INVALID_ARGS;
69723 }
69724
69725 baseConfig.pOutputChannels = &channels;
69726
69727 result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base);
69728 if (result != MA_SUCCESS) {
69729 return result;
69730 }
69731
69732 pDataSourceNode->pDataSource = pConfig->pDataSource;
69733
69734 return MA_SUCCESS;
69735}
69736
69737MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks)
69738{
69739 ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks);
69740}
69741
69743{
69744 if (pDataSourceNode == NULL) {
69745 return MA_INVALID_ARGS;
69746 }
69747
69748 return ma_data_source_set_looping(pDataSourceNode->pDataSource, isLooping);
69749}
69750
69752{
69753 if (pDataSourceNode == NULL) {
69754 return MA_FALSE;
69755 }
69756
69757 return ma_data_source_is_looping(pDataSourceNode->pDataSource);
69758}
69759
69760
69761
69762/* Splitter Node. */
69764{
69766
69767 MA_ZERO_OBJECT(&config);
69769 config.channels = channels;
69770
69771 return config;
69772}
69773
69774
69775static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
69776{
69777 ma_node_base* pNodeBase = (ma_node_base*)pNode;
69778 ma_uint32 iOutputBus;
69779 ma_uint32 channels;
69780
69781 MA_ASSERT(pNodeBase != NULL);
69782 MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1);
69783 MA_ASSERT(ma_node_get_output_bus_count(pNodeBase) >= 2);
69784
69785 /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */
69786 (void)pFrameCountIn;
69787
69788 /* NOTE: This assumes the same number of channels for all inputs and outputs. This was checked in ma_splitter_node_init(). */
69789 channels = ma_node_get_input_channels(pNodeBase, 0);
69790
69791 /* Splitting is just copying the first input bus and copying it over to each output bus. */
69792 for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) {
69793 ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels);
69794 }
69795}
69796
69797static ma_node_vtable g_ma_splitter_node_vtable =
69798{
69799 ma_splitter_node_process_pcm_frames,
69800 NULL, /* onGetRequiredInputFrameCount */
69801 1, /* 1 input bus. */
69802 2, /* 2 output buses. */
69803 0
69804};
69805
69806MA_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)
69807{
69808 ma_result result;
69809 ma_node_config baseConfig;
69810 ma_uint32 pInputChannels[1];
69811 ma_uint32 pOutputChannels[2];
69812
69813 if (pSplitterNode == NULL) {
69814 return MA_INVALID_ARGS;
69815 }
69816
69817 MA_ZERO_OBJECT(pSplitterNode);
69818
69819 if (pConfig == NULL) {
69820 return MA_INVALID_ARGS;
69821 }
69822
69823 /* Splitters require the same number of channels between inputs and outputs. */
69824 pInputChannels[0] = pConfig->channels;
69825 pOutputChannels[0] = pConfig->channels;
69826 pOutputChannels[1] = pConfig->channels;
69827
69828 baseConfig = pConfig->nodeConfig;
69829 baseConfig.vtable = &g_ma_splitter_node_vtable;
69830 baseConfig.pInputChannels = pInputChannels;
69831 baseConfig.pOutputChannels = pOutputChannels;
69832
69833 result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base);
69834 if (result != MA_SUCCESS) {
69835 return result; /* Failed to initialize the base node. */
69836 }
69837
69838 return MA_SUCCESS;
69839}
69840
69841MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks)
69842{
69843 ma_node_uninit(pSplitterNode, pAllocationCallbacks);
69844}
69845
69846
69847/*
69848Biquad Node
69849*/
69850MA_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)
69851{
69852 ma_biquad_node_config config;
69853
69855 config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);
69856
69857 return config;
69858}
69859
69860static void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
69861{
69862 ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
69863
69864 MA_ASSERT(pNode != NULL);
69865 (void)pFrameCountIn;
69866
69867 ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
69868}
69869
69870static ma_node_vtable g_ma_biquad_node_vtable =
69871{
69872 ma_biquad_node_process_pcm_frames,
69873 NULL, /* onGetRequiredInputFrameCount */
69874 1, /* One input. */
69875 1, /* One output. */
69876 0 /* Default flags. */
69877};
69878
69879MA_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)
69880{
69881 ma_result result;
69882 ma_node_config baseNodeConfig;
69883
69884 if (pNode == NULL) {
69885 return MA_INVALID_ARGS;
69886 }
69887
69888 MA_ZERO_OBJECT(pNode);
69889
69890 if (pConfig == NULL) {
69891 return MA_INVALID_ARGS;
69892 }
69893
69894 if (pConfig->biquad.format != ma_format_f32) {
69895 return MA_INVALID_ARGS; /* The format must be f32. */
69896 }
69897
69898 result = ma_biquad_init(&pConfig->biquad, pAllocationCallbacks, &pNode->biquad);
69899 if (result != MA_SUCCESS) {
69900 return result;
69901 }
69902
69903 baseNodeConfig = ma_node_config_init();
69904 baseNodeConfig.vtable = &g_ma_biquad_node_vtable;
69905 baseNodeConfig.pInputChannels = &pConfig->biquad.channels;
69906 baseNodeConfig.pOutputChannels = &pConfig->biquad.channels;
69907
69908 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
69909 if (result != MA_SUCCESS) {
69910 return result;
69911 }
69912
69913 return result;
69914}
69915
69917{
69918 ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
69919
69920 MA_ASSERT(pNode != NULL);
69921
69922 return ma_biquad_reinit(pConfig, &pLPFNode->biquad);
69923}
69924
69925MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
69926{
69927 ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
69928
69929 if (pNode == NULL) {
69930 return;
69931 }
69932
69933 ma_node_uninit(pNode, pAllocationCallbacks);
69934 ma_biquad_uninit(&pLPFNode->biquad, pAllocationCallbacks);
69935}
69936
69937
69938
69939/*
69940Low Pass Filter Node
69941*/
69942MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
69943{
69944 ma_lpf_node_config config;
69945
69947 config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
69948
69949 return config;
69950}
69951
69952static void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
69953{
69954 ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
69955
69956 MA_ASSERT(pNode != NULL);
69957 (void)pFrameCountIn;
69958
69959 ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
69960}
69961
69962static ma_node_vtable g_ma_lpf_node_vtable =
69963{
69964 ma_lpf_node_process_pcm_frames,
69965 NULL, /* onGetRequiredInputFrameCount */
69966 1, /* One input. */
69967 1, /* One output. */
69968 0 /* Default flags. */
69969};
69970
69971MA_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)
69972{
69973 ma_result result;
69974 ma_node_config baseNodeConfig;
69975
69976 if (pNode == NULL) {
69977 return MA_INVALID_ARGS;
69978 }
69979
69980 MA_ZERO_OBJECT(pNode);
69981
69982 if (pConfig == NULL) {
69983 return MA_INVALID_ARGS;
69984 }
69985
69986 if (pConfig->lpf.format != ma_format_f32) {
69987 return MA_INVALID_ARGS; /* The format must be f32. */
69988 }
69989
69990 result = ma_lpf_init(&pConfig->lpf, pAllocationCallbacks, &pNode->lpf);
69991 if (result != MA_SUCCESS) {
69992 return result;
69993 }
69994
69995 baseNodeConfig = ma_node_config_init();
69996 baseNodeConfig.vtable = &g_ma_lpf_node_vtable;
69997 baseNodeConfig.pInputChannels = &pConfig->lpf.channels;
69998 baseNodeConfig.pOutputChannels = &pConfig->lpf.channels;
69999
70000 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
70001 if (result != MA_SUCCESS) {
70002 return result;
70003 }
70004
70005 return result;
70006}
70007
70009{
70010 ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
70011
70012 if (pNode == NULL) {
70013 return MA_INVALID_ARGS;
70014 }
70015
70016 return ma_lpf_reinit(pConfig, &pLPFNode->lpf);
70017}
70018
70019MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
70020{
70021 ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
70022
70023 if (pNode == NULL) {
70024 return;
70025 }
70026
70027 ma_node_uninit(pNode, pAllocationCallbacks);
70028 ma_lpf_uninit(&pLPFNode->lpf, pAllocationCallbacks);
70029}
70030
70031
70032
70033/*
70034High Pass Filter Node
70035*/
70036MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
70037{
70038 ma_hpf_node_config config;
70039
70041 config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
70042
70043 return config;
70044}
70045
70046static void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
70047{
70048 ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
70049
70050 MA_ASSERT(pNode != NULL);
70051 (void)pFrameCountIn;
70052
70053 ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
70054}
70055
70056static ma_node_vtable g_ma_hpf_node_vtable =
70057{
70058 ma_hpf_node_process_pcm_frames,
70059 NULL, /* onGetRequiredInputFrameCount */
70060 1, /* One input. */
70061 1, /* One output. */
70062 0 /* Default flags. */
70063};
70064
70065MA_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)
70066{
70067 ma_result result;
70068 ma_node_config baseNodeConfig;
70069
70070 if (pNode == NULL) {
70071 return MA_INVALID_ARGS;
70072 }
70073
70074 MA_ZERO_OBJECT(pNode);
70075
70076 if (pConfig == NULL) {
70077 return MA_INVALID_ARGS;
70078 }
70079
70080 if (pConfig->hpf.format != ma_format_f32) {
70081 return MA_INVALID_ARGS; /* The format must be f32. */
70082 }
70083
70084 result = ma_hpf_init(&pConfig->hpf, pAllocationCallbacks, &pNode->hpf);
70085 if (result != MA_SUCCESS) {
70086 return result;
70087 }
70088
70089 baseNodeConfig = ma_node_config_init();
70090 baseNodeConfig.vtable = &g_ma_hpf_node_vtable;
70091 baseNodeConfig.pInputChannels = &pConfig->hpf.channels;
70092 baseNodeConfig.pOutputChannels = &pConfig->hpf.channels;
70093
70094 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
70095 if (result != MA_SUCCESS) {
70096 return result;
70097 }
70098
70099 return result;
70100}
70101
70103{
70104 ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
70105
70106 if (pNode == NULL) {
70107 return MA_INVALID_ARGS;
70108 }
70109
70110 return ma_hpf_reinit(pConfig, &pHPFNode->hpf);
70111}
70112
70113MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
70114{
70115 ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
70116
70117 if (pNode == NULL) {
70118 return;
70119 }
70120
70121 ma_node_uninit(pNode, pAllocationCallbacks);
70122 ma_hpf_uninit(&pHPFNode->hpf, pAllocationCallbacks);
70123}
70124
70125
70126
70127
70128/*
70129Band Pass Filter Node
70130*/
70131MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
70132{
70133 ma_bpf_node_config config;
70134
70136 config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
70137
70138 return config;
70139}
70140
70141static void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
70142{
70143 ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
70144
70145 MA_ASSERT(pNode != NULL);
70146 (void)pFrameCountIn;
70147
70148 ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
70149}
70150
70151static ma_node_vtable g_ma_bpf_node_vtable =
70152{
70153 ma_bpf_node_process_pcm_frames,
70154 NULL, /* onGetRequiredInputFrameCount */
70155 1, /* One input. */
70156 1, /* One output. */
70157 0 /* Default flags. */
70158};
70159
70160MA_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)
70161{
70162 ma_result result;
70163 ma_node_config baseNodeConfig;
70164
70165 if (pNode == NULL) {
70166 return MA_INVALID_ARGS;
70167 }
70168
70169 MA_ZERO_OBJECT(pNode);
70170
70171 if (pConfig == NULL) {
70172 return MA_INVALID_ARGS;
70173 }
70174
70175 if (pConfig->bpf.format != ma_format_f32) {
70176 return MA_INVALID_ARGS; /* The format must be f32. */
70177 }
70178
70179 result = ma_bpf_init(&pConfig->bpf, pAllocationCallbacks, &pNode->bpf);
70180 if (result != MA_SUCCESS) {
70181 return result;
70182 }
70183
70184 baseNodeConfig = ma_node_config_init();
70185 baseNodeConfig.vtable = &g_ma_bpf_node_vtable;
70186 baseNodeConfig.pInputChannels = &pConfig->bpf.channels;
70187 baseNodeConfig.pOutputChannels = &pConfig->bpf.channels;
70188
70189 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
70190 if (result != MA_SUCCESS) {
70191 return result;
70192 }
70193
70194 return result;
70195}
70196
70198{
70199 ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
70200
70201 if (pNode == NULL) {
70202 return MA_INVALID_ARGS;
70203 }
70204
70205 return ma_bpf_reinit(pConfig, &pBPFNode->bpf);
70206}
70207
70208MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
70209{
70210 ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
70211
70212 if (pNode == NULL) {
70213 return;
70214 }
70215
70216 ma_node_uninit(pNode, pAllocationCallbacks);
70217 ma_bpf_uninit(&pBPFNode->bpf, pAllocationCallbacks);
70218}
70219
70220
70221
70222/*
70223Notching Filter Node
70224*/
70225MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
70226{
70227 ma_notch_node_config config;
70228
70230 config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency);
70231
70232 return config;
70233}
70234
70235static void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
70236{
70237 ma_notch_node* pBPFNode = (ma_notch_node*)pNode;
70238
70239 MA_ASSERT(pNode != NULL);
70240 (void)pFrameCountIn;
70241
70242 ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
70243}
70244
70245static ma_node_vtable g_ma_notch_node_vtable =
70246{
70247 ma_notch_node_process_pcm_frames,
70248 NULL, /* onGetRequiredInputFrameCount */
70249 1, /* One input. */
70250 1, /* One output. */
70251 0 /* Default flags. */
70252};
70253
70254MA_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)
70255{
70256 ma_result result;
70257 ma_node_config baseNodeConfig;
70258
70259 if (pNode == NULL) {
70260 return MA_INVALID_ARGS;
70261 }
70262
70263 MA_ZERO_OBJECT(pNode);
70264
70265 if (pConfig == NULL) {
70266 return MA_INVALID_ARGS;
70267 }
70268
70269 if (pConfig->notch.format != ma_format_f32) {
70270 return MA_INVALID_ARGS; /* The format must be f32. */
70271 }
70272
70273 result = ma_notch2_init(&pConfig->notch, pAllocationCallbacks, &pNode->notch);
70274 if (result != MA_SUCCESS) {
70275 return result;
70276 }
70277
70278 baseNodeConfig = ma_node_config_init();
70279 baseNodeConfig.vtable = &g_ma_notch_node_vtable;
70280 baseNodeConfig.pInputChannels = &pConfig->notch.channels;
70281 baseNodeConfig.pOutputChannels = &pConfig->notch.channels;
70282
70283 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
70284 if (result != MA_SUCCESS) {
70285 return result;
70286 }
70287
70288 return result;
70289}
70290
70292{
70293 ma_notch_node* pNotchNode = (ma_notch_node*)pNode;
70294
70295 if (pNode == NULL) {
70296 return MA_INVALID_ARGS;
70297 }
70298
70299 return ma_notch2_reinit(pConfig, &pNotchNode->notch);
70300}
70301
70302MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
70303{
70304 ma_notch_node* pNotchNode = (ma_notch_node*)pNode;
70305
70306 if (pNode == NULL) {
70307 return;
70308 }
70309
70310 ma_node_uninit(pNode, pAllocationCallbacks);
70311 ma_notch2_uninit(&pNotchNode->notch, pAllocationCallbacks);
70312}
70313
70314
70315
70316/*
70317Peaking Filter Node
70318*/
70319MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
70320{
70321 ma_peak_node_config config;
70322
70324 config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
70325
70326 return config;
70327}
70328
70329static void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
70330{
70331 ma_peak_node* pBPFNode = (ma_peak_node*)pNode;
70332
70333 MA_ASSERT(pNode != NULL);
70334 (void)pFrameCountIn;
70335
70336 ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
70337}
70338
70339static ma_node_vtable g_ma_peak_node_vtable =
70340{
70341 ma_peak_node_process_pcm_frames,
70342 NULL, /* onGetRequiredInputFrameCount */
70343 1, /* One input. */
70344 1, /* One output. */
70345 0 /* Default flags. */
70346};
70347
70348MA_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)
70349{
70350 ma_result result;
70351 ma_node_config baseNodeConfig;
70352
70353 if (pNode == NULL) {
70354 return MA_INVALID_ARGS;
70355 }
70356
70357 MA_ZERO_OBJECT(pNode);
70358
70359 if (pConfig == NULL) {
70360 return MA_INVALID_ARGS;
70361 }
70362
70363 if (pConfig->peak.format != ma_format_f32) {
70364 return MA_INVALID_ARGS; /* The format must be f32. */
70365 }
70366
70367 result = ma_peak2_init(&pConfig->peak, pAllocationCallbacks, &pNode->peak);
70368 if (result != MA_SUCCESS) {
70369 ma_node_uninit(pNode, pAllocationCallbacks);
70370 return result;
70371 }
70372
70373 baseNodeConfig = ma_node_config_init();
70374 baseNodeConfig.vtable = &g_ma_peak_node_vtable;
70375 baseNodeConfig.pInputChannels = &pConfig->peak.channels;
70376 baseNodeConfig.pOutputChannels = &pConfig->peak.channels;
70377
70378 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
70379 if (result != MA_SUCCESS) {
70380 return result;
70381 }
70382
70383 return result;
70384}
70385
70387{
70388 ma_peak_node* pPeakNode = (ma_peak_node*)pNode;
70389
70390 if (pNode == NULL) {
70391 return MA_INVALID_ARGS;
70392 }
70393
70394 return ma_peak2_reinit(pConfig, &pPeakNode->peak);
70395}
70396
70397MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
70398{
70399 ma_peak_node* pPeakNode = (ma_peak_node*)pNode;
70400
70401 if (pNode == NULL) {
70402 return;
70403 }
70404
70405 ma_node_uninit(pNode, pAllocationCallbacks);
70406 ma_peak2_uninit(&pPeakNode->peak, pAllocationCallbacks);
70407}
70408
70409
70410
70411/*
70412Low Shelf Filter Node
70413*/
70414MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
70415{
70417
70419 config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
70420
70421 return config;
70422}
70423
70424static void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
70425{
70426 ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode;
70427
70428 MA_ASSERT(pNode != NULL);
70429 (void)pFrameCountIn;
70430
70431 ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
70432}
70433
70434static ma_node_vtable g_ma_loshelf_node_vtable =
70435{
70436 ma_loshelf_node_process_pcm_frames,
70437 NULL, /* onGetRequiredInputFrameCount */
70438 1, /* One input. */
70439 1, /* One output. */
70440 0 /* Default flags. */
70441};
70442
70443MA_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)
70444{
70445 ma_result result;
70446 ma_node_config baseNodeConfig;
70447
70448 if (pNode == NULL) {
70449 return MA_INVALID_ARGS;
70450 }
70451
70452 MA_ZERO_OBJECT(pNode);
70453
70454 if (pConfig == NULL) {
70455 return MA_INVALID_ARGS;
70456 }
70457
70458 if (pConfig->loshelf.format != ma_format_f32) {
70459 return MA_INVALID_ARGS; /* The format must be f32. */
70460 }
70461
70462 result = ma_loshelf2_init(&pConfig->loshelf, pAllocationCallbacks, &pNode->loshelf);
70463 if (result != MA_SUCCESS) {
70464 return result;
70465 }
70466
70467 baseNodeConfig = ma_node_config_init();
70468 baseNodeConfig.vtable = &g_ma_loshelf_node_vtable;
70469 baseNodeConfig.pInputChannels = &pConfig->loshelf.channels;
70470 baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels;
70471
70472 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
70473 if (result != MA_SUCCESS) {
70474 return result;
70475 }
70476
70477 return result;
70478}
70479
70481{
70482 ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode;
70483
70484 if (pNode == NULL) {
70485 return MA_INVALID_ARGS;
70486 }
70487
70488 return ma_loshelf2_reinit(pConfig, &pLoshelfNode->loshelf);
70489}
70490
70491MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
70492{
70493 ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode;
70494
70495 if (pNode == NULL) {
70496 return;
70497 }
70498
70499 ma_node_uninit(pNode, pAllocationCallbacks);
70500 ma_loshelf2_uninit(&pLoshelfNode->loshelf, pAllocationCallbacks);
70501}
70502
70503
70504
70505/*
70506High Shelf Filter Node
70507*/
70508MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
70509{
70511
70513 config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
70514
70515 return config;
70516}
70517
70518static void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
70519{
70520 ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode;
70521
70522 MA_ASSERT(pNode != NULL);
70523 (void)pFrameCountIn;
70524
70525 ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
70526}
70527
70528static ma_node_vtable g_ma_hishelf_node_vtable =
70529{
70530 ma_hishelf_node_process_pcm_frames,
70531 NULL, /* onGetRequiredInputFrameCount */
70532 1, /* One input. */
70533 1, /* One output. */
70534 0 /* Default flags. */
70535};
70536
70537MA_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)
70538{
70539 ma_result result;
70540 ma_node_config baseNodeConfig;
70541
70542 if (pNode == NULL) {
70543 return MA_INVALID_ARGS;
70544 }
70545
70546 MA_ZERO_OBJECT(pNode);
70547
70548 if (pConfig == NULL) {
70549 return MA_INVALID_ARGS;
70550 }
70551
70552 if (pConfig->hishelf.format != ma_format_f32) {
70553 return MA_INVALID_ARGS; /* The format must be f32. */
70554 }
70555
70556 result = ma_hishelf2_init(&pConfig->hishelf, pAllocationCallbacks, &pNode->hishelf);
70557 if (result != MA_SUCCESS) {
70558 return result;
70559 }
70560
70561 baseNodeConfig = ma_node_config_init();
70562 baseNodeConfig.vtable = &g_ma_hishelf_node_vtable;
70563 baseNodeConfig.pInputChannels = &pConfig->hishelf.channels;
70564 baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels;
70565
70566 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
70567 if (result != MA_SUCCESS) {
70568 return result;
70569 }
70570
70571 return result;
70572}
70573
70575{
70576 ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode;
70577
70578 if (pNode == NULL) {
70579 return MA_INVALID_ARGS;
70580 }
70581
70582 return ma_hishelf2_reinit(pConfig, &pHishelfNode->hishelf);
70583}
70584
70585MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
70586{
70587 ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode;
70588
70589 if (pNode == NULL) {
70590 return;
70591 }
70592
70593 ma_node_uninit(pNode, pAllocationCallbacks);
70594 ma_hishelf2_uninit(&pHishelfNode->hishelf, pAllocationCallbacks);
70595}
70596
70597
70598
70599
70600MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)
70601{
70602 ma_delay_node_config config;
70603
70605 config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay);
70606
70607 return config;
70608}
70609
70610
70611static void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
70612{
70613 ma_delay_node* pDelayNode = (ma_delay_node*)pNode;
70614
70615 (void)pFrameCountIn;
70616
70617 ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
70618}
70619
70620static ma_node_vtable g_ma_delay_node_vtable =
70621{
70622 ma_delay_node_process_pcm_frames,
70623 NULL,
70624 1, /* 1 input channels. */
70625 1, /* 1 output channel. */
70626 MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Delay requires continuous processing to ensure the tail get's processed. */
70627};
70628
70629MA_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)
70630{
70631 ma_result result;
70632 ma_node_config baseConfig;
70633
70634 if (pDelayNode == NULL) {
70635 return MA_INVALID_ARGS;
70636 }
70637
70638 MA_ZERO_OBJECT(pDelayNode);
70639
70640 result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay);
70641 if (result != MA_SUCCESS) {
70642 return result;
70643 }
70644
70645 baseConfig = pConfig->nodeConfig;
70646 baseConfig.vtable = &g_ma_delay_node_vtable;
70647 baseConfig.pInputChannels = &pConfig->delay.channels;
70648 baseConfig.pOutputChannels = &pConfig->delay.channels;
70649
70650 result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode);
70651 if (result != MA_SUCCESS) {
70652 ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks);
70653 return result;
70654 }
70655
70656 return result;
70657}
70658
70659MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks)
70660{
70661 if (pDelayNode == NULL) {
70662 return;
70663 }
70664
70665 /* The base node is always uninitialized first. */
70666 ma_node_uninit(pDelayNode, pAllocationCallbacks);
70667 ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks);
70668}
70669
70670MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value)
70671{
70672 if (pDelayNode == NULL) {
70673 return;
70674 }
70675
70676 ma_delay_set_wet(&pDelayNode->delay, value);
70677}
70678
70679MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode)
70680{
70681 if (pDelayNode == NULL) {
70682 return 0;
70683 }
70684
70685 return ma_delay_get_wet(&pDelayNode->delay);
70686}
70687
70688MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value)
70689{
70690 if (pDelayNode == NULL) {
70691 return;
70692 }
70693
70694 ma_delay_set_dry(&pDelayNode->delay, value);
70695}
70696
70697MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode)
70698{
70699 if (pDelayNode == NULL) {
70700 return 0;
70701 }
70702
70703 return ma_delay_get_dry(&pDelayNode->delay);
70704}
70705
70706MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value)
70707{
70708 if (pDelayNode == NULL) {
70709 return;
70710 }
70711
70712 ma_delay_set_decay(&pDelayNode->delay, value);
70713}
70714
70715MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode)
70716{
70717 if (pDelayNode == NULL) {
70718 return 0;
70719 }
70720
70721 return ma_delay_get_decay(&pDelayNode->delay);
70722}
70723#endif /* MA_NO_NODE_GRAPH */
70724
70725
70726#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH)
70727
70732#define MA_SEEK_TARGET_NONE (~(ma_uint64)0)
70733
70735{
70736 ma_engine_node_config config;
70737
70738 MA_ZERO_OBJECT(&config);
70739 config.pEngine = pEngine;
70740 config.type = type;
70741 config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0;
70743
70744 return config;
70745}
70746
70747
70748static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode)
70749{
70750 ma_bool32 isUpdateRequired = MA_FALSE;
70751 float newPitch;
70752
70753 MA_ASSERT(pEngineNode != NULL);
70754
70755 newPitch = c89atomic_load_explicit_f32(&pEngineNode->pitch, c89atomic_memory_order_acquire);
70756
70757 if (pEngineNode->oldPitch != newPitch) {
70758 pEngineNode->oldPitch = newPitch;
70759 isUpdateRequired = MA_TRUE;
70760 }
70761
70762 if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) {
70763 pEngineNode->oldDopplerPitch = pEngineNode->spatializer.dopplerPitch;
70764 isUpdateRequired = MA_TRUE;
70765 }
70766
70767 if (isUpdateRequired) {
70768 float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine);
70769 ma_linear_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch);
70770 }
70771}
70772
70773static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode)
70774{
70775 MA_ASSERT(pEngineNode != NULL);
70776
70777 /* Don't try to be clever by skiping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */
70778 return !c89atomic_load_explicit_32(&pEngineNode->isPitchDisabled, c89atomic_memory_order_acquire);
70779}
70780
70781static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode)
70782{
70783 MA_ASSERT(pEngineNode != NULL);
70784
70785 return !c89atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, c89atomic_memory_order_acquire);
70786}
70787
70788static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount)
70789{
70790 ma_uint64 inputFrameCount = 0;
70791
70792 if (ma_engine_node_is_pitching_enabled(pEngineNode)) {
70793 ma_result result = ma_linear_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount, &inputFrameCount);
70794 if (result != MA_SUCCESS) {
70795 inputFrameCount = 0;
70796 }
70797 } else {
70798 inputFrameCount = outputFrameCount; /* No resampling, so 1:1. */
70799 }
70800
70801 return inputFrameCount;
70802}
70803
70804static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
70805{
70806 ma_uint32 frameCountIn;
70807 ma_uint32 frameCountOut;
70808 ma_uint32 totalFramesProcessedIn;
70809 ma_uint32 totalFramesProcessedOut;
70810 ma_uint32 channelsIn;
70811 ma_uint32 channelsOut;
70812 ma_bool32 isPitchingEnabled;
70813 ma_bool32 isFadingEnabled;
70814 ma_bool32 isSpatializationEnabled;
70815 ma_bool32 isPanningEnabled;
70816
70817 frameCountIn = *pFrameCountIn;
70818 frameCountOut = *pFrameCountOut;
70819
70820 channelsIn = ma_spatializer_get_input_channels(&pEngineNode->spatializer);
70821 channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer);
70822
70823 totalFramesProcessedIn = 0;
70824 totalFramesProcessedOut = 0;
70825
70826 isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode);
70827 isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1;
70828 isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode);
70829 isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1;
70830
70831 /* Keep going while we've still got data available for processing. */
70832 while (totalFramesProcessedOut < frameCountOut) {
70833 /*
70834 We need to process in a specific order. We always do resampling first because it's likely
70835 we're going to be increasing the channel count after spatialization. Also, I want to do
70836 fading based on the output sample rate.
70837
70838 We'll first read into a buffer from the resampler. Then we'll do all processing that
70839 operates on the on the input channel count. We'll then get the spatializer to output to
70840 the output buffer and then do all effects from that point directly in the output buffer
70841 in-place.
70842
70843 Note that we're always running the resampler. If we try to be clever and skip resampling
70844 when the pitch is 1, we'll get a glitch when we move away from 1, back to 1, and then
70845 away from 1 again. We'll want to implement any pitch=1 optimizations in the resampler
70846 itself.
70847
70848 There's a small optimization here that we'll utilize since it might be a fairly common
70849 case. When the input and output channel counts are the same, we'll read straight into the
70850 output buffer from the resampler and do everything in-place.
70851 */
70852 const float* pRunningFramesIn;
70853 float* pRunningFramesOut;
70854 float* pWorkingBuffer; /* This is the buffer that we'll be processing frames in. This is in input channels. */
70855 float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)];
70856 ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn;
70857 ma_uint32 framesAvailableIn;
70858 ma_uint32 framesAvailableOut;
70859 ma_uint32 framesJustProcessedIn;
70860 ma_uint32 framesJustProcessedOut;
70861 ma_bool32 isWorkingBufferValid = MA_FALSE;
70862
70863 framesAvailableIn = frameCountIn - totalFramesProcessedIn;
70864 framesAvailableOut = frameCountOut - totalFramesProcessedOut;
70865
70866 pRunningFramesIn = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn);
70867 pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut);
70868
70869 if (channelsIn == channelsOut) {
70870 /* Fast path. Channel counts are the same. No need for an intermediary input buffer. */
70871 pWorkingBuffer = pRunningFramesOut;
70872 } else {
70873 /* Slow path. Channel counts are different. Need to use an intermediary input buffer. */
70874 pWorkingBuffer = temp;
70875 if (framesAvailableOut > tempCapInFrames) {
70876 framesAvailableOut = tempCapInFrames;
70877 }
70878 }
70879
70880 /* First is resampler. */
70881 if (isPitchingEnabled) {
70882 ma_uint64 resampleFrameCountIn = framesAvailableIn;
70883 ma_uint64 resampleFrameCountOut = framesAvailableOut;
70884
70885 ma_linear_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut);
70886 isWorkingBufferValid = MA_TRUE;
70887
70888 framesJustProcessedIn = (ma_uint32)resampleFrameCountIn;
70889 framesJustProcessedOut = (ma_uint32)resampleFrameCountOut;
70890 } else {
70891 framesJustProcessedIn = ma_min(framesAvailableIn, framesAvailableOut);
70892 framesJustProcessedOut = framesJustProcessedIn; /* When no resampling is being performed, the number of output frames is the same as input frames. */
70893 }
70894
70895 /* Fading. */
70896 if (isFadingEnabled) {
70897 if (isWorkingBufferValid) {
70898 ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); /* In-place processing. */
70899 } else {
70900 ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut);
70901 isWorkingBufferValid = MA_TRUE;
70902 }
70903 }
70904
70905 /*
70906 If at this point we still haven't actually done anything with the working buffer we need
70907 to just read straight from the input buffer.
70908 */
70909 if (isWorkingBufferValid == MA_FALSE) {
70910 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. */
70911 }
70912
70913 /* Spatialization. */
70914 if (isSpatializationEnabled) {
70915 ma_uint32 iListener;
70916
70917 /*
70918 When determining the listener to use, we first check to see if the sound is pinned to a
70919 specific listener. If so, we use that. Otherwise we just use the closest listener.
70920 */
70921 if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) {
70922 iListener = pEngineNode->pinnedListenerIndex;
70923 } else {
70924 iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, pEngineNode->spatializer.position.x, pEngineNode->spatializer.position.y, pEngineNode->spatializer.position.z);
70925 }
70926
70927 ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut);
70928 } else {
70929 /* No spatialization, but we still need to do channel conversion. */
70930 if (channelsIn == channelsOut) {
70931 /* No channel conversion required. Just copy straight to the output buffer. */
70932 ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut, ma_format_f32, channelsOut);
70933 } else {
70934 /* Channel conversion required. TODO: Add support for channel maps here. */
70935 ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->pEngine->monoExpansionMode);
70936 }
70937 }
70938
70939 /* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */
70940
70941 /* Panning. */
70942 if (isPanningEnabled) {
70943 ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut); /* In-place processing. */
70944 }
70945
70946 /* We're done for this chunk. */
70947 totalFramesProcessedIn += framesJustProcessedIn;
70948 totalFramesProcessedOut += framesJustProcessedOut;
70949
70950 /* 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. */
70951 if (framesJustProcessedOut == 0) {
70952 break;
70953 }
70954 }
70955
70956 /* At this point we're done processing. */
70957 *pFrameCountIn = totalFramesProcessedIn;
70958 *pFrameCountOut = totalFramesProcessedOut;
70959}
70960
70961static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
70962{
70963 /* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */
70964 ma_result result = MA_SUCCESS;
70965 ma_sound* pSound = (ma_sound*)pNode;
70966 ma_uint32 frameCount = *pFrameCountOut;
70967 ma_uint32 totalFramesRead = 0;
70968 ma_format dataSourceFormat;
70969 ma_uint32 dataSourceChannels;
70970 ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
70971 ma_uint32 tempCapInFrames;
70972
70973 /* This is a data source node which means no input buses. */
70974 (void)ppFramesIn;
70975 (void)pFrameCountIn;
70976
70977 /* If we're marked at the end we need to stop the sound and do nothing. */
70978 if (ma_sound_at_end(pSound)) {
70979 ma_sound_stop(pSound);
70980 *pFrameCountOut = 0;
70981 return;
70982 }
70983
70984 /* If we're seeking, do so now before reading. */
70985 if (pSound->seekTarget != MA_SEEK_TARGET_NONE) {
70987
70988 /* Any time-dependant effects need to have their times updated. */
70989 ma_node_set_time(pSound, pSound->seekTarget);
70990
70991 pSound->seekTarget = MA_SEEK_TARGET_NONE;
70992 }
70993
70994 /*
70995 We want to update the pitch once. For sounds, this can be either at the start or at the end. If
70996 we don't force this to only ever be updating once, we could end up in a situation where
70997 retrieving the required input frame count ends up being different to what we actually retrieve.
70998 What could happen is that the required input frame count is calculated, the pitch is update,
70999 and then this processing function is called resulting in a different number of input frames
71000 being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else
71001 you'll hit the aforementioned bug.
71002 */
71003 ma_engine_node_update_pitch_if_required(&pSound->engineNode);
71004
71005 /*
71006 For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ
71007 from the main engine.
71008 */
71009 result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0);
71010 if (result == MA_SUCCESS) {
71011 tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels);
71012
71013 /* Keep reading until we've read as much as was requested or we reach the end of the data source. */
71014 while (totalFramesRead < frameCount) {
71015 ma_uint32 framesRemaining = frameCount - totalFramesRead;
71016 ma_uint32 framesToRead;
71017 ma_uint64 framesJustRead;
71018 ma_uint32 frameCountIn;
71019 ma_uint32 frameCountOut;
71020 const float* pRunningFramesIn;
71021 float* pRunningFramesOut;
71022
71023 /*
71024 The first thing we need to do is read into the temporary buffer. We can calculate exactly
71025 how many input frames we'll need after resampling.
71026 */
71027 framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining);
71028 if (framesToRead > tempCapInFrames) {
71029 framesToRead = tempCapInFrames;
71030 }
71031
71032 result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead);
71033
71034 /* 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. */
71035 if (result == MA_AT_END) {
71036 c89atomic_exchange_32(&pSound->atEnd, MA_TRUE); /* This will be set to false in ma_sound_start(). */
71037 }
71038
71039 pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound)));
71040
71041 frameCountIn = (ma_uint32)framesJustRead;
71042 frameCountOut = framesRemaining;
71043
71044 /* Convert if necessary. */
71045 if (dataSourceFormat == ma_format_f32) {
71046 /* Fast path. No data conversion necessary. */
71047 pRunningFramesIn = (float*)temp;
71048 ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);
71049 } else {
71050 /* 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. */
71051 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. */
71052 ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none);
71053
71054 /* Now that we have our samples in f32 format we can process like normal. */
71055 pRunningFramesIn = tempf32;
71056 ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);
71057 }
71058
71059 /* We should have processed all of our input frames since we calculated the required number of input frames at the top. */
71060 MA_ASSERT(frameCountIn == framesJustRead);
71061 totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */
71062
71063 if (result != MA_SUCCESS || ma_sound_at_end(pSound)) {
71064 break; /* Might have reached the end. */
71065 }
71066 }
71067 }
71068
71069 *pFrameCountOut = totalFramesRead;
71070}
71071
71072static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
71073{
71074 /*
71075 Make sure the pitch is updated before trying to read anything. It's important that this is done
71076 only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that
71077 ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(),
71078 and if another thread modifies the pitch just after that call it can result in a glitch due to
71079 the input rate changing.
71080 */
71081 ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
71082
71083 /* For groups, the input data has already been read and we just need to apply the effect. */
71084 ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
71085}
71086
71087static ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount)
71088{
71089 ma_uint64 inputFrameCount;
71090
71091 MA_ASSERT(pInputFrameCount != NULL);
71092
71093 /* Our pitch will affect this calculation. We need to update it. */
71094 ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
71095
71096 inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount);
71097 if (inputFrameCount > 0xFFFFFFFF) {
71098 inputFrameCount = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */
71099 }
71100
71101 *pInputFrameCount = (ma_uint32)inputFrameCount;
71102
71103 return MA_SUCCESS;
71104}
71105
71106
71107static ma_node_vtable g_ma_engine_node_vtable__sound =
71108{
71109 ma_engine_node_process_pcm_frames__sound,
71110 NULL, /* onGetRequiredInputFrameCount */
71111 0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */
71112 1, /* Sounds have one output bus. */
71113 0 /* Default flags. */
71114};
71115
71116static ma_node_vtable g_ma_engine_node_vtable__group =
71117{
71118 ma_engine_node_process_pcm_frames__group,
71119 ma_engine_node_get_required_input_frame_count__group,
71120 1, /* Groups have one input bus. */
71121 1, /* Groups have one output bus. */
71122 MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */
71123};
71124
71125
71126
71127static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig)
71128{
71129 ma_node_config baseNodeConfig;
71130
71131 if (pConfig->type == ma_engine_node_type_sound) {
71132 /* Sound. */
71133 baseNodeConfig = ma_node_config_init();
71134 baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound;
71135 baseNodeConfig.initialState = ma_node_state_stopped; /* Sounds are stopped by default. */
71136 } else {
71137 /* Group. */
71138 baseNodeConfig = ma_node_config_init();
71139 baseNodeConfig.vtable = &g_ma_engine_node_vtable__group;
71140 baseNodeConfig.initialState = ma_node_state_started; /* Groups are started by default. */
71141 }
71142
71143 return baseNodeConfig;
71144}
71145
71146static ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig)
71147{
71148 return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]);
71149}
71150
71151typedef struct
71152{
71153 size_t sizeInBytes;
71154 size_t baseNodeOffset;
71155 size_t resamplerOffset;
71156 size_t spatializerOffset;
71157} ma_engine_node_heap_layout;
71158
71159static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout)
71160{
71161 ma_result result;
71162 size_t tempHeapSize;
71163 ma_node_config baseNodeConfig;
71164 ma_linear_resampler_config resamplerConfig;
71165 ma_spatializer_config spatializerConfig;
71166 ma_uint32 channelsIn;
71167 ma_uint32 channelsOut;
71168
71169 MA_ASSERT(pHeapLayout);
71170
71171 MA_ZERO_OBJECT(pHeapLayout);
71172
71173 if (pConfig == NULL) {
71174 return MA_INVALID_ARGS;
71175 }
71176
71177 if (pConfig->pEngine == NULL) {
71178 return MA_INVALID_ARGS; /* An engine must be specified. */
71179 }
71180
71181 pHeapLayout->sizeInBytes = 0;
71182
71183 channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine);
71184 channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine);
71185
71186
71187 /* Base node. */
71188 baseNodeConfig = ma_engine_node_base_node_config_init(pConfig);
71189 baseNodeConfig.pInputChannels = &channelsIn;
71190 baseNodeConfig.pOutputChannels = &channelsOut;
71191
71192 result = ma_node_get_heap_size(ma_engine_get_node_graph(pConfig->pEngine), &baseNodeConfig, &tempHeapSize);
71193 if (result != MA_SUCCESS) {
71194 return result; /* Failed to retrieve the size of the heap for the base node. */
71195 }
71196
71197 pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes;
71198 pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
71199
71200
71201 /* Resmapler. */
71202 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. */
71203 resamplerConfig.lpfOrder = 0;
71204
71205 result = ma_linear_resampler_get_heap_size(&resamplerConfig, &tempHeapSize);
71206 if (result != MA_SUCCESS) {
71207 return result; /* Failed to retrieve the size of the heap for the resampler. */
71208 }
71209
71210 pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes;
71211 pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
71212
71213
71214 /* Spatializer. */
71215 spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig);
71216
71217 result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize);
71218 if (result != MA_SUCCESS) {
71219 return result; /* Failed to retrieve the size of the heap for the spatializer. */
71220 }
71221
71222 pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes;
71223 pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
71224
71225
71226 return MA_SUCCESS;
71227}
71228
71229MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes)
71230{
71231 ma_result result;
71232 ma_engine_node_heap_layout heapLayout;
71233
71234 if (pHeapSizeInBytes == NULL) {
71235 return MA_INVALID_ARGS;
71236 }
71237
71238 *pHeapSizeInBytes = 0;
71239
71240 result = ma_engine_node_get_heap_layout(pConfig, &heapLayout);
71241 if (result != MA_SUCCESS) {
71242 return result;
71243 }
71244
71245 *pHeapSizeInBytes = heapLayout.sizeInBytes;
71246
71247 return MA_SUCCESS;
71248}
71249
71251{
71252 ma_result result;
71253 ma_engine_node_heap_layout heapLayout;
71254 ma_node_config baseNodeConfig;
71255 ma_linear_resampler_config resamplerConfig;
71256 ma_fader_config faderConfig;
71257 ma_spatializer_config spatializerConfig;
71258 ma_panner_config pannerConfig;
71259 ma_uint32 channelsIn;
71260 ma_uint32 channelsOut;
71261
71262 if (pEngineNode == NULL) {
71263 return MA_INVALID_ARGS;
71264 }
71265
71266 MA_ZERO_OBJECT(pEngineNode);
71267
71268 result = ma_engine_node_get_heap_layout(pConfig, &heapLayout);
71269 if (result != MA_SUCCESS) {
71270 return result;
71271 }
71272
71274 return MA_INVALID_ARGS; /* Invalid listener. */
71275 }
71276
71277 pEngineNode->_pHeap = pHeap;
71278 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
71279
71280 pEngineNode->pEngine = pConfig->pEngine;
71281 pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine);
71282 pEngineNode->pitch = 1;
71283 pEngineNode->oldPitch = 1;
71284 pEngineNode->oldDopplerPitch = 1;
71285 pEngineNode->isPitchDisabled = pConfig->isPitchDisabled;
71286 pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled;
71287 pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex;
71288
71289
71290 channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine);
71291 channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine);
71292
71293
71294 /* Base node. */
71295 baseNodeConfig = ma_engine_node_base_node_config_init(pConfig);
71296 baseNodeConfig.pInputChannels = &channelsIn;
71297 baseNodeConfig.pOutputChannels = &channelsOut;
71298
71299 result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode);
71300 if (result != MA_SUCCESS) {
71301 goto error0;
71302 }
71303
71304
71305 /*
71306 We can now initialize the effects we need in order to implement the engine node. There's a
71307 defined order of operations here, mainly centered around when we convert our channels from the
71308 data source's native channel count to the engine's channel count. As a rule, we want to do as
71309 much computation as possible before spatialization because there's a chance that will increase
71310 the channel count, thereby increasing the amount of work needing to be done to process.
71311 */
71312
71313 /* We'll always do resampling first. */
71314 resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], pEngineNode->sampleRate, ma_engine_get_sample_rate(pEngineNode->pEngine));
71315 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. */
71316
71317 result = ma_linear_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pEngineNode->resampler);
71318 if (result != MA_SUCCESS) {
71319 goto error1;
71320 }
71321
71322
71323 /* After resampling will come the fader. */
71324 faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine));
71325
71326 result = ma_fader_init(&faderConfig, &pEngineNode->fader);
71327 if (result != MA_SUCCESS) {
71328 goto error2;
71329 }
71330
71331
71332 /*
71333 Spatialization comes next. We spatialize based ont he node's output channel count. It's up the caller to
71334 ensure channels counts link up correctly in the node graph.
71335 */
71336 spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig);
71337 spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames;
71338
71339 result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer);
71340 if (result != MA_SUCCESS) {
71341 goto error2;
71342 }
71343
71344
71345 /*
71346 After spatialization comes panning. We need to do this after spatialization because otherwise we wouldn't
71347 be able to pan mono sounds.
71348 */
71349 pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]);
71350
71351 result = ma_panner_init(&pannerConfig, &pEngineNode->panner);
71352 if (result != MA_SUCCESS) {
71353 goto error3;
71354 }
71355
71356 return MA_SUCCESS;
71357
71358 /* No need for allocation callbacks here because we use a preallocated heap. */
71359error3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL);
71360error2: ma_linear_resampler_uninit(&pEngineNode->resampler, NULL);
71361error1: ma_node_uninit(&pEngineNode->baseNode, NULL);
71362error0: return result;
71363}
71364
71365MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode)
71366{
71367 ma_result result;
71368 size_t heapSizeInBytes;
71369 void* pHeap;
71370
71371 result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes);
71372 if (result != MA_SUCCESS) {
71373 return result;
71374 }
71375
71376 if (heapSizeInBytes > 0) {
71377 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
71378 if (pHeap == NULL) {
71379 return MA_OUT_OF_MEMORY;
71380 }
71381 } else {
71382 pHeap = NULL;
71383 }
71384
71385 result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode);
71386 if (result != MA_SUCCESS) {
71387 ma_free(pHeap, pAllocationCallbacks);
71388 return result;
71389 }
71390
71391 pEngineNode->_ownsHeap = MA_TRUE;
71392 return MA_SUCCESS;
71393}
71394
71395MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks)
71396{
71397 /*
71398 The base node always needs to be uninitialized first to ensure it's detached from the graph completely before we
71399 destroy anything that might be in the middle of being used by the processing function.
71400 */
71401 ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks);
71402
71403 /* Now that the node has been uninitialized we can safely uninitialize the rest. */
71404 ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks);
71405 ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks);
71406
71407 /* Free the heap last. */
71408 if (pEngineNode->_ownsHeap) {
71409 ma_free(pEngineNode->_pHeap, pAllocationCallbacks);
71410 }
71411}
71412
71413
71415{
71416 ma_sound_config config;
71417
71418 MA_ZERO_OBJECT(&config);
71419 config.rangeEndInPCMFrames = ~((ma_uint64)0);
71420 config.loopPointEndInPCMFrames = ~((ma_uint64)0);
71421
71422 return config;
71423}
71424
71426{
71427 ma_sound_group_config config;
71428
71429 MA_ZERO_OBJECT(&config);
71430
71431 return config;
71432}
71433
71434
71436{
71437 ma_engine_config config;
71438
71439 MA_ZERO_OBJECT(&config);
71440 config.listenerCount = 1; /* Always want at least one listener. */
71442
71443 return config;
71444}
71445
71446
71447#if !defined(MA_NO_DEVICE_IO)
71448static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
71449{
71450 ma_engine* pEngine = (ma_engine*)pDevice->pUserData;
71451
71452 (void)pFramesIn;
71453
71454 /*
71455 Experiment: Try processing a resource manager job if we're on the Emscripten build.
71456
71457 This serves two purposes:
71458
71459 1) It ensures jobs are actually processed at some point since we cannot guarantee that the
71460 caller is doing the right thing and calling ma_resource_manager_process_next_job(); and
71461
71462 2) It's an attempt at working around an issue where processing jobs on the Emscripten main
71463 loop doesn't work as well as it should. When trying to load sounds without the `DECODE`
71464 flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time
71465 before the callback is processed. I think it's got something to do with the single-
71466 threaded nature of Web, but I'm not entirely sure.
71467 */
71468 #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN)
71469 {
71470 if (pEngine->pResourceManager != NULL) {
71473 }
71474 }
71475 }
71476 #endif
71477
71478 ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL);
71479}
71480#endif
71481
71483{
71484 ma_result result;
71485 ma_node_graph_config nodeGraphConfig;
71486 ma_engine_config engineConfig;
71487 ma_spatializer_listener_config listenerConfig;
71488 ma_uint32 iListener;
71489
71490 if (pEngine == NULL) {
71491 return MA_INVALID_ARGS;
71492 }
71493
71494 MA_ZERO_OBJECT(pEngine);
71495
71496 /* The config is allowed to be NULL in which case we use defaults for everything. */
71497 if (pConfig != NULL) {
71498 engineConfig = *pConfig;
71499 } else {
71500 engineConfig = ma_engine_config_init();
71501 }
71502
71503 pEngine->monoExpansionMode = engineConfig.monoExpansionMode;
71504 ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks);
71505
71506 #if !defined(MA_NO_RESOURCE_MANAGER)
71507 {
71508 pEngine->pResourceManager = engineConfig.pResourceManager;
71509 }
71510 #endif
71511
71512 #if !defined(MA_NO_DEVICE_IO)
71513 {
71514 pEngine->pDevice = engineConfig.pDevice;
71515
71516 /* If we don't have a device, we need one. */
71517 if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) {
71518 ma_device_config deviceConfig;
71519
71520 pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks);
71521 if (pEngine->pDevice == NULL) {
71522 return MA_OUT_OF_MEMORY;
71523 }
71524
71526 deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID;
71527 deviceConfig.playback.format = ma_format_f32;
71528 deviceConfig.playback.channels = engineConfig.channels;
71529 deviceConfig.sampleRate = engineConfig.sampleRate;
71530 deviceConfig.dataCallback = ma_engine_data_callback_internal;
71531 deviceConfig.pUserData = pEngine;
71532 deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames;
71533 deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds;
71534 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. */
71535 deviceConfig.noClip = MA_TRUE; /* The engine will do clipping itself. */
71536
71537 if (engineConfig.pContext == NULL) {
71538 ma_context_config contextConfig = ma_context_config_init();
71539 contextConfig.allocationCallbacks = pEngine->allocationCallbacks;
71540 contextConfig.pLog = engineConfig.pLog;
71541
71542 /* If the engine config does not specify a log, use the resource manager's if we have one. */
71543 #ifndef MA_NO_RESOURCE_MANAGER
71544 {
71545 if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) {
71546 contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager);
71547 }
71548 }
71549 #endif
71550
71551 result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice);
71552 } else {
71553 result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice);
71554 }
71555
71556 if (result != MA_SUCCESS) {
71557 ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
71558 pEngine->pDevice = NULL;
71559 return result;
71560 }
71561
71562 pEngine->ownsDevice = MA_TRUE;
71563 }
71564
71565 /* Update the channel count and sample rate of the engine config so we can reference it below. */
71566 if (pEngine->pDevice != NULL) {
71567 engineConfig.channels = pEngine->pDevice->playback.channels;
71568 engineConfig.sampleRate = pEngine->pDevice->sampleRate;
71569 }
71570 }
71571 #endif
71572
71573 if (engineConfig.channels == 0 || engineConfig.sampleRate == 0) {
71574 return MA_INVALID_ARGS;
71575 }
71576
71577 pEngine->sampleRate = engineConfig.sampleRate;
71578
71579 /* The engine always uses either the log that was passed into the config, or the context's log is available. */
71580 if (engineConfig.pLog != NULL) {
71581 pEngine->pLog = engineConfig.pLog;
71582 } else {
71583 #if !defined(MA_NO_DEVICE_IO)
71584 {
71585 pEngine->pLog = ma_device_get_log(pEngine->pDevice);
71586 }
71587 #else
71588 {
71589 pEngine->pLog = NULL;
71590 }
71591 #endif
71592 }
71593
71594
71595 /* The engine is a node graph. This needs to be initialized after we have the device so we can can determine the channel count. */
71596 nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels);
71597 nodeGraphConfig.nodeCacheCapInFrames = (engineConfig.periodSizeInFrames > 0xFFFF) ? 0xFFFF : (ma_uint16)engineConfig.periodSizeInFrames;
71598
71599 result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph);
71600 if (result != MA_SUCCESS) {
71601 goto on_error_1;
71602 }
71603
71604
71605 /* We need at least one listener. */
71606 if (engineConfig.listenerCount == 0) {
71607 engineConfig.listenerCount = 1;
71608 }
71609
71610 if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) {
71611 result = MA_INVALID_ARGS; /* Too many listeners. */
71612 goto on_error_1;
71613 }
71614
71615 for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) {
71617
71618 /*
71619 If we're using a device, use the device's channel map for the listener. Otherwise just use
71620 miniaudio's default channel map.
71621 */
71622 #if !defined(MA_NO_DEVICE_IO)
71623 {
71624 if (pEngine->pDevice != NULL) {
71625 /*
71626 Temporarily disabled. There is a subtle bug here where front-left and front-right
71627 will be used by the device's channel map, but this is not what we want to use for
71628 spatialization. Instead we want to use side-left and side-right. I need to figure
71629 out a better solution for this. For now, disabling the user of device channel maps.
71630 */
71631 /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/
71632 }
71633 }
71634 #endif
71635
71636 result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]); /* TODO: Change this to a pre-allocated heap. */
71637 if (result != MA_SUCCESS) {
71638 goto on_error_2;
71639 }
71640
71641 pEngine->listenerCount += 1;
71642 }
71643
71644
71645 /* Gain smoothing for spatialized sounds. */
71646 pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames;
71647 if (pEngine->gainSmoothTimeInFrames == 0) {
71648 ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds;
71649 if (gainSmoothTimeInMilliseconds == 0) {
71650 gainSmoothTimeInMilliseconds = 8;
71651 }
71652
71653 pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000; /* 8ms by default. */
71654 }
71655
71656
71657 /* We need a resource manager. */
71658 #ifndef MA_NO_RESOURCE_MANAGER
71659 {
71660 if (pEngine->pResourceManager == NULL) {
71661 ma_resource_manager_config resourceManagerConfig;
71662
71663 pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks);
71664 if (pEngine->pResourceManager == NULL) {
71665 result = MA_OUT_OF_MEMORY;
71666 goto on_error_2;
71667 }
71668
71669 resourceManagerConfig = ma_resource_manager_config_init();
71670 resourceManagerConfig.pLog = pEngine->pLog; /* Always use the engine's log for internally-managed resource managers. */
71671 resourceManagerConfig.decodedFormat = ma_format_f32;
71672 resourceManagerConfig.decodedChannels = 0; /* Leave the decoded channel count as 0 so we can get good spatialization. */
71673 resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine);
71674 ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks);
71675 resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS;
71676
71677 /* The Emscripten build cannot use threads. */
71678 #if defined(MA_EMSCRIPTEN)
71679 {
71680 resourceManagerConfig.jobThreadCount = 0;
71681 resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
71682 }
71683 #endif
71684
71685 result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager);
71686 if (result != MA_SUCCESS) {
71687 goto on_error_3;
71688 }
71689
71690 pEngine->ownsResourceManager = MA_TRUE;
71691 }
71692 }
71693 #endif
71694
71695 /* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */
71696 pEngine->inlinedSoundLock = 0;
71697 pEngine->pInlinedSoundHead = NULL;
71698
71699 /* Start the engine if required. This should always be the last step. */
71700 #if !defined(MA_NO_DEVICE_IO)
71701 {
71702 if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) {
71703 result = ma_engine_start(pEngine);
71704 if (result != MA_SUCCESS) {
71705 goto on_error_4; /* Failed to start the engine. */
71706 }
71707 }
71708 }
71709 #endif
71710
71711 return MA_SUCCESS;
71712
71713#if !defined(MA_NO_DEVICE_IO)
71714on_error_4:
71715#endif
71716#if !defined(MA_NO_RESOURCE_MANAGER)
71717on_error_3:
71718 if (pEngine->ownsResourceManager) {
71719 ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks);
71720 }
71721#endif /* MA_NO_RESOURCE_MANAGER */
71722on_error_2:
71723 for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
71724 ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks);
71725 }
71726
71728on_error_1:
71729 #if !defined(MA_NO_DEVICE_IO)
71730 {
71731 if (pEngine->ownsDevice) {
71732 ma_device_uninit(pEngine->pDevice);
71733 ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
71734 }
71735 }
71736 #endif
71737
71738 return result;
71739}
71740
71741MA_API void ma_engine_uninit(ma_engine* pEngine)
71742{
71743 ma_uint32 iListener;
71744
71745 if (pEngine == NULL) {
71746 return;
71747 }
71748
71749 /* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */
71750 #if !defined(MA_NO_DEVICE_IO)
71751 {
71752 if (pEngine->ownsDevice) {
71753 ma_device_uninit(pEngine->pDevice);
71754 ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
71755 } else {
71756 if (pEngine->pDevice != NULL) {
71757 ma_device_stop(pEngine->pDevice);
71758 }
71759 }
71760 }
71761 #endif
71762
71763 /*
71764 All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case
71765 I want to do some kind of garbage collection later on.
71766 */
71768 {
71769 for (;;) {
71770 ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead;
71771 if (pSoundToDelete == NULL) {
71772 break; /* Done. */
71773 }
71774
71775 pEngine->pInlinedSoundHead = pSoundToDelete->pNext;
71776
71777 ma_sound_uninit(&pSoundToDelete->sound);
71778 ma_free(pSoundToDelete, &pEngine->allocationCallbacks);
71779 }
71780 }
71782
71783 for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
71784 ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks);
71785 }
71786
71787 /* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */
71789
71790 /* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */
71791#ifndef MA_NO_RESOURCE_MANAGER
71792 if (pEngine->ownsResourceManager) {
71794 ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks);
71795 }
71796#endif
71797}
71798
71799MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
71800{
71801 return ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, pFramesRead);
71802}
71803
71805{
71806 if (pEngine == NULL) {
71807 return NULL;
71808 }
71809
71810 return &pEngine->nodeGraph;
71811}
71812
71813#if !defined(MA_NO_RESOURCE_MANAGER)
71815{
71816 if (pEngine == NULL) {
71817 return NULL;
71818 }
71819
71820 #if !defined(MA_NO_RESOURCE_MANAGER)
71821 {
71822 return pEngine->pResourceManager;
71823 }
71824 #else
71825 {
71826 return NULL;
71827 }
71828 #endif
71829}
71830#endif
71831
71833{
71834 if (pEngine == NULL) {
71835 return NULL;
71836 }
71837
71838 #if !defined(MA_NO_DEVICE_IO)
71839 {
71840 return pEngine->pDevice;
71841 }
71842 #else
71843 {
71844 return NULL;
71845 }
71846 #endif
71847}
71848
71850{
71851 if (pEngine == NULL) {
71852 return NULL;
71853 }
71854
71855 if (pEngine->pLog != NULL) {
71856 return pEngine->pLog;
71857 } else {
71858 #if !defined(MA_NO_DEVICE_IO)
71859 {
71860 return ma_device_get_log(ma_engine_get_device(pEngine));
71861 }
71862 #else
71863 {
71864 return NULL;
71865 }
71866 #endif
71867 }
71868}
71869
71871{
71872 return ma_node_graph_get_endpoint(&pEngine->nodeGraph);
71873}
71874
71876{
71877 return ma_node_graph_get_time(&pEngine->nodeGraph);
71878}
71879
71881{
71882 return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime);
71883}
71884
71886{
71887 return ma_node_graph_get_channels(&pEngine->nodeGraph);
71888}
71889
71891{
71892 if (pEngine == NULL) {
71893 return 0;
71894 }
71895
71896 return pEngine->sampleRate;
71897}
71898
71899
71901{
71902 ma_result result;
71903
71904 if (pEngine == NULL) {
71905 return MA_INVALID_ARGS;
71906 }
71907
71908 #if !defined(MA_NO_DEVICE_IO)
71909 {
71910 if (pEngine->pDevice != NULL) {
71911 result = ma_device_start(pEngine->pDevice);
71912 } else {
71913 result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "starting" the engine. */
71914 }
71915 }
71916 #else
71917 {
71918 result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "starting" the engine. */
71919 }
71920 #endif
71921
71922 if (result != MA_SUCCESS) {
71923 return result;
71924 }
71925
71926 return MA_SUCCESS;
71927}
71928
71930{
71931 ma_result result;
71932
71933 if (pEngine == NULL) {
71934 return MA_INVALID_ARGS;
71935 }
71936
71937 #if !defined(MA_NO_DEVICE_IO)
71938 {
71939 if (pEngine->pDevice != NULL) {
71940 result = ma_device_stop(pEngine->pDevice);
71941 } else {
71942 result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "stopping" the engine. */
71943 }
71944 }
71945 #else
71946 {
71947 result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "stopping" the engine. */
71948 }
71949 #endif
71950
71951 if (result != MA_SUCCESS) {
71952 return result;
71953 }
71954
71955 return MA_SUCCESS;
71956}
71957
71958MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume)
71959{
71960 if (pEngine == NULL) {
71961 return MA_INVALID_ARGS;
71962 }
71963
71965}
71966
71967MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB)
71968{
71969 if (pEngine == NULL) {
71970 return MA_INVALID_ARGS;
71971 }
71972
71974}
71975
71976
71978{
71979 if (pEngine == NULL) {
71980 return 0;
71981 }
71982
71983 return pEngine->listenerCount;
71984}
71985
71986MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ)
71987{
71988 ma_uint32 iListener;
71989 ma_uint32 iListenerClosest;
71990 float closestLen2 = MA_FLT_MAX;
71991
71992 if (pEngine == NULL || pEngine->listenerCount == 1) {
71993 return 0;
71994 }
71995
71996 iListenerClosest = 0;
71997 for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
71998 if (ma_engine_listener_is_enabled(pEngine, iListener)) {
71999 float len2 = ma_vec3f_len2(ma_vec3f_sub(pEngine->listeners[iListener].position, ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ)));
72000 if (closestLen2 > len2) {
72001 closestLen2 = len2;
72002 iListenerClosest = iListener;
72003 }
72004 }
72005 }
72006
72007 MA_ASSERT(iListenerClosest < 255);
72008 return iListenerClosest;
72009}
72010
72011MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
72012{
72013 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
72014 return;
72015 }
72016
72017 ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z);
72018}
72019
72021{
72022 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
72023 return ma_vec3f_init_3f(0, 0, 0);
72024 }
72025
72026 return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]);
72027}
72028
72029MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
72030{
72031 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
72032 return;
72033 }
72034
72035 ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z);
72036}
72037
72039{
72040 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
72041 return ma_vec3f_init_3f(0, 0, -1);
72042 }
72043
72044 return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]);
72045}
72046
72047MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
72048{
72049 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
72050 return;
72051 }
72052
72053 ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z);
72054}
72055
72057{
72058 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
72059 return ma_vec3f_init_3f(0, 0, 0);
72060 }
72061
72062 return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]);
72063}
72064
72065MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
72066{
72067 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
72068 return;
72069 }
72070
72071 ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain);
72072}
72073
72074MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
72075{
72076 if (pInnerAngleInRadians != NULL) {
72077 *pInnerAngleInRadians = 0;
72078 }
72079
72080 if (pOuterAngleInRadians != NULL) {
72081 *pOuterAngleInRadians = 0;
72082 }
72083
72084 if (pOuterGain != NULL) {
72085 *pOuterGain = 0;
72086 }
72087
72088 ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
72089}
72090
72091MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
72092{
72093 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
72094 return;
72095 }
72096
72097 ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z);
72098}
72099
72101{
72102 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
72103 return ma_vec3f_init_3f(0, 1, 0);
72104 }
72105
72106 return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]);
72107}
72108
72109MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled)
72110{
72111 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
72112 return;
72113 }
72114
72115 ma_spatializer_listener_set_enabled(&pEngine->listeners[listenerIndex], isEnabled);
72116}
72117
72119{
72120 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
72121 return MA_FALSE;
72122 }
72123
72124 return ma_spatializer_listener_is_enabled(&pEngine->listeners[listenerIndex]);
72125}
72126
72127
72128#ifndef MA_NO_RESOURCE_MANAGER
72129MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex)
72130{
72131 ma_result result = MA_SUCCESS;
72132 ma_sound_inlined* pSound = NULL;
72133 ma_sound_inlined* pNextSound = NULL;
72134
72135 if (pEngine == NULL || pFilePath == NULL) {
72136 return MA_INVALID_ARGS;
72137 }
72138
72139 /* Attach to the endpoint node if nothing is specicied. */
72140 if (pNode == NULL) {
72141 pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph);
72142 nodeInputBusIndex = 0;
72143 }
72144
72145 /*
72146 We want to check if we can recycle an already-allocated inlined sound. Since this is just a
72147 helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep
72148 the implementation simple. Maybe this can be optimized later if there's enough demand, but
72149 if this function is being used it probably means the caller doesn't really care too much.
72150
72151 What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise
72152 we just keep iterating. If we reach the end without finding a sound to recycle we just
72153 allocate a new one. This doesn't scale well for a massive number of sounds being played
72154 simultaneously as we don't ever actually free the sound objects. Some kind of garbage
72155 collection routine might be valuable for this which I'll think about.
72156 */
72158 {
72159 ma_uint32 soundFlags = 0;
72160
72161 for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) {
72162 if (ma_sound_at_end(&pNextSound->sound)) {
72163 /*
72164 The sound is at the end which means it's available for recycling. All we need to do
72165 is uninitialize it and reinitialize it. All we're doing is recycling memory.
72166 */
72167 pSound = pNextSound;
72168 c89atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1);
72169 break;
72170 }
72171 }
72172
72173 if (pSound != NULL) {
72174 /*
72175 We actually want to detach the sound from the list here. The reason is because we want the sound
72176 to be in a consistent state at the non-recycled case to simplify the logic below.
72177 */
72178 if (pEngine->pInlinedSoundHead == pSound) {
72179 pEngine->pInlinedSoundHead = pSound->pNext;
72180 }
72181
72182 if (pSound->pPrev != NULL) {
72183 pSound->pPrev->pNext = pSound->pNext;
72184 }
72185 if (pSound->pNext != NULL) {
72186 pSound->pNext->pPrev = pSound->pPrev;
72187 }
72188
72189 /* Now the previous sound needs to be uninitialized. */
72190 ma_sound_uninit(&pNextSound->sound);
72191 } else {
72192 /* No sound available for recycling. Allocate one now. */
72193 pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks);
72194 }
72195
72196 if (pSound != NULL) { /* Safety check for the allocation above. */
72197 /*
72198 At this point we should have memory allocated for the inlined sound. We just need
72199 to initialize it like a normal sound now.
72200 */
72201 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. */
72202 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. */
72203 soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */
72204 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. */
72205
72206 result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound);
72207 if (result == MA_SUCCESS) {
72208 /* Now attach the sound to the graph. */
72209 result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex);
72210 if (result == MA_SUCCESS) {
72211 /* 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. */
72212 pSound->pNext = pEngine->pInlinedSoundHead;
72213 pSound->pPrev = NULL;
72214
72215 pEngine->pInlinedSoundHead = pSound; /* <-- This is what attaches the sound to the list. */
72216 if (pSound->pNext != NULL) {
72217 pSound->pNext->pPrev = pSound;
72218 }
72219 } else {
72220 ma_free(pSound, &pEngine->allocationCallbacks);
72221 }
72222 } else {
72223 ma_free(pSound, &pEngine->allocationCallbacks);
72224 }
72225 } else {
72226 result = MA_OUT_OF_MEMORY;
72227 }
72228 }
72230
72231 if (result != MA_SUCCESS) {
72232 return result;
72233 }
72234
72235 /* Finally we can start playing the sound. */
72236 result = ma_sound_start(&pSound->sound);
72237 if (result != MA_SUCCESS) {
72238 /* Failed to start the sound. We need to mark it for recycling and return an error. */
72239 c89atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE);
72240 return result;
72241 }
72242
72243 c89atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1);
72244 return result;
72245}
72246
72247MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup)
72248{
72249 return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0);
72250}
72251#endif
72252
72253
72254static ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound)
72255{
72256 if (pSound == NULL) {
72257 return MA_INVALID_ARGS;
72258 }
72259
72260 MA_ZERO_OBJECT(pSound);
72261 pSound->seekTarget = MA_SEEK_TARGET_NONE;
72262
72263 if (pEngine == NULL) {
72264 return MA_INVALID_ARGS;
72265 }
72266
72267 return MA_SUCCESS;
72268}
72269
72270static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
72271{
72272 ma_result result;
72273 ma_engine_node_config engineNodeConfig;
72274 ma_engine_node_type type; /* Will be set to ma_engine_node_type_group if no data source is specified. */
72275
72276 /* Do not clear pSound to zero here - that's done at a higher level with ma_sound_preinit(). */
72277 MA_ASSERT(pEngine != NULL);
72278 MA_ASSERT(pSound != NULL);
72279
72280 if (pConfig == NULL) {
72281 return MA_INVALID_ARGS;
72282 }
72283
72284 pSound->pDataSource = pConfig->pDataSource;
72285
72286 if (pConfig->pDataSource != NULL) {
72288 } else {
72290 }
72291
72292 /*
72293 Sounds are engine nodes. Before we can initialize this we need to determine the channel count.
72294 If we can't do this we need to abort. It's up to the caller to ensure they're using a data
72295 source that provides this information upfront.
72296 */
72297 engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags);
72298 engineNodeConfig.channelsIn = pConfig->channelsIn;
72299 engineNodeConfig.channelsOut = pConfig->channelsOut;
72300
72301 /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */
72302 if (pConfig->pDataSource != NULL) {
72303 result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate, NULL, 0);
72304 if (result != MA_SUCCESS) {
72305 return result; /* Failed to retrieve the channel count. */
72306 }
72307
72308 if (engineNodeConfig.channelsIn == 0) {
72309 return MA_INVALID_OPERATION; /* Invalid channel count. */
72310 }
72311
72312 if (engineNodeConfig.channelsOut == MA_SOUND_SOURCE_CHANNEL_COUNT) {
72313 engineNodeConfig.channelsOut = engineNodeConfig.channelsIn;
72314 }
72315 }
72316
72317
72318 /* Getting here means we should have a valid channel count and we can initialize the engine node. */
72319 result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode);
72320 if (result != MA_SUCCESS) {
72321 return result;
72322 }
72323
72324 /* If no attachment is specified, attach the sound straight to the endpoint. */
72325 if (pConfig->pInitialAttachment == NULL) {
72326 /* No group. Attach straight to the endpoint by default, unless the caller has requested that do not. */
72327 if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) {
72328 result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0);
72329 }
72330 } else {
72331 /* 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. */
72332 result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex);
72333 }
72334
72335 if (result != MA_SUCCESS) {
72337 return result;
72338 }
72339
72340
72341 /* Apply initial range and looping state to the data source if applicable. */
72342 if (pConfig->rangeBegInPCMFrames != 0 || pConfig->rangeEndInPCMFrames != ~((ma_uint64)0)) {
72344 }
72345
72346 if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) {
72348 }
72349
72350 ma_sound_set_looping(pSound, pConfig->isLooping);
72351
72352 return MA_SUCCESS;
72353}
72354
72355#ifndef MA_NO_RESOURCE_MANAGER
72356MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
72357{
72358 ma_result result = MA_SUCCESS;
72359 ma_uint32 flags;
72360 ma_sound_config config;
72362
72363 /*
72364 The engine requires knowledge of the channel count of the underlying data source before it can
72365 initialize the sound. Therefore, we need to make the resource manager wait until initialization
72366 of the underlying data source to be initialized so we can get access to the channel count. To
72367 do this, the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT is forced.
72368
72369 Because we're initializing the data source before the sound, there's a chance the notification
72370 will get triggered before this function returns. This is OK, so long as the caller is aware of
72371 it and can avoid accessing the sound from within the notification.
72372 */
72374
72376 if (pSound->pResourceManagerDataSource == NULL) {
72377 return MA_OUT_OF_MEMORY;
72378 }
72379
72381 notifications.done.pFence = pConfig->pDoneFence;
72382
72383 /*
72384 We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does
72385 not return prematurely before the sound has finished initializing.
72386 */
72387 if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); }
72388 {
72390 resourceManagerDataSourceConfig.pFilePath = pConfig->pFilePath;
72391 resourceManagerDataSourceConfig.pFilePathW = pConfig->pFilePathW;
72392 resourceManagerDataSourceConfig.flags = flags;
72393 resourceManagerDataSourceConfig.pNotifications = &notifications;
72394 resourceManagerDataSourceConfig.initialSeekPointInPCMFrames = pConfig->initialSeekPointInPCMFrames;
72395 resourceManagerDataSourceConfig.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames;
72396 resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames;
72397 resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames;
72398 resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames;
72399 resourceManagerDataSourceConfig.isLooping = pConfig->isLooping;
72400
72401 result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource);
72402 if (result != MA_SUCCESS) {
72403 goto done;
72404 }
72405
72406 pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */
72407
72408 /* We need to use a slightly customized version of the config so we'll need to make a copy. */
72409 config = *pConfig;
72410 config.pFilePath = NULL;
72411 config.pFilePathW = NULL;
72412 config.pDataSource = pSound->pResourceManagerDataSource;
72413
72414 result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound);
72415 if (result != MA_SUCCESS) {
72418 MA_ZERO_OBJECT(pSound);
72419 goto done;
72420 }
72421 }
72422done:
72423 if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); }
72424 return result;
72425}
72426
72427MA_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)
72428{
72430 config.pFilePath = pFilePath;
72431 config.flags = flags;
72432 config.pInitialAttachment = pGroup;
72433 config.pDoneFence = pDoneFence;
72434 return ma_sound_init_ex(pEngine, &config, pSound);
72435}
72436
72437MA_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)
72438{
72440 config.pFilePathW = pFilePath;
72441 config.flags = flags;
72442 config.pInitialAttachment = pGroup;
72443 config.pDoneFence = pDoneFence;
72444 return ma_sound_init_ex(pEngine, &config, pSound);
72445}
72446
72447MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound)
72448{
72449 ma_result result;
72450 ma_sound_config config;
72451
72452 result = ma_sound_preinit(pEngine, pSound);
72453 if (result != MA_SUCCESS) {
72454 return result;
72455 }
72456
72457 if (pExistingSound == NULL) {
72458 return MA_INVALID_ARGS;
72459 }
72460
72461 /* Cloning only works for data buffers (not streams) that are loaded from the resource manager. */
72462 if (pExistingSound->pResourceManagerDataSource == NULL) {
72463 return MA_INVALID_OPERATION;
72464 }
72465
72466 /*
72467 We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream)
72468 the this will fail.
72469 */
72471 if (pSound->pResourceManagerDataSource == NULL) {
72472 return MA_OUT_OF_MEMORY;
72473 }
72474
72476 if (result != MA_SUCCESS) {
72478 return result;
72479 }
72480
72481 config = ma_sound_config_init();
72482 config.pDataSource = pSound->pResourceManagerDataSource;
72483 config.flags = flags;
72484 config.pInitialAttachment = pGroup;
72485
72486 result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound);
72487 if (result != MA_SUCCESS) {
72490 MA_ZERO_OBJECT(pSound);
72491 return result;
72492 }
72493
72494 return MA_SUCCESS;
72495}
72496#endif
72497
72499{
72501 config.pDataSource = pDataSource;
72502 config.flags = flags;
72503 config.pInitialAttachment = pGroup;
72504 return ma_sound_init_ex(pEngine, &config, pSound);
72505}
72506
72507MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
72508{
72509 ma_result result;
72510
72511 result = ma_sound_preinit(pEngine, pSound);
72512 if (result != MA_SUCCESS) {
72513 return result;
72514 }
72515
72516 if (pConfig == NULL) {
72517 return MA_INVALID_ARGS;
72518 }
72519
72520 /* We need to load the sound differently depending on whether or not we're loading from a file. */
72521#ifndef MA_NO_RESOURCE_MANAGER
72522 if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) {
72523 return ma_sound_init_from_file_internal(pEngine, pConfig, pSound);
72524 } else
72525#endif
72526 {
72527 /*
72528 Getting here means we're not loading from a file. We may be loading from an already-initialized
72529 data source, or none at all. If we aren't specifying any data source, we'll be initializing the
72530 the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this
72531 for us, so no special treatment required here.
72532 */
72533 return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound);
72534 }
72535}
72536
72537MA_API void ma_sound_uninit(ma_sound* pSound)
72538{
72539 if (pSound == NULL) {
72540 return;
72541 }
72542
72543 /*
72544 Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done
72545 so which makes thread safety beyond this point trivial.
72546 */
72548
72549 /* 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. */
72550#ifndef MA_NO_RESOURCE_MANAGER
72551 if (pSound->ownsDataSource) {
72554 pSound->pDataSource = NULL;
72555 }
72556#else
72557 MA_ASSERT(pSound->ownsDataSource == MA_FALSE);
72558#endif
72559}
72560
72562{
72563 if (pSound == NULL) {
72564 return NULL;
72565 }
72566
72567 return pSound->engineNode.pEngine;
72568}
72569
72571{
72572 if (pSound == NULL) {
72573 return NULL;
72574 }
72575
72576 return pSound->pDataSource;
72577}
72578
72580{
72581 if (pSound == NULL) {
72582 return MA_INVALID_ARGS;
72583 }
72584
72585 /* If the sound is already playing, do nothing. */
72586 if (ma_sound_is_playing(pSound)) {
72587 return MA_SUCCESS;
72588 }
72589
72590 /* If the sound is at the end it means we want to start from the start again. */
72591 if (ma_sound_at_end(pSound)) {
72593 if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) {
72594 return result; /* Failed to seek back to the start. */
72595 }
72596
72597 /* Make sure we clear the end indicator. */
72598 c89atomic_exchange_32(&pSound->atEnd, MA_FALSE);
72599 }
72600
72601 /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */
72603
72604 return MA_SUCCESS;
72605}
72606
72608{
72609 if (pSound == NULL) {
72610 return MA_INVALID_ARGS;
72611 }
72612
72613 /* This will stop the sound immediately. Use ma_sound_set_stop_time() to stop the sound at a specific time. */
72615
72616 return MA_SUCCESS;
72617}
72618
72619MA_API void ma_sound_set_volume(ma_sound* pSound, float volume)
72620{
72621 if (pSound == NULL) {
72622 return;
72623 }
72624
72625 /* The volume is controlled via the output bus. */
72626 ma_node_set_output_bus_volume(pSound, 0, volume);
72627}
72628
72629MA_API float ma_sound_get_volume(const ma_sound* pSound)
72630{
72631 if (pSound == NULL) {
72632 return 0;
72633 }
72634
72635 return ma_node_get_output_bus_volume(pSound, 0);
72636}
72637
72638MA_API void ma_sound_set_pan(ma_sound* pSound, float pan)
72639{
72640 if (pSound == NULL) {
72641 return;
72642 }
72643
72644 ma_panner_set_pan(&pSound->engineNode.panner, pan);
72645}
72646
72647MA_API float ma_sound_get_pan(const ma_sound* pSound)
72648{
72649 if (pSound == NULL) {
72650 return 0;
72651 }
72652
72653 return ma_panner_get_pan(&pSound->engineNode.panner);
72654}
72655
72656MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode)
72657{
72658 if (pSound == NULL) {
72659 return;
72660 }
72661
72662 ma_panner_set_mode(&pSound->engineNode.panner, panMode);
72663}
72664
72666{
72667 if (pSound == NULL) {
72668 return ma_pan_mode_balance;
72669 }
72670
72671 return ma_panner_get_mode(&pSound->engineNode.panner);
72672}
72673
72674MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch)
72675{
72676 if (pSound == NULL) {
72677 return;
72678 }
72679
72680 if (pitch <= 0) {
72681 return;
72682 }
72683
72684 c89atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, c89atomic_memory_order_release);
72685}
72686
72687MA_API float ma_sound_get_pitch(const ma_sound* pSound)
72688{
72689 if (pSound == NULL) {
72690 return 0;
72691 }
72692
72693 return c89atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */
72694}
72695
72697{
72698 if (pSound == NULL) {
72699 return;
72700 }
72701
72702 c89atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, c89atomic_memory_order_release);
72703}
72704
72706{
72707 if (pSound == NULL) {
72708 return MA_FALSE;
72709 }
72710
72711 return ma_engine_node_is_spatialization_enabled(&pSound->engineNode);
72712}
72713
72715{
72716 if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) {
72717 return;
72718 }
72719
72720 c89atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, c89atomic_memory_order_release);
72721}
72722
72724{
72725 if (pSound == NULL) {
72727 }
72728
72729 return c89atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, c89atomic_memory_order_acquire);
72730}
72731
72733{
72734 ma_uint32 listenerIndex;
72735
72736 if (pSound == NULL) {
72737 return 0;
72738 }
72739
72740 listenerIndex = ma_sound_get_pinned_listener_index(pSound);
72741 if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) {
72742 ma_vec3f position = ma_sound_get_position(pSound);
72743 return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z);
72744 }
72745
72746 return listenerIndex;
72747}
72748
72750{
72751 ma_vec3f relativePos;
72752 ma_engine* pEngine;
72753
72754 if (pSound == NULL) {
72755 return ma_vec3f_init_3f(0, 0, -1);
72756 }
72757
72758 pEngine = ma_sound_get_engine(pSound);
72759 if (pEngine == NULL) {
72760 return ma_vec3f_init_3f(0, 0, -1);
72761 }
72762
72764
72765 return ma_vec3f_normalize(ma_vec3f_neg(relativePos));
72766}
72767
72768MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z)
72769{
72770 if (pSound == NULL) {
72771 return;
72772 }
72773
72775}
72776
72778{
72779 if (pSound == NULL) {
72780 return ma_vec3f_init_3f(0, 0, 0);
72781 }
72782
72784}
72785
72786MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z)
72787{
72788 if (pSound == NULL) {
72789 return;
72790 }
72791
72793}
72794
72796{
72797 if (pSound == NULL) {
72798 return ma_vec3f_init_3f(0, 0, 0);
72799 }
72800
72802}
72803
72804MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z)
72805{
72806 if (pSound == NULL) {
72807 return;
72808 }
72809
72811}
72812
72814{
72815 if (pSound == NULL) {
72816 return ma_vec3f_init_3f(0, 0, 0);
72817 }
72818
72820}
72821
72823{
72824 if (pSound == NULL) {
72825 return;
72826 }
72827
72829}
72830
72832{
72833 if (pSound == NULL) {
72835 }
72836
72838}
72839
72840MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning)
72841{
72842 if (pSound == NULL) {
72843 return;
72844 }
72845
72847}
72848
72850{
72851 if (pSound == NULL) {
72853 }
72854
72856}
72857
72858MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff)
72859{
72860 if (pSound == NULL) {
72861 return;
72862 }
72863
72865}
72866
72867MA_API float ma_sound_get_rolloff(const ma_sound* pSound)
72868{
72869 if (pSound == NULL) {
72870 return 0;
72871 }
72872
72874}
72875
72876MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain)
72877{
72878 if (pSound == NULL) {
72879 return;
72880 }
72881
72883}
72884
72885MA_API float ma_sound_get_min_gain(const ma_sound* pSound)
72886{
72887 if (pSound == NULL) {
72888 return 0;
72889 }
72890
72892}
72893
72894MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain)
72895{
72896 if (pSound == NULL) {
72897 return;
72898 }
72899
72901}
72902
72903MA_API float ma_sound_get_max_gain(const ma_sound* pSound)
72904{
72905 if (pSound == NULL) {
72906 return 0;
72907 }
72908
72910}
72911
72912MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance)
72913{
72914 if (pSound == NULL) {
72915 return;
72916 }
72917
72919}
72920
72921MA_API float ma_sound_get_min_distance(const ma_sound* pSound)
72922{
72923 if (pSound == NULL) {
72924 return 0;
72925 }
72926
72928}
72929
72930MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance)
72931{
72932 if (pSound == NULL) {
72933 return;
72934 }
72935
72937}
72938
72939MA_API float ma_sound_get_max_distance(const ma_sound* pSound)
72940{
72941 if (pSound == NULL) {
72942 return 0;
72943 }
72944
72946}
72947
72948MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
72949{
72950 if (pSound == NULL) {
72951 return;
72952 }
72953
72954 ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain);
72955}
72956
72957MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
72958{
72959 if (pInnerAngleInRadians != NULL) {
72960 *pInnerAngleInRadians = 0;
72961 }
72962
72963 if (pOuterAngleInRadians != NULL) {
72964 *pOuterAngleInRadians = 0;
72965 }
72966
72967 if (pOuterGain != NULL) {
72968 *pOuterGain = 0;
72969 }
72970
72971 ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
72972}
72973
72974MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor)
72975{
72976 if (pSound == NULL) {
72977 return;
72978 }
72979
72981}
72982
72983MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound)
72984{
72985 if (pSound == NULL) {
72986 return 0;
72987 }
72988
72990}
72991
72992MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor)
72993{
72994 if (pSound == NULL) {
72995 return;
72996 }
72997
72998 ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor);
72999}
73000
73002{
73003 if (pSound == NULL) {
73004 return 1;
73005 }
73006
73008}
73009
73010
73011MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)
73012{
73013 if (pSound == NULL) {
73014 return;
73015 }
73016
73017 ma_fader_set_fade(&pSound->engineNode.fader, volumeBeg, volumeEnd, fadeLengthInFrames);
73018}
73019
73020MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)
73021{
73022 if (pSound == NULL) {
73023 return;
73024 }
73025
73026 ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000);
73027}
73028
73030{
73031 if (pSound == NULL) {
73032 return MA_INVALID_ARGS;
73033 }
73034
73036}
73037
73038MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames)
73039{
73040 if (pSound == NULL) {
73041 return;
73042 }
73043
73044 ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames);
73045}
73046
73047MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)
73048{
73049 if (pSound == NULL) {
73050 return;
73051 }
73052
73053 ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000);
73054}
73055
73056MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames)
73057{
73058 if (pSound == NULL) {
73059 return;
73060 }
73061
73062 ma_node_set_state_time(pSound, ma_node_state_stopped, absoluteGlobalTimeInFrames);
73063}
73064
73065MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)
73066{
73067 if (pSound == NULL) {
73068 return;
73069 }
73070
73071 ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000);
73072}
73073
73075{
73076 if (pSound == NULL) {
73077 return MA_FALSE;
73078 }
73079
73081}
73082
73084{
73085 if (pSound == NULL) {
73086 return 0;
73087 }
73088
73089 return ma_node_get_time(pSound);
73090}
73091
73092MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping)
73093{
73094 if (pSound == NULL) {
73095 return;
73096 }
73097
73098 /* Looping is only a valid concept if the sound is backed by a data source. */
73099 if (pSound->pDataSource == NULL) {
73100 return;
73101 }
73102
73103 /* The looping state needs to be applied to the data source in order for any looping to actually happen. */
73104 ma_data_source_set_looping(pSound->pDataSource, isLooping);
73105}
73106
73108{
73109 if (pSound == NULL) {
73110 return MA_FALSE;
73111 }
73112
73113 /* There is no notion of looping for sounds that are not backed by a data source. */
73114 if (pSound->pDataSource == NULL) {
73115 return MA_FALSE;
73116 }
73117
73118 return ma_data_source_is_looping(pSound->pDataSource);
73119}
73120
73122{
73123 if (pSound == NULL) {
73124 return MA_FALSE;
73125 }
73126
73127 /* There is no notion of an end of a sound if it's not backed by a data source. */
73128 if (pSound->pDataSource == NULL) {
73129 return MA_FALSE;
73130 }
73131
73132 return c89atomic_load_32(&pSound->atEnd);
73133}
73134
73136{
73137 if (pSound == NULL) {
73138 return MA_INVALID_ARGS;
73139 }
73140
73141 /* Seeking is only valid for sounds that are backed by a data source. */
73142 if (pSound->pDataSource == NULL) {
73143 return MA_INVALID_OPERATION;
73144 }
73145
73146 /*
73147 Resource manager data sources are thread safe which means we can just seek immediately. However, we cannot guarantee that other data sources are
73148 thread safe as well so in that case we'll need to get the mixing thread to seek for us to ensure we don't try seeking at the same time as reading.
73149 */
73150#ifndef MA_NO_RESOURCE_MANAGER
73151 if (pSound->pDataSource == pSound->pResourceManagerDataSource) {
73153 if (result != MA_SUCCESS) {
73154 return result;
73155 }
73156
73157 /* Time dependant effects need to have their timers updated. */
73158 return ma_node_set_time(&pSound->engineNode, frameIndex);
73159 }
73160#endif
73161
73162 /* Getting here means the data source is not a resource manager data source so we'll need to get the mixing thread to do the seeking for us. */
73163 pSound->seekTarget = frameIndex;
73164
73165 return MA_SUCCESS;
73166}
73167
73168MA_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)
73169{
73170 if (pSound == NULL) {
73171 return MA_INVALID_ARGS;
73172 }
73173
73174 /* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */
73175 if (pSound->pDataSource == NULL) {
73176 ma_uint32 channels;
73177
73178 if (pFormat != NULL) {
73179 *pFormat = ma_format_f32;
73180 }
73181
73182 channels = ma_node_get_input_channels(&pSound->engineNode, 0);
73183 if (pChannels != NULL) {
73184 *pChannels = channels;
73185 }
73186
73187 if (pSampleRate != NULL) {
73188 *pSampleRate = pSound->engineNode.resampler.config.sampleRateIn;
73189 }
73190
73191 if (pChannelMap != NULL) {
73192 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels);
73193 }
73194
73195 return MA_SUCCESS;
73196 } else {
73197 return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
73198 }
73199}
73200
73202{
73203 if (pSound == NULL) {
73204 return MA_INVALID_ARGS;
73205 }
73206
73207 /* The notion of a cursor is only valid for sounds that are backed by a data source. */
73208 if (pSound->pDataSource == NULL) {
73209 return MA_INVALID_OPERATION;
73210 }
73211
73213}
73214
73216{
73217 if (pSound == NULL) {
73218 return MA_INVALID_ARGS;
73219 }
73220
73221 /* The notion of a sound length is only valid for sounds that are backed by a data source. */
73222 if (pSound->pDataSource == NULL) {
73223 return MA_INVALID_OPERATION;
73224 }
73225
73227}
73228
73230{
73231 if (pSound == NULL) {
73232 return MA_INVALID_ARGS;
73233 }
73234
73235 /* The notion of a cursor is only valid for sounds that are backed by a data source. */
73236 if (pSound->pDataSource == NULL) {
73237 return MA_INVALID_OPERATION;
73238 }
73239
73240 return ma_data_source_get_cursor_in_seconds(pSound->pDataSource, pCursor);
73241}
73242
73244{
73245 if (pSound == NULL) {
73246 return MA_INVALID_ARGS;
73247 }
73248
73249 /* The notion of a sound length is only valid for sounds that are backed by a data source. */
73250 if (pSound->pDataSource == NULL) {
73251 return MA_INVALID_OPERATION;
73252 }
73253
73254 return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength);
73255}
73256
73257
73259{
73261 config.flags = flags;
73262 config.pInitialAttachment = pParentGroup;
73263 return ma_sound_group_init_ex(pEngine, &config, pGroup);
73264}
73265
73267{
73268 ma_sound_config soundConfig;
73269
73270 if (pGroup == NULL) {
73271 return MA_INVALID_ARGS;
73272 }
73273
73274 MA_ZERO_OBJECT(pGroup);
73275
73276 if (pConfig == NULL) {
73277 return MA_INVALID_ARGS;
73278 }
73279
73280 /* A sound group is just a sound without a data source. */
73281 soundConfig = *pConfig;
73282 soundConfig.pFilePath = NULL;
73283 soundConfig.pFilePathW = NULL;
73284 soundConfig.pDataSource = NULL;
73285
73286 /*
73287 Groups need to have spatialization disabled by default because I think it'll be pretty rare
73288 that programs will want to spatialize groups (but not unheard of). Certainly it feels like
73289 disabling this by default feels like the right option. Spatialization can be enabled with a
73290 call to ma_sound_group_set_spatialization_enabled().
73291 */
73293
73294 return ma_sound_init_ex(pEngine, &soundConfig, pGroup);
73295}
73296
73298{
73299 ma_sound_uninit(pGroup);
73300}
73301
73303{
73304 return ma_sound_get_engine(pGroup);
73305}
73306
73308{
73309 return ma_sound_start(pGroup);
73310}
73311
73313{
73314 return ma_sound_stop(pGroup);
73315}
73316
73317MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume)
73318{
73319 ma_sound_set_volume(pGroup, volume);
73320}
73321
73323{
73324 return ma_sound_get_volume(pGroup);
73325}
73326
73327MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan)
73328{
73329 ma_sound_set_pan(pGroup, pan);
73330}
73331
73332MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup)
73333{
73334 return ma_sound_get_pan(pGroup);
73335}
73336
73338{
73339 ma_sound_set_pan_mode(pGroup, panMode);
73340}
73341
73343{
73344 return ma_sound_get_pan_mode(pGroup);
73345}
73346
73347MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch)
73348{
73349 ma_sound_set_pitch(pGroup, pitch);
73350}
73351
73353{
73354 return ma_sound_get_pitch(pGroup);
73355}
73356
73358{
73359 ma_sound_set_spatialization_enabled(pGroup, enabled);
73360}
73361
73363{
73365}
73366
73368{
73369 ma_sound_set_pinned_listener_index(pGroup, listenerIndex);
73370}
73371
73373{
73375}
73376
73378{
73379 return ma_sound_get_listener_index(pGroup);
73380}
73381
73383{
73385}
73386
73387MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z)
73388{
73389 ma_sound_set_position(pGroup, x, y, z);
73390}
73391
73393{
73394 return ma_sound_get_position(pGroup);
73395}
73396
73397MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z)
73398{
73399 ma_sound_set_direction(pGroup, x, y, z);
73400}
73401
73403{
73404 return ma_sound_get_direction(pGroup);
73405}
73406
73407MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z)
73408{
73409 ma_sound_set_velocity(pGroup, x, y, z);
73410}
73411
73413{
73414 return ma_sound_get_velocity(pGroup);
73415}
73416
73418{
73419 ma_sound_set_attenuation_model(pGroup, attenuationModel);
73420}
73421
73423{
73424 return ma_sound_get_attenuation_model(pGroup);
73425}
73426
73428{
73429 ma_sound_set_positioning(pGroup, positioning);
73430}
73431
73433{
73434 return ma_sound_get_positioning(pGroup);
73435}
73436
73437MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff)
73438{
73439 ma_sound_set_rolloff(pGroup, rolloff);
73440}
73441
73443{
73444 return ma_sound_get_rolloff(pGroup);
73445}
73446
73447MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain)
73448{
73449 ma_sound_set_min_gain(pGroup, minGain);
73450}
73451
73453{
73454 return ma_sound_get_min_gain(pGroup);
73455}
73456
73457MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain)
73458{
73459 ma_sound_set_max_gain(pGroup, maxGain);
73460}
73461
73463{
73464 return ma_sound_get_max_gain(pGroup);
73465}
73466
73467MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance)
73468{
73469 ma_sound_set_min_distance(pGroup, minDistance);
73470}
73471
73473{
73474 return ma_sound_get_min_distance(pGroup);
73475}
73476
73477MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance)
73478{
73479 ma_sound_set_max_distance(pGroup, maxDistance);
73480}
73481
73483{
73484 return ma_sound_get_max_distance(pGroup);
73485}
73486
73487MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
73488{
73489 ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain);
73490}
73491
73492MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
73493{
73494 ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
73495}
73496
73497MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor)
73498{
73499 ma_sound_set_doppler_factor(pGroup, dopplerFactor);
73500}
73501
73503{
73504 return ma_sound_get_doppler_factor(pGroup);
73505}
73506
73507MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor)
73508{
73509 ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor);
73510}
73511
73513{
73515}
73516
73517MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)
73518{
73519 ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames);
73520}
73521
73522MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)
73523{
73524 ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds);
73525}
73526
73528{
73529 return ma_sound_get_current_fade_volume(pGroup);
73530}
73531
73532MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames)
73533{
73534 ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames);
73535}
73536
73537MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)
73538{
73539 ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds);
73540}
73541
73542MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames)
73543{
73544 ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames);
73545}
73546
73547MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)
73548{
73549 ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds);
73550}
73551
73553{
73554 return ma_sound_is_playing(pGroup);
73555}
73556
73558{
73559 return ma_sound_get_time_in_pcm_frames(pGroup);
73560}
73561#endif /* MA_NO_ENGINE */
73562
73563
73564
73565
73575#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
73576#if !defined(DR_WAV_IMPLEMENTATION) && !defined(DRWAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
73577/* dr_wav_c begin */
73578#ifndef dr_wav_c
73579#define dr_wav_c
73580#include <stdlib.h>
73581#include <string.h>
73582#include <limits.h>
73583#ifndef DR_WAV_NO_STDIO
73584#include <stdio.h>
73585#include <wchar.h>
73586#endif
73587#ifndef DRWAV_ASSERT
73588#include <assert.h>
73589#define DRWAV_ASSERT(expression) assert(expression)
73590#endif
73591#ifndef DRWAV_MALLOC
73592#define DRWAV_MALLOC(sz) malloc((sz))
73593#endif
73594#ifndef DRWAV_REALLOC
73595#define DRWAV_REALLOC(p, sz) realloc((p), (sz))
73596#endif
73597#ifndef DRWAV_FREE
73598#define DRWAV_FREE(p) free((p))
73599#endif
73600#ifndef DRWAV_COPY_MEMORY
73601#define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
73602#endif
73603#ifndef DRWAV_ZERO_MEMORY
73604#define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
73605#endif
73606#ifndef DRWAV_ZERO_OBJECT
73607#define DRWAV_ZERO_OBJECT(p) DRWAV_ZERO_MEMORY((p), sizeof(*p))
73608#endif
73609#define drwav_countof(x) (sizeof(x) / sizeof(x[0]))
73610#define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
73611#define drwav_min(a, b) (((a) < (b)) ? (a) : (b))
73612#define drwav_max(a, b) (((a) > (b)) ? (a) : (b))
73613#define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x))))
73614#define drwav_offset_ptr(p, offset) (((drwav_uint8*)(p)) + (offset))
73615#define DRWAV_MAX_SIMD_VECTOR_SIZE 64
73616#if defined(__x86_64__) || defined(_M_X64)
73617 #define DRWAV_X64
73618#elif defined(__i386) || defined(_M_IX86)
73619 #define DRWAV_X86
73620#elif defined(__arm__) || defined(_M_ARM)
73621 #define DRWAV_ARM
73622#endif
73623#ifdef _MSC_VER
73624 #define DRWAV_INLINE __forceinline
73625#elif defined(__GNUC__)
73626 #if defined(__STRICT_ANSI__)
73627 #define DRWAV_INLINE __inline__ __attribute__((always_inline))
73628 #else
73629 #define DRWAV_INLINE inline __attribute__((always_inline))
73630 #endif
73631#elif defined(__WATCOMC__)
73632 #define DRWAV_INLINE __inline
73633#else
73634 #define DRWAV_INLINE
73635#endif
73636#if defined(SIZE_MAX)
73637 #define DRWAV_SIZE_MAX SIZE_MAX
73638#else
73639 #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
73640 #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF)
73641 #else
73642 #define DRWAV_SIZE_MAX 0xFFFFFFFF
73643 #endif
73644#endif
73645#if defined(_MSC_VER) && _MSC_VER >= 1400
73646 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
73647 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
73648 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
73649#elif defined(__clang__)
73650 #if defined(__has_builtin)
73651 #if __has_builtin(__builtin_bswap16)
73652 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
73653 #endif
73654 #if __has_builtin(__builtin_bswap32)
73655 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
73656 #endif
73657 #if __has_builtin(__builtin_bswap64)
73658 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
73659 #endif
73660 #endif
73661#elif defined(__GNUC__)
73662 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
73663 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
73664 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
73665 #endif
73666 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
73667 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
73668 #endif
73669#endif
73670DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision)
73671{
73672 if (pMajor) {
73673 *pMajor = DRWAV_VERSION_MAJOR;
73674 }
73675 if (pMinor) {
73676 *pMinor = DRWAV_VERSION_MINOR;
73677 }
73678 if (pRevision) {
73679 *pRevision = DRWAV_VERSION_REVISION;
73680 }
73681}
73682DRWAV_API const char* drwav_version_string(void)
73683{
73684 return DRWAV_VERSION_STRING;
73685}
73686#ifndef DRWAV_MAX_SAMPLE_RATE
73687#define DRWAV_MAX_SAMPLE_RATE 384000
73688#endif
73689#ifndef DRWAV_MAX_CHANNELS
73690#define DRWAV_MAX_CHANNELS 256
73691#endif
73692#ifndef DRWAV_MAX_BITS_PER_SAMPLE
73693#define DRWAV_MAX_BITS_PER_SAMPLE 64
73694#endif
73695static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00};
73696static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
73697static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
73698static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
73699static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
73700static DRWAV_INLINE int drwav__is_little_endian(void)
73701{
73702#if defined(DRWAV_X86) || defined(DRWAV_X64)
73703 return DRWAV_TRUE;
73704#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
73705 return DRWAV_TRUE;
73706#else
73707 int n = 1;
73708 return (*(char*)&n) == 1;
73709#endif
73710}
73711static DRWAV_INLINE void drwav_bytes_to_guid(const drwav_uint8* data, drwav_uint8* guid)
73712{
73713 int i;
73714 for (i = 0; i < 16; ++i) {
73715 guid[i] = data[i];
73716 }
73717}
73718static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n)
73719{
73720#ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC
73721 #if defined(_MSC_VER)
73722 return _byteswap_ushort(n);
73723 #elif defined(__GNUC__) || defined(__clang__)
73724 return __builtin_bswap16(n);
73725 #else
73726 #error "This compiler does not support the byte swap intrinsic."
73727 #endif
73728#else
73729 return ((n & 0xFF00) >> 8) |
73730 ((n & 0x00FF) << 8);
73731#endif
73732}
73733static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n)
73734{
73735#ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC
73736 #if defined(_MSC_VER)
73737 return _byteswap_ulong(n);
73738 #elif defined(__GNUC__) || defined(__clang__)
73739 #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT)
73740 drwav_uint32 r;
73741 __asm__ __volatile__ (
73742 #if defined(DRWAV_64BIT)
73743 "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n)
73744 #else
73745 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
73746 #endif
73747 );
73748 return r;
73749 #else
73750 return __builtin_bswap32(n);
73751 #endif
73752 #else
73753 #error "This compiler does not support the byte swap intrinsic."
73754 #endif
73755#else
73756 return ((n & 0xFF000000) >> 24) |
73757 ((n & 0x00FF0000) >> 8) |
73758 ((n & 0x0000FF00) << 8) |
73759 ((n & 0x000000FF) << 24);
73760#endif
73761}
73762static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n)
73763{
73764#ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC
73765 #if defined(_MSC_VER)
73766 return _byteswap_uint64(n);
73767 #elif defined(__GNUC__) || defined(__clang__)
73768 return __builtin_bswap64(n);
73769 #else
73770 #error "This compiler does not support the byte swap intrinsic."
73771 #endif
73772#else
73773 return ((n & ((drwav_uint64)0xFF000000 << 32)) >> 56) |
73774 ((n & ((drwav_uint64)0x00FF0000 << 32)) >> 40) |
73775 ((n & ((drwav_uint64)0x0000FF00 << 32)) >> 24) |
73776 ((n & ((drwav_uint64)0x000000FF << 32)) >> 8) |
73777 ((n & ((drwav_uint64)0xFF000000 )) << 8) |
73778 ((n & ((drwav_uint64)0x00FF0000 )) << 24) |
73779 ((n & ((drwav_uint64)0x0000FF00 )) << 40) |
73780 ((n & ((drwav_uint64)0x000000FF )) << 56);
73781#endif
73782}
73783static DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n)
73784{
73785 return (drwav_int16)drwav__bswap16((drwav_uint16)n);
73786}
73787static DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount)
73788{
73789 drwav_uint64 iSample;
73790 for (iSample = 0; iSample < sampleCount; iSample += 1) {
73791 pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]);
73792 }
73793}
73794static DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p)
73795{
73796 drwav_uint8 t;
73797 t = p[0];
73798 p[0] = p[2];
73799 p[2] = t;
73800}
73801static DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount)
73802{
73803 drwav_uint64 iSample;
73804 for (iSample = 0; iSample < sampleCount; iSample += 1) {
73805 drwav_uint8* pSample = pSamples + (iSample*3);
73806 drwav__bswap_s24(pSample);
73807 }
73808}
73809static DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n)
73810{
73811 return (drwav_int32)drwav__bswap32((drwav_uint32)n);
73812}
73813static DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount)
73814{
73815 drwav_uint64 iSample;
73816 for (iSample = 0; iSample < sampleCount; iSample += 1) {
73817 pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]);
73818 }
73819}
73820static DRWAV_INLINE float drwav__bswap_f32(float n)
73821{
73822 union {
73823 drwav_uint32 i;
73824 float f;
73825 } x;
73826 x.f = n;
73827 x.i = drwav__bswap32(x.i);
73828 return x.f;
73829}
73830static DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount)
73831{
73832 drwav_uint64 iSample;
73833 for (iSample = 0; iSample < sampleCount; iSample += 1) {
73834 pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]);
73835 }
73836}
73837static DRWAV_INLINE double drwav__bswap_f64(double n)
73838{
73839 union {
73840 drwav_uint64 i;
73841 double f;
73842 } x;
73843 x.f = n;
73844 x.i = drwav__bswap64(x.i);
73845 return x.f;
73846}
73847static DRWAV_INLINE void drwav__bswap_samples_f64(double* pSamples, drwav_uint64 sampleCount)
73848{
73849 drwav_uint64 iSample;
73850 for (iSample = 0; iSample < sampleCount; iSample += 1) {
73851 pSamples[iSample] = drwav__bswap_f64(pSamples[iSample]);
73852 }
73853}
73854static DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample)
73855{
73856 switch (bytesPerSample)
73857 {
73858 case 1:
73859 {
73860 } break;
73861 case 2:
73862 {
73863 drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount);
73864 } break;
73865 case 3:
73866 {
73867 drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount);
73868 } break;
73869 case 4:
73870 {
73871 drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount);
73872 } break;
73873 default:
73874 {
73875 DRWAV_ASSERT(DRWAV_FALSE);
73876 } break;
73877 }
73878}
73879static DRWAV_INLINE void drwav__bswap_samples_ieee(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample)
73880{
73881 switch (bytesPerSample)
73882 {
73883 #if 0
73884 case 2:
73885 {
73886 drwav__bswap_samples_f16((drwav_float16*)pSamples, sampleCount);
73887 } break;
73888 #endif
73889 case 4:
73890 {
73891 drwav__bswap_samples_f32((float*)pSamples, sampleCount);
73892 } break;
73893 case 8:
73894 {
73895 drwav__bswap_samples_f64((double*)pSamples, sampleCount);
73896 } break;
73897 default:
73898 {
73899 DRWAV_ASSERT(DRWAV_FALSE);
73900 } break;
73901 }
73902}
73903static DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample, drwav_uint16 format)
73904{
73905 switch (format)
73906 {
73907 case DR_WAVE_FORMAT_PCM:
73908 {
73909 drwav__bswap_samples_pcm(pSamples, sampleCount, bytesPerSample);
73910 } break;
73912 {
73913 drwav__bswap_samples_ieee(pSamples, sampleCount, bytesPerSample);
73914 } break;
73917 {
73918 drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount);
73919 } break;
73922 default:
73923 {
73924 DRWAV_ASSERT(DRWAV_FALSE);
73925 } break;
73926 }
73927}
73928DRWAV_PRIVATE void* drwav__malloc_default(size_t sz, void* pUserData)
73929{
73930 (void)pUserData;
73931 return DRWAV_MALLOC(sz);
73932}
73933DRWAV_PRIVATE void* drwav__realloc_default(void* p, size_t sz, void* pUserData)
73934{
73935 (void)pUserData;
73936 return DRWAV_REALLOC(p, sz);
73937}
73938DRWAV_PRIVATE void drwav__free_default(void* p, void* pUserData)
73939{
73940 (void)pUserData;
73941 DRWAV_FREE(p);
73942}
73943DRWAV_PRIVATE void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocation_callbacks* pAllocationCallbacks)
73944{
73945 if (pAllocationCallbacks == NULL) {
73946 return NULL;
73947 }
73948 if (pAllocationCallbacks->onMalloc != NULL) {
73949 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
73950 }
73951 if (pAllocationCallbacks->onRealloc != NULL) {
73952 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
73953 }
73954 return NULL;
73955}
73956DRWAV_PRIVATE void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_allocation_callbacks* pAllocationCallbacks)
73957{
73958 if (pAllocationCallbacks == NULL) {
73959 return NULL;
73960 }
73961 if (pAllocationCallbacks->onRealloc != NULL) {
73962 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
73963 }
73964 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
73965 void* p2;
73966 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
73967 if (p2 == NULL) {
73968 return NULL;
73969 }
73970 if (p != NULL) {
73971 DRWAV_COPY_MEMORY(p2, p, szOld);
73972 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
73973 }
73974 return p2;
73975 }
73976 return NULL;
73977}
73978DRWAV_PRIVATE void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks)
73979{
73980 if (p == NULL || pAllocationCallbacks == NULL) {
73981 return;
73982 }
73983 if (pAllocationCallbacks->onFree != NULL) {
73984 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
73985 }
73986}
73987DRWAV_PRIVATE drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks)
73988{
73989 if (pAllocationCallbacks != NULL) {
73990 return *pAllocationCallbacks;
73991 } else {
73992 drwav_allocation_callbacks allocationCallbacks;
73993 allocationCallbacks.pUserData = NULL;
73994 allocationCallbacks.onMalloc = drwav__malloc_default;
73995 allocationCallbacks.onRealloc = drwav__realloc_default;
73996 allocationCallbacks.onFree = drwav__free_default;
73997 return allocationCallbacks;
73998 }
73999}
74000static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag)
74001{
74002 return
74003 formatTag == DR_WAVE_FORMAT_ADPCM ||
74004 formatTag == DR_WAVE_FORMAT_DVI_ADPCM;
74005}
74006DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize)
74007{
74008 return (unsigned int)(chunkSize % 2);
74009}
74010DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize)
74011{
74012 return (unsigned int)(chunkSize % 8);
74013}
74014DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
74015DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
74016DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
74017DRWAV_PRIVATE drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut)
74018{
74019 if (container == drwav_container_riff || container == drwav_container_rf64) {
74020 drwav_uint8 sizeInBytes[4];
74021 if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) {
74022 return DRWAV_AT_END;
74023 }
74024 if (onRead(pUserData, sizeInBytes, 4) != 4) {
74025 return DRWAV_INVALID_FILE;
74026 }
74027 pHeaderOut->sizeInBytes = drwav_bytes_to_u32(sizeInBytes);
74028 pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes);
74029 *pRunningBytesReadOut += 8;
74030 } else {
74031 drwav_uint8 sizeInBytes[8];
74032 if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) {
74033 return DRWAV_AT_END;
74034 }
74035 if (onRead(pUserData, sizeInBytes, 8) != 8) {
74036 return DRWAV_INVALID_FILE;
74037 }
74038 pHeaderOut->sizeInBytes = drwav_bytes_to_u64(sizeInBytes) - 24;
74039 pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes);
74040 *pRunningBytesReadOut += 24;
74041 }
74042 return DRWAV_SUCCESS;
74043}
74044DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
74045{
74046 drwav_uint64 bytesRemainingToSeek = offset;
74047 while (bytesRemainingToSeek > 0) {
74048 if (bytesRemainingToSeek > 0x7FFFFFFF) {
74049 if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
74050 return DRWAV_FALSE;
74051 }
74052 bytesRemainingToSeek -= 0x7FFFFFFF;
74053 } else {
74054 if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) {
74055 return DRWAV_FALSE;
74056 }
74057 bytesRemainingToSeek = 0;
74058 }
74059 }
74060 return DRWAV_TRUE;
74061}
74062DRWAV_PRIVATE drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
74063{
74064 if (offset <= 0x7FFFFFFF) {
74065 return onSeek(pUserData, (int)offset, drwav_seek_origin_start);
74066 }
74067 if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) {
74068 return DRWAV_FALSE;
74069 }
74070 offset -= 0x7FFFFFFF;
74071 for (;;) {
74072 if (offset <= 0x7FFFFFFF) {
74073 return onSeek(pUserData, (int)offset, drwav_seek_origin_current);
74074 }
74075 if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
74076 return DRWAV_FALSE;
74077 }
74078 offset -= 0x7FFFFFFF;
74079 }
74080}
74081DRWAV_PRIVATE drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut)
74082{
74083 drwav_chunk_header header;
74084 drwav_uint8 fmt[16];
74085 if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) {
74086 return DRWAV_FALSE;
74087 }
74088 while (((container == drwav_container_riff || container == drwav_container_rf64) && !drwav_fourcc_equal(header.id.fourcc, "fmt ")) || (container == drwav_container_w64 && !drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT))) {
74089 if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) {
74090 return DRWAV_FALSE;
74091 }
74092 *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize;
74093 if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) {
74094 return DRWAV_FALSE;
74095 }
74096 }
74097 if (container == drwav_container_riff || container == drwav_container_rf64) {
74098 if (!drwav_fourcc_equal(header.id.fourcc, "fmt ")) {
74099 return DRWAV_FALSE;
74100 }
74101 } else {
74102 if (!drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT)) {
74103 return DRWAV_FALSE;
74104 }
74105 }
74106 if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) {
74107 return DRWAV_FALSE;
74108 }
74109 *pRunningBytesReadOut += sizeof(fmt);
74110 fmtOut->formatTag = drwav_bytes_to_u16(fmt + 0);
74111 fmtOut->channels = drwav_bytes_to_u16(fmt + 2);
74112 fmtOut->sampleRate = drwav_bytes_to_u32(fmt + 4);
74113 fmtOut->avgBytesPerSec = drwav_bytes_to_u32(fmt + 8);
74114 fmtOut->blockAlign = drwav_bytes_to_u16(fmt + 12);
74115 fmtOut->bitsPerSample = drwav_bytes_to_u16(fmt + 14);
74116 fmtOut->extendedSize = 0;
74117 fmtOut->validBitsPerSample = 0;
74118 fmtOut->channelMask = 0;
74119 memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat));
74120 if (header.sizeInBytes > 16) {
74121 drwav_uint8 fmt_cbSize[2];
74122 int bytesReadSoFar = 0;
74123 if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) {
74124 return DRWAV_FALSE;
74125 }
74126 *pRunningBytesReadOut += sizeof(fmt_cbSize);
74127 bytesReadSoFar = 18;
74128 fmtOut->extendedSize = drwav_bytes_to_u16(fmt_cbSize);
74129 if (fmtOut->extendedSize > 0) {
74130 if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
74131 if (fmtOut->extendedSize != 22) {
74132 return DRWAV_FALSE;
74133 }
74134 }
74135 if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
74136 drwav_uint8 fmtext[22];
74137 if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) {
74138 return DRWAV_FALSE;
74139 }
74140 fmtOut->validBitsPerSample = drwav_bytes_to_u16(fmtext + 0);
74141 fmtOut->channelMask = drwav_bytes_to_u32(fmtext + 2);
74142 drwav_bytes_to_guid(fmtext + 6, fmtOut->subFormat);
74143 } else {
74144 if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) {
74145 return DRWAV_FALSE;
74146 }
74147 }
74148 *pRunningBytesReadOut += fmtOut->extendedSize;
74149 bytesReadSoFar += fmtOut->extendedSize;
74150 }
74151 if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) {
74152 return DRWAV_FALSE;
74153 }
74154 *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar);
74155 }
74156 if (header.paddingSize > 0) {
74157 if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) {
74158 return DRWAV_FALSE;
74159 }
74160 *pRunningBytesReadOut += header.paddingSize;
74161 }
74162 return DRWAV_TRUE;
74163}
74164DRWAV_PRIVATE size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor)
74165{
74166 size_t bytesRead;
74167 DRWAV_ASSERT(onRead != NULL);
74168 DRWAV_ASSERT(pCursor != NULL);
74169 bytesRead = onRead(pUserData, pBufferOut, bytesToRead);
74170 *pCursor += bytesRead;
74171 return bytesRead;
74172}
74173#if 0
74174DRWAV_PRIVATE drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor)
74175{
74176 DRWAV_ASSERT(onSeek != NULL);
74177 DRWAV_ASSERT(pCursor != NULL);
74178 if (!onSeek(pUserData, offset, origin)) {
74179 return DRWAV_FALSE;
74180 }
74181 if (origin == drwav_seek_origin_start) {
74182 *pCursor = offset;
74183 } else {
74184 *pCursor += offset;
74185 }
74186 return DRWAV_TRUE;
74187}
74188#endif
74189#define DRWAV_SMPL_BYTES 36
74190#define DRWAV_SMPL_LOOP_BYTES 24
74191#define DRWAV_INST_BYTES 7
74192#define DRWAV_ACID_BYTES 24
74193#define DRWAV_CUE_BYTES 4
74194#define DRWAV_BEXT_BYTES 602
74195#define DRWAV_BEXT_DESCRIPTION_BYTES 256
74196#define DRWAV_BEXT_ORIGINATOR_NAME_BYTES 32
74197#define DRWAV_BEXT_ORIGINATOR_REF_BYTES 32
74198#define DRWAV_BEXT_RESERVED_BYTES 180
74199#define DRWAV_BEXT_UMID_BYTES 64
74200#define DRWAV_CUE_POINT_BYTES 24
74201#define DRWAV_LIST_LABEL_OR_NOTE_BYTES 4
74202#define DRWAV_LIST_LABELLED_TEXT_BYTES 20
74203#define DRWAV_METADATA_ALIGNMENT 8
74204typedef enum
74205{
74206 drwav__metadata_parser_stage_count,
74207 drwav__metadata_parser_stage_read
74208} drwav__metadata_parser_stage;
74209typedef struct
74210{
74211 drwav_read_proc onRead;
74212 drwav_seek_proc onSeek;
74213 void *pReadSeekUserData;
74214 drwav__metadata_parser_stage stage;
74215 drwav_metadata *pMetadata;
74216 drwav_uint32 metadataCount;
74217 drwav_uint8 *pData;
74218 drwav_uint8 *pDataCursor;
74219 drwav_uint64 metadataCursor;
74220 drwav_uint64 extraCapacity;
74221} drwav__metadata_parser;
74222DRWAV_PRIVATE size_t drwav__metadata_memory_capacity(drwav__metadata_parser* pParser)
74223{
74224 drwav_uint64 cap = sizeof(drwav_metadata) * (drwav_uint64)pParser->metadataCount + pParser->extraCapacity;
74225 if (cap > DRWAV_SIZE_MAX) {
74226 return 0;
74227 }
74228 return (size_t)cap;
74229}
74230DRWAV_PRIVATE drwav_uint8* drwav__metadata_get_memory(drwav__metadata_parser* pParser, size_t size, size_t align)
74231{
74232 drwav_uint8* pResult;
74233 if (align) {
74234 drwav_uintptr modulo = (drwav_uintptr)pParser->pDataCursor % align;
74235 if (modulo != 0) {
74236 pParser->pDataCursor += align - modulo;
74237 }
74238 }
74239 pResult = pParser->pDataCursor;
74240 DRWAV_ASSERT((pResult + size) <= (pParser->pData + drwav__metadata_memory_capacity(pParser)));
74241 pParser->pDataCursor += size;
74242 return pResult;
74243}
74244DRWAV_PRIVATE void drwav__metadata_request_extra_memory_for_stage_2(drwav__metadata_parser* pParser, size_t bytes, size_t align)
74245{
74246 size_t extra = bytes + (align ? (align - 1) : 0);
74247 pParser->extraCapacity += extra;
74248}
74249DRWAV_PRIVATE drwav_result drwav__metadata_alloc(drwav__metadata_parser* pParser, drwav_allocation_callbacks* pAllocationCallbacks)
74250{
74251 if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) {
74252 free(pParser->pData);
74253 pParser->pData = (drwav_uint8*)pAllocationCallbacks->onMalloc(drwav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData);
74254 pParser->pDataCursor = pParser->pData;
74255 if (pParser->pData == NULL) {
74256 return DRWAV_OUT_OF_MEMORY;
74257 }
74258 pParser->pMetadata = (drwav_metadata*)drwav__metadata_get_memory(pParser, sizeof(drwav_metadata) * pParser->metadataCount, 1);
74259 pParser->metadataCursor = 0;
74260 }
74261 return DRWAV_SUCCESS;
74262}
74263DRWAV_PRIVATE size_t drwav__metadata_parser_read(drwav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor)
74264{
74265 if (pCursor != NULL) {
74266 return drwav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor);
74267 } else {
74268 return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead);
74269 }
74270}
74271DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata)
74272{
74273 drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES];
74274 drwav_uint64 totalBytesRead = 0;
74275 size_t bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead);
74276 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
74277 DRWAV_ASSERT(pChunkHeader != NULL);
74278 if (bytesJustRead == sizeof(smplHeaderData)) {
74279 drwav_uint32 iSampleLoop;
74280 pMetadata->type = drwav_metadata_type_smpl;
74281 pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0);
74282 pMetadata->data.smpl.productId = drwav_bytes_to_u32(smplHeaderData + 4);
74283 pMetadata->data.smpl.samplePeriodNanoseconds = drwav_bytes_to_u32(smplHeaderData + 8);
74284 pMetadata->data.smpl.midiUnityNote = drwav_bytes_to_u32(smplHeaderData + 12);
74285 pMetadata->data.smpl.midiPitchFraction = drwav_bytes_to_u32(smplHeaderData + 16);
74286 pMetadata->data.smpl.smpteFormat = drwav_bytes_to_u32(smplHeaderData + 20);
74287 pMetadata->data.smpl.smpteOffset = drwav_bytes_to_u32(smplHeaderData + 24);
74288 pMetadata->data.smpl.sampleLoopCount = drwav_bytes_to_u32(smplHeaderData + 28);
74289 pMetadata->data.smpl.samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(smplHeaderData + 32);
74290 if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES) {
74291 pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT);
74292 for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) {
74293 drwav_uint8 smplLoopData[DRWAV_SMPL_LOOP_BYTES];
74294 bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead);
74295 if (bytesJustRead == sizeof(smplLoopData)) {
74296 pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0);
74297 pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4);
74298 pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 8);
74299 pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 12);
74300 pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16);
74301 pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20);
74302 } else {
74303 break;
74304 }
74305 }
74306 if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {
74307 pMetadata->data.smpl.pSamplerSpecificData = drwav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1);
74308 DRWAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL);
74309 drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead);
74310 }
74311 }
74312 }
74313 return totalBytesRead;
74314}
74315DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata)
74316{
74317 drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES];
74318 drwav_uint64 totalBytesRead = 0;
74319 size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead);
74320 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
74321 if (bytesJustRead == sizeof(cueHeaderSectionData)) {
74322 pMetadata->type = drwav_metadata_type_cue;
74323 pMetadata->data.cue.cuePointCount = drwav_bytes_to_u32(cueHeaderSectionData);
74324 if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES) {
74325 pMetadata->data.cue.pCuePoints = (drwav_cue_point*)drwav__metadata_get_memory(pParser, sizeof(drwav_cue_point) * pMetadata->data.cue.cuePointCount, DRWAV_METADATA_ALIGNMENT);
74326 DRWAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL);
74327 if (pMetadata->data.cue.cuePointCount > 0) {
74328 drwav_uint32 iCuePoint;
74329 for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {
74330 drwav_uint8 cuePointData[DRWAV_CUE_POINT_BYTES];
74331 bytesJustRead = drwav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead);
74332 if (bytesJustRead == sizeof(cuePointData)) {
74333 pMetadata->data.cue.pCuePoints[iCuePoint].id = drwav_bytes_to_u32(cuePointData + 0);
74334 pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = drwav_bytes_to_u32(cuePointData + 4);
74335 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8];
74336 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9];
74337 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10];
74338 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11];
74339 pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12);
74340 pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16);
74341 pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = drwav_bytes_to_u32(cuePointData + 20);
74342 } else {
74343 break;
74344 }
74345 }
74346 }
74347 }
74348 }
74349 return totalBytesRead;
74350}
74351DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata)
74352{
74353 drwav_uint8 instData[DRWAV_INST_BYTES];
74354 drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL);
74355 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
74356 if (bytesRead == sizeof(instData)) {
74357 pMetadata->type = drwav_metadata_type_inst;
74358 pMetadata->data.inst.midiUnityNote = (drwav_int8)instData[0];
74359 pMetadata->data.inst.fineTuneCents = (drwav_int8)instData[1];
74360 pMetadata->data.inst.gainDecibels = (drwav_int8)instData[2];
74361 pMetadata->data.inst.lowNote = (drwav_int8)instData[3];
74362 pMetadata->data.inst.highNote = (drwav_int8)instData[4];
74363 pMetadata->data.inst.lowVelocity = (drwav_int8)instData[5];
74364 pMetadata->data.inst.highVelocity = (drwav_int8)instData[6];
74365 }
74366 return bytesRead;
74367}
74368DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata)
74369{
74370 drwav_uint8 acidData[DRWAV_ACID_BYTES];
74371 drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL);
74372 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
74373 if (bytesRead == sizeof(acidData)) {
74374 pMetadata->type = drwav_metadata_type_acid;
74375 pMetadata->data.acid.flags = drwav_bytes_to_u32(acidData + 0);
74376 pMetadata->data.acid.midiUnityNote = drwav_bytes_to_u16(acidData + 4);
74377 pMetadata->data.acid.reserved1 = drwav_bytes_to_u16(acidData + 6);
74378 pMetadata->data.acid.reserved2 = drwav_bytes_to_f32(acidData + 8);
74379 pMetadata->data.acid.numBeats = drwav_bytes_to_u32(acidData + 12);
74380 pMetadata->data.acid.meterDenominator = drwav_bytes_to_u16(acidData + 16);
74381 pMetadata->data.acid.meterNumerator = drwav_bytes_to_u16(acidData + 18);
74382 pMetadata->data.acid.tempo = drwav_bytes_to_f32(acidData + 20);
74383 }
74384 return bytesRead;
74385}
74386DRWAV_PRIVATE size_t drwav__strlen_clamped(const char* str, size_t maxToRead)
74387{
74388 size_t result = 0;
74389 while (*str++ && result < maxToRead) {
74390 result += 1;
74391 }
74392 return result;
74393}
74394DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser, const char* str, size_t maxToRead)
74395{
74396 size_t len = drwav__strlen_clamped(str, maxToRead);
74397 if (len) {
74398 char* result = (char*)drwav__metadata_get_memory(pParser, len + 1, 1);
74399 DRWAV_ASSERT(result != NULL);
74400 memcpy(result, str, len);
74401 result[len] = '\0';
74402 return result;
74403 } else {
74404 return NULL;
74405 }
74406}
74407typedef struct
74408{
74409 const void* pBuffer;
74410 size_t sizeInBytes;
74411 size_t cursor;
74412} drwav_buffer_reader;
74413DRWAV_PRIVATE drwav_result drwav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, drwav_buffer_reader* pReader)
74414{
74415 DRWAV_ASSERT(pBuffer != NULL);
74416 DRWAV_ASSERT(pReader != NULL);
74417 DRWAV_ZERO_OBJECT(pReader);
74418 pReader->pBuffer = pBuffer;
74419 pReader->sizeInBytes = sizeInBytes;
74420 pReader->cursor = 0;
74421 return DRWAV_SUCCESS;
74422}
74423DRWAV_PRIVATE const void* drwav_buffer_reader_ptr(const drwav_buffer_reader* pReader)
74424{
74425 DRWAV_ASSERT(pReader != NULL);
74426 return drwav_offset_ptr(pReader->pBuffer, pReader->cursor);
74427}
74428DRWAV_PRIVATE drwav_result drwav_buffer_reader_seek(drwav_buffer_reader* pReader, size_t bytesToSeek)
74429{
74430 DRWAV_ASSERT(pReader != NULL);
74431 if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) {
74432 return DRWAV_BAD_SEEK;
74433 }
74434 pReader->cursor += bytesToSeek;
74435 return DRWAV_SUCCESS;
74436}
74437DRWAV_PRIVATE drwav_result drwav_buffer_reader_read(drwav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead)
74438{
74439 drwav_result result = DRWAV_SUCCESS;
74440 size_t bytesRemaining;
74441 DRWAV_ASSERT(pReader != NULL);
74442 if (pBytesRead != NULL) {
74443 *pBytesRead = 0;
74444 }
74445 bytesRemaining = (pReader->sizeInBytes - pReader->cursor);
74446 if (bytesToRead > bytesRemaining) {
74447 bytesToRead = bytesRemaining;
74448 }
74449 if (pDst == NULL) {
74450 result = drwav_buffer_reader_seek(pReader, bytesToRead);
74451 } else {
74452 DRWAV_COPY_MEMORY(pDst, drwav_buffer_reader_ptr(pReader), bytesToRead);
74453 pReader->cursor += bytesToRead;
74454 }
74455 DRWAV_ASSERT(pReader->cursor <= pReader->sizeInBytes);
74456 if (result == DRWAV_SUCCESS) {
74457 if (pBytesRead != NULL) {
74458 *pBytesRead = bytesToRead;
74459 }
74460 }
74461 return DRWAV_SUCCESS;
74462}
74463DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u16(drwav_buffer_reader* pReader, drwav_uint16* pDst)
74464{
74465 drwav_result result;
74466 size_t bytesRead;
74467 drwav_uint8 data[2];
74468 DRWAV_ASSERT(pReader != NULL);
74469 DRWAV_ASSERT(pDst != NULL);
74470 *pDst = 0;
74471 result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead);
74472 if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) {
74473 return result;
74474 }
74475 *pDst = drwav_bytes_to_u16(data);
74476 return DRWAV_SUCCESS;
74477}
74478DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u32(drwav_buffer_reader* pReader, drwav_uint32* pDst)
74479{
74480 drwav_result result;
74481 size_t bytesRead;
74482 drwav_uint8 data[4];
74483 DRWAV_ASSERT(pReader != NULL);
74484 DRWAV_ASSERT(pDst != NULL);
74485 *pDst = 0;
74486 result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead);
74487 if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) {
74488 return result;
74489 }
74490 *pDst = drwav_bytes_to_u32(data);
74491 return DRWAV_SUCCESS;
74492}
74493DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize)
74494{
74495 drwav_uint8 bextData[DRWAV_BEXT_BYTES];
74496 size_t bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL);
74497 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
74498 if (bytesRead == sizeof(bextData)) {
74499 drwav_buffer_reader reader;
74500 drwav_uint32 timeReferenceLow;
74501 drwav_uint32 timeReferenceHigh;
74502 size_t extraBytes;
74503 pMetadata->type = drwav_metadata_type_bext;
74504 if (drwav_buffer_reader_init(bextData, bytesRead, &reader) == DRWAV_SUCCESS) {
74505 pMetadata->data.bext.pDescription = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_DESCRIPTION_BYTES);
74506 drwav_buffer_reader_seek(&reader, DRWAV_BEXT_DESCRIPTION_BYTES);
74507 pMetadata->data.bext.pOriginatorName = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_NAME_BYTES);
74508 drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_NAME_BYTES);
74509 pMetadata->data.bext.pOriginatorReference = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_REF_BYTES);
74510 drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_REF_BYTES);
74511 drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL);
74512 drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL);
74513 drwav_buffer_reader_read_u32(&reader, &timeReferenceLow);
74514 drwav_buffer_reader_read_u32(&reader, &timeReferenceHigh);
74515 pMetadata->data.bext.timeReference = ((drwav_uint64)timeReferenceHigh << 32) + timeReferenceLow;
74516 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version);
74517 pMetadata->data.bext.pUMID = drwav__metadata_get_memory(pParser, DRWAV_BEXT_UMID_BYTES, 1);
74518 drwav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES, NULL);
74519 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue);
74520 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange);
74521 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel);
74522 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness);
74523 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness);
74524 DRWAV_ASSERT((drwav_offset_ptr(drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_RESERVED_BYTES)) == (bextData + DRWAV_BEXT_BYTES));
74525 extraBytes = (size_t)(chunkSize - DRWAV_BEXT_BYTES);
74526 if (extraBytes > 0) {
74527 pMetadata->data.bext.pCodingHistory = (char*)drwav__metadata_get_memory(pParser, extraBytes + 1, 1);
74528 DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL);
74529 bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL);
74530 pMetadata->data.bext.codingHistorySize = (drwav_uint32)strlen(pMetadata->data.bext.pCodingHistory);
74531 } else {
74532 pMetadata->data.bext.pCodingHistory = NULL;
74533 pMetadata->data.bext.codingHistorySize = 0;
74534 }
74535 }
74536 }
74537 return bytesRead;
74538}
74539DRWAV_PRIVATE drwav_uint64 drwav__read_list_label_or_note_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize, drwav_metadata_type type)
74540{
74541 drwav_uint8 cueIDBuffer[DRWAV_LIST_LABEL_OR_NOTE_BYTES];
74542 drwav_uint64 totalBytesRead = 0;
74543 size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead);
74544 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
74545 if (bytesJustRead == sizeof(cueIDBuffer)) {
74546 drwav_uint32 sizeIncludingNullTerminator;
74547 pMetadata->type = type;
74548 pMetadata->data.labelOrNote.cuePointId = drwav_bytes_to_u32(cueIDBuffer);
74549 sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES;
74550 if (sizeIncludingNullTerminator > 0) {
74551 pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1;
74552 pMetadata->data.labelOrNote.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);
74553 DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);
74554 drwav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead);
74555 } else {
74556 pMetadata->data.labelOrNote.stringLength = 0;
74557 pMetadata->data.labelOrNote.pString = NULL;
74558 }
74559 }
74560 return totalBytesRead;
74561}
74562DRWAV_PRIVATE drwav_uint64 drwav__read_list_labelled_cue_region_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize)
74563{
74564 drwav_uint8 buffer[DRWAV_LIST_LABELLED_TEXT_BYTES];
74565 drwav_uint64 totalBytesRead = 0;
74566 size_t bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead);
74567 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
74568 if (bytesJustRead == sizeof(buffer)) {
74569 drwav_uint32 sizeIncludingNullTerminator;
74571 pMetadata->data.labelledCueRegion.cuePointId = drwav_bytes_to_u32(buffer + 0);
74572 pMetadata->data.labelledCueRegion.sampleLength = drwav_bytes_to_u32(buffer + 4);
74573 pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8];
74574 pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9];
74575 pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10];
74576 pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11];
74577 pMetadata->data.labelledCueRegion.country = drwav_bytes_to_u16(buffer + 12);
74578 pMetadata->data.labelledCueRegion.language = drwav_bytes_to_u16(buffer + 14);
74579 pMetadata->data.labelledCueRegion.dialect = drwav_bytes_to_u16(buffer + 16);
74580 pMetadata->data.labelledCueRegion.codePage = drwav_bytes_to_u16(buffer + 18);
74581 sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABELLED_TEXT_BYTES;
74582 if (sizeIncludingNullTerminator > 0) {
74583 pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1;
74584 pMetadata->data.labelledCueRegion.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);
74585 DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);
74586 drwav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead);
74587 } else {
74588 pMetadata->data.labelledCueRegion.stringLength = 0;
74589 pMetadata->data.labelledCueRegion.pString = NULL;
74590 }
74591 }
74592 return totalBytesRead;
74593}
74594DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_info_text_chunk(drwav__metadata_parser* pParser, drwav_uint64 chunkSize, drwav_metadata_type type)
74595{
74596 drwav_uint64 bytesRead = 0;
74597 drwav_uint32 stringSizeWithNullTerminator = (drwav_uint32)chunkSize;
74598 if (pParser->stage == drwav__metadata_parser_stage_count) {
74599 pParser->metadataCount += 1;
74600 drwav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1);
74601 } else {
74602 drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];
74603 pMetadata->type = type;
74604 if (stringSizeWithNullTerminator > 0) {
74605 pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1;
74606 pMetadata->data.infoText.pString = (char*)drwav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1);
74607 DRWAV_ASSERT(pMetadata->data.infoText.pString != NULL);
74608 bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL);
74609 if (bytesRead == chunkSize) {
74610 pParser->metadataCursor += 1;
74611 } else {
74612 }
74613 } else {
74614 pMetadata->data.infoText.stringLength = 0;
74615 pMetadata->data.infoText.pString = NULL;
74616 pParser->metadataCursor += 1;
74617 }
74618 }
74619 return bytesRead;
74620}
74621DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_unknown_chunk(drwav__metadata_parser* pParser, const drwav_uint8* pChunkId, drwav_uint64 chunkSize, drwav_metadata_location location)
74622{
74623 drwav_uint64 bytesRead = 0;
74624 if (location == drwav_metadata_location_invalid) {
74625 return 0;
74626 }
74627 if (drwav_fourcc_equal(pChunkId, "data") || drwav_fourcc_equal(pChunkId, "fmt") || drwav_fourcc_equal(pChunkId, "fact")) {
74628 return 0;
74629 }
74630 if (pParser->stage == drwav__metadata_parser_stage_count) {
74631 pParser->metadataCount += 1;
74632 drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1);
74633 } else {
74634 drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];
74635 pMetadata->type = drwav_metadata_type_unknown;
74636 pMetadata->data.unknown.chunkLocation = location;
74637 pMetadata->data.unknown.id[0] = pChunkId[0];
74638 pMetadata->data.unknown.id[1] = pChunkId[1];
74639 pMetadata->data.unknown.id[2] = pChunkId[2];
74640 pMetadata->data.unknown.id[3] = pChunkId[3];
74641 pMetadata->data.unknown.dataSizeInBytes = (drwav_uint32)chunkSize;
74642 pMetadata->data.unknown.pData = (drwav_uint8 *)drwav__metadata_get_memory(pParser, (size_t)chunkSize, 1);
74643 DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL);
74644 bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL);
74645 if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) {
74646 pParser->metadataCursor += 1;
74647 } else {
74648 }
74649 }
74650 return bytesRead;
74651}
74652DRWAV_PRIVATE drwav_bool32 drwav__chunk_matches(drwav_metadata_type allowedMetadataTypes, const drwav_uint8* pChunkID, drwav_metadata_type type, const char* pID)
74653{
74654 return (allowedMetadataTypes & type) && drwav_fourcc_equal(pChunkID, pID);
74655}
74656DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata_type allowedMetadataTypes)
74657{
74658 const drwav_uint8 *pChunkID = pChunkHeader->id.fourcc;
74659 drwav_uint64 bytesRead = 0;
74660 if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_smpl, "smpl")) {
74661 if (pChunkHeader->sizeInBytes >= DRWAV_SMPL_BYTES) {
74662 if (pParser->stage == drwav__metadata_parser_stage_count) {
74663 drwav_uint8 buffer[4];
74664 size_t bytesJustRead;
74665 if (!pParser->onSeek(pParser->pReadSeekUserData, 28, drwav_seek_origin_current)) {
74666 return bytesRead;
74667 }
74668 bytesRead += 28;
74669 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);
74670 if (bytesJustRead == sizeof(buffer)) {
74671 drwav_uint32 loopCount = drwav_bytes_to_u32(buffer);
74672 drwav_uint64 calculatedLoopCount;
74673 calculatedLoopCount = (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES;
74674 if (calculatedLoopCount == loopCount) {
74675 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);
74676 if (bytesJustRead == sizeof(buffer)) {
74677 drwav_uint32 samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(buffer);
74678 pParser->metadataCount += 1;
74679 drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_smpl_loop) * loopCount, DRWAV_METADATA_ALIGNMENT);
74680 drwav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1);
74681 }
74682 } else {
74683 }
74684 }
74685 } else {
74686 bytesRead = drwav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);
74687 if (bytesRead == pChunkHeader->sizeInBytes) {
74688 pParser->metadataCursor += 1;
74689 } else {
74690 }
74691 }
74692 } else {
74693 }
74694 } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_inst, "inst")) {
74695 if (pChunkHeader->sizeInBytes == DRWAV_INST_BYTES) {
74696 if (pParser->stage == drwav__metadata_parser_stage_count) {
74697 pParser->metadataCount += 1;
74698 } else {
74699 bytesRead = drwav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
74700 if (bytesRead == pChunkHeader->sizeInBytes) {
74701 pParser->metadataCursor += 1;
74702 } else {
74703 }
74704 }
74705 } else {
74706 }
74707 } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_acid, "acid")) {
74708 if (pChunkHeader->sizeInBytes == DRWAV_ACID_BYTES) {
74709 if (pParser->stage == drwav__metadata_parser_stage_count) {
74710 pParser->metadataCount += 1;
74711 } else {
74712 bytesRead = drwav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
74713 if (bytesRead == pChunkHeader->sizeInBytes) {
74714 pParser->metadataCursor += 1;
74715 } else {
74716 }
74717 }
74718 } else {
74719 }
74720 } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_cue, "cue ")) {
74721 if (pChunkHeader->sizeInBytes >= DRWAV_CUE_BYTES) {
74722 if (pParser->stage == drwav__metadata_parser_stage_count) {
74723 size_t cueCount;
74724 pParser->metadataCount += 1;
74725 cueCount = (size_t)(pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES;
74726 drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_cue_point) * cueCount, DRWAV_METADATA_ALIGNMENT);
74727 } else {
74728 bytesRead = drwav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);
74729 if (bytesRead == pChunkHeader->sizeInBytes) {
74730 pParser->metadataCursor += 1;
74731 } else {
74732 }
74733 }
74734 } else {
74735 }
74736 } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_bext, "bext")) {
74737 if (pChunkHeader->sizeInBytes >= DRWAV_BEXT_BYTES) {
74738 if (pParser->stage == drwav__metadata_parser_stage_count) {
74739 char buffer[DRWAV_BEXT_DESCRIPTION_BYTES + 1];
74740 size_t allocSizeNeeded = DRWAV_BEXT_UMID_BYTES;
74741 size_t bytesJustRead;
74742 buffer[DRWAV_BEXT_DESCRIPTION_BYTES] = '\0';
74743 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_DESCRIPTION_BYTES, &bytesRead);
74744 if (bytesJustRead != DRWAV_BEXT_DESCRIPTION_BYTES) {
74745 return bytesRead;
74746 }
74747 allocSizeNeeded += strlen(buffer) + 1;
74748 buffer[DRWAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0';
74749 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead);
74750 if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_NAME_BYTES) {
74751 return bytesRead;
74752 }
74753 allocSizeNeeded += strlen(buffer) + 1;
74754 buffer[DRWAV_BEXT_ORIGINATOR_REF_BYTES] = '\0';
74755 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead);
74756 if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_REF_BYTES) {
74757 return bytesRead;
74758 }
74759 allocSizeNeeded += strlen(buffer) + 1;
74760 allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES;
74761 drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1);
74762 pParser->metadataCount += 1;
74763 } else {
74764 bytesRead = drwav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes);
74765 if (bytesRead == pChunkHeader->sizeInBytes) {
74766 pParser->metadataCursor += 1;
74767 } else {
74768 }
74769 }
74770 } else {
74771 }
74772 } else if (drwav_fourcc_equal(pChunkID, "LIST") || drwav_fourcc_equal(pChunkID, "list")) {
74774 while (bytesRead < pChunkHeader->sizeInBytes) {
74775 drwav_uint8 subchunkId[4];
74776 drwav_uint8 subchunkSizeBuffer[4];
74777 drwav_uint64 subchunkDataSize;
74778 drwav_uint64 subchunkBytesRead = 0;
74779 drwav_uint64 bytesJustRead = drwav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead);
74780 if (bytesJustRead != sizeof(subchunkId)) {
74781 break;
74782 }
74783 if (drwav_fourcc_equal(subchunkId, "adtl")) {
74785 continue;
74786 } else if (drwav_fourcc_equal(subchunkId, "INFO")) {
74788 continue;
74789 }
74790 bytesJustRead = drwav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead);
74791 if (bytesJustRead != sizeof(subchunkSizeBuffer)) {
74792 break;
74793 }
74794 subchunkDataSize = drwav_bytes_to_u32(subchunkSizeBuffer);
74795 if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_label, "labl") || drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_note, "note")) {
74796 if (subchunkDataSize >= DRWAV_LIST_LABEL_OR_NOTE_BYTES) {
74797 drwav_uint64 stringSizeWithNullTerm = subchunkDataSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES;
74798 if (pParser->stage == drwav__metadata_parser_stage_count) {
74799 pParser->metadataCount += 1;
74800 drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1);
74801 } else {
74802 subchunkBytesRead = drwav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, drwav_fourcc_equal(subchunkId, "labl") ? drwav_metadata_type_list_label : drwav_metadata_type_list_note);
74803 if (subchunkBytesRead == subchunkDataSize) {
74804 pParser->metadataCursor += 1;
74805 } else {
74806 }
74807 }
74808 } else {
74809 }
74810 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_labelled_cue_region, "ltxt")) {
74811 if (subchunkDataSize >= DRWAV_LIST_LABELLED_TEXT_BYTES) {
74812 drwav_uint64 stringSizeWithNullTerminator = subchunkDataSize - DRWAV_LIST_LABELLED_TEXT_BYTES;
74813 if (pParser->stage == drwav__metadata_parser_stage_count) {
74814 pParser->metadataCount += 1;
74815 drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1);
74816 } else {
74817 subchunkBytesRead = drwav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize);
74818 if (subchunkBytesRead == subchunkDataSize) {
74819 pParser->metadataCursor += 1;
74820 } else {
74821 }
74822 }
74823 } else {
74824 }
74825 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_software, "ISFT")) {
74826 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_software);
74827 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_copyright, "ICOP")) {
74828 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_copyright);
74829 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_title, "INAM")) {
74830 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_title);
74831 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_artist, "IART")) {
74832 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_artist);
74833 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_comment, "ICMT")) {
74834 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_comment);
74835 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_date, "ICRD")) {
74836 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_date);
74837 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_genre, "IGNR")) {
74838 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_genre);
74839 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_album, "IPRD")) {
74840 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_album);
74841 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_tracknumber, "ITRK")) {
74842 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_tracknumber);
74843 } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) {
74844 subchunkBytesRead = drwav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType);
74845 }
74846 bytesRead += subchunkBytesRead;
74847 DRWAV_ASSERT(subchunkBytesRead <= subchunkDataSize);
74848 if (subchunkBytesRead < subchunkDataSize) {
74849 drwav_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead;
74850 if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, drwav_seek_origin_current)) {
74851 break;
74852 }
74853 bytesRead += bytesToSeek;
74854 }
74855 if ((subchunkDataSize % 2) == 1) {
74856 if (!pParser->onSeek(pParser->pReadSeekUserData, 1, drwav_seek_origin_current)) {
74857 break;
74858 }
74859 bytesRead += 1;
74860 }
74861 }
74862 } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) {
74863 bytesRead = drwav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, drwav_metadata_location_top_level);
74864 }
74865 return bytesRead;
74866}
74867DRWAV_PRIVATE drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav)
74868{
74869 drwav_uint32 bytesPerFrame;
74870 if ((pWav->bitsPerSample & 0x7) == 0) {
74871 bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3;
74872 } else {
74873 bytesPerFrame = pWav->fmt.blockAlign;
74874 }
74876 if (bytesPerFrame != pWav->fmt.channels) {
74877 return 0;
74878 }
74879 }
74880 return bytesPerFrame;
74881}
74883{
74884 if (pFMT == NULL) {
74885 return 0;
74886 }
74887 if (pFMT->formatTag != DR_WAVE_FORMAT_EXTENSIBLE) {
74888 return pFMT->formatTag;
74889 } else {
74890 return drwav_bytes_to_u16(pFMT->subFormat);
74891 }
74892}
74893DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
74894{
74895 if (pWav == NULL || onRead == NULL || onSeek == NULL) {
74896 return DRWAV_FALSE;
74897 }
74898 DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));
74899 pWav->onRead = onRead;
74900 pWav->onSeek = onSeek;
74901 pWav->pUserData = pReadSeekUserData;
74902 pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
74904 return DRWAV_FALSE;
74905 }
74906 return DRWAV_TRUE;
74907}
74908DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags)
74909{
74910 drwav_uint64 cursor;
74911 drwav_bool32 sequential;
74912 drwav_uint8 riff[4];
74913 drwav_fmt fmt;
74914 unsigned short translatedFormatTag;
74915 drwav_bool32 foundDataChunk;
74916 drwav_uint64 dataChunkSize = 0;
74917 drwav_uint64 sampleCountFromFactChunk = 0;
74918 drwav_uint64 chunkSize;
74919 drwav__metadata_parser metadataParser;
74920 cursor = 0;
74921 sequential = (flags & DRWAV_SEQUENTIAL) != 0;
74922 if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) {
74923 return DRWAV_FALSE;
74924 }
74925 if (drwav_fourcc_equal(riff, "RIFF")) {
74927 } else if (drwav_fourcc_equal(riff, "riff")) {
74928 int i;
74929 drwav_uint8 riff2[12];
74931 if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) {
74932 return DRWAV_FALSE;
74933 }
74934 for (i = 0; i < 12; ++i) {
74935 if (riff2[i] != drwavGUID_W64_RIFF[i+4]) {
74936 return DRWAV_FALSE;
74937 }
74938 }
74939 } else if (drwav_fourcc_equal(riff, "RF64")) {
74941 } else {
74942 return DRWAV_FALSE;
74943 }
74945 drwav_uint8 chunkSizeBytes[4];
74946 drwav_uint8 wave[4];
74947 if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
74948 return DRWAV_FALSE;
74949 }
74950 if (pWav->container == drwav_container_riff) {
74951 if (drwav_bytes_to_u32(chunkSizeBytes) < 36) {
74952 return DRWAV_FALSE;
74953 }
74954 } else {
74955 if (drwav_bytes_to_u32(chunkSizeBytes) != 0xFFFFFFFF) {
74956 return DRWAV_FALSE;
74957 }
74958 }
74959 if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
74960 return DRWAV_FALSE;
74961 }
74962 if (!drwav_fourcc_equal(wave, "WAVE")) {
74963 return DRWAV_FALSE;
74964 }
74965 } else {
74966 drwav_uint8 chunkSizeBytes[8];
74967 drwav_uint8 wave[16];
74968 if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
74969 return DRWAV_FALSE;
74970 }
74971 if (drwav_bytes_to_u64(chunkSizeBytes) < 80) {
74972 return DRWAV_FALSE;
74973 }
74974 if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
74975 return DRWAV_FALSE;
74976 }
74977 if (!drwav_guid_equal(wave, drwavGUID_W64_WAVE)) {
74978 return DRWAV_FALSE;
74979 }
74980 }
74981 if (pWav->container == drwav_container_rf64) {
74982 drwav_uint8 sizeBytes[8];
74983 drwav_uint64 bytesRemainingInChunk;
74984 drwav_chunk_header header;
74985 drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
74986 if (result != DRWAV_SUCCESS) {
74987 return DRWAV_FALSE;
74988 }
74989 if (!drwav_fourcc_equal(header.id.fourcc, "ds64")) {
74990 return DRWAV_FALSE;
74991 }
74992 bytesRemainingInChunk = header.sizeInBytes + header.paddingSize;
74993 if (!drwav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) {
74994 return DRWAV_FALSE;
74995 }
74996 bytesRemainingInChunk -= 8;
74997 cursor += 8;
74998 if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
74999 return DRWAV_FALSE;
75000 }
75001 bytesRemainingInChunk -= 8;
75002 dataChunkSize = drwav_bytes_to_u64(sizeBytes);
75003 if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
75004 return DRWAV_FALSE;
75005 }
75006 bytesRemainingInChunk -= 8;
75007 sampleCountFromFactChunk = drwav_bytes_to_u64(sizeBytes);
75008 if (!drwav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) {
75009 return DRWAV_FALSE;
75010 }
75011 cursor += bytesRemainingInChunk;
75012 }
75013 if (!drwav__read_fmt(pWav->onRead, pWav->onSeek, pWav->pUserData, pWav->container, &cursor, &fmt)) {
75014 return DRWAV_FALSE;
75015 }
75016 if ((fmt.sampleRate == 0 || fmt.sampleRate > DRWAV_MAX_SAMPLE_RATE) ||
75017 (fmt.channels == 0 || fmt.channels > DRWAV_MAX_CHANNELS) ||
75018 (fmt.bitsPerSample == 0 || fmt.bitsPerSample > DRWAV_MAX_BITS_PER_SAMPLE) ||
75019 fmt.blockAlign == 0) {
75020 return DRWAV_FALSE;
75021 }
75022 translatedFormatTag = fmt.formatTag;
75023 if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
75024 translatedFormatTag = drwav_bytes_to_u16(fmt.subFormat + 0);
75025 }
75026 memset(&metadataParser, 0, sizeof(metadataParser));
75027 if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) {
75028 drwav_uint64 cursorForMetadata = cursor;
75029 metadataParser.onRead = pWav->onRead;
75030 metadataParser.onSeek = pWav->onSeek;
75031 metadataParser.pReadSeekUserData = pWav->pUserData;
75032 metadataParser.stage = drwav__metadata_parser_stage_count;
75033 for (;;) {
75034 drwav_result result;
75035 drwav_uint64 bytesRead;
75036 drwav_uint64 remainingBytes;
75037 drwav_chunk_header header;
75038 result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursorForMetadata, &header);
75039 if (result != DRWAV_SUCCESS) {
75040 break;
75041 }
75042 bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes);
75043 DRWAV_ASSERT(bytesRead <= header.sizeInBytes);
75044 remainingBytes = header.sizeInBytes - bytesRead + header.paddingSize;
75045 if (!drwav__seek_forward(pWav->onSeek, remainingBytes, pWav->pUserData)) {
75046 break;
75047 }
75048 cursorForMetadata += remainingBytes;
75049 }
75050 if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) {
75051 return DRWAV_FALSE;
75052 }
75053 drwav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks);
75054 metadataParser.stage = drwav__metadata_parser_stage_read;
75055 }
75056 foundDataChunk = DRWAV_FALSE;
75057 for (;;) {
75058 drwav_chunk_header header;
75059 drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
75060 if (result != DRWAV_SUCCESS) {
75061 if (!foundDataChunk) {
75062 return DRWAV_FALSE;
75063 } else {
75064 break;
75065 }
75066 }
75067 if (!sequential && onChunk != NULL) {
75068 drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt);
75069 if (callbackBytesRead > 0) {
75070 if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) {
75071 return DRWAV_FALSE;
75072 }
75073 }
75074 }
75075 if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) {
75076 drwav_uint64 bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes);
75077 if (bytesRead > 0) {
75078 if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) {
75079 return DRWAV_FALSE;
75080 }
75081 }
75082 }
75083 if (!foundDataChunk) {
75084 pWav->dataChunkDataPos = cursor;
75085 }
75086 chunkSize = header.sizeInBytes;
75088 if (drwav_fourcc_equal(header.id.fourcc, "data")) {
75089 foundDataChunk = DRWAV_TRUE;
75090 if (pWav->container != drwav_container_rf64) {
75091 dataChunkSize = chunkSize;
75092 }
75093 }
75094 } else {
75095 if (drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA)) {
75096 foundDataChunk = DRWAV_TRUE;
75097 dataChunkSize = chunkSize;
75098 }
75099 }
75100 if (foundDataChunk && sequential) {
75101 break;
75102 }
75103 if (pWav->container == drwav_container_riff) {
75104 if (drwav_fourcc_equal(header.id.fourcc, "fact")) {
75105 drwav_uint32 sampleCount;
75106 if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) {
75107 return DRWAV_FALSE;
75108 }
75109 chunkSize -= 4;
75110 if (!foundDataChunk) {
75111 pWav->dataChunkDataPos = cursor;
75112 }
75114 sampleCountFromFactChunk = sampleCount;
75115 } else {
75116 sampleCountFromFactChunk = 0;
75117 }
75118 }
75119 } else if (pWav->container == drwav_container_w64) {
75120 if (drwav_guid_equal(header.id.guid, drwavGUID_W64_FACT)) {
75121 if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) {
75122 return DRWAV_FALSE;
75123 }
75124 chunkSize -= 8;
75125 if (!foundDataChunk) {
75126 pWav->dataChunkDataPos = cursor;
75127 }
75128 }
75129 } else if (pWav->container == drwav_container_rf64) {
75130 }
75131 chunkSize += header.paddingSize;
75132 if (!drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData)) {
75133 break;
75134 }
75135 cursor += chunkSize;
75136 if (!foundDataChunk) {
75137 pWav->dataChunkDataPos = cursor;
75138 }
75139 }
75140 pWav->pMetadata = metadataParser.pMetadata;
75141 pWav->metadataCount = metadataParser.metadataCount;
75142 if (!foundDataChunk) {
75143 return DRWAV_FALSE;
75144 }
75145 if (!sequential) {
75146 if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) {
75147 return DRWAV_FALSE;
75148 }
75149 cursor = pWav->dataChunkDataPos;
75150 }
75151 pWav->fmt = fmt;
75152 pWav->sampleRate = fmt.sampleRate;
75153 pWav->channels = fmt.channels;
75154 pWav->bitsPerSample = fmt.bitsPerSample;
75155 pWav->bytesRemaining = dataChunkSize;
75156 pWav->translatedFormatTag = translatedFormatTag;
75157 pWav->dataChunkDataSize = dataChunkSize;
75158 if (sampleCountFromFactChunk != 0) {
75159 pWav->totalPCMFrameCount = sampleCountFromFactChunk;
75160 } else {
75161 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
75162 if (bytesPerFrame == 0) {
75163 return DRWAV_FALSE;
75164 }
75165 pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame;
75167 drwav_uint64 totalBlockHeaderSizeInBytes;
75168 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
75169 if ((blockCount * fmt.blockAlign) < dataChunkSize) {
75170 blockCount += 1;
75171 }
75172 totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels);
75173 pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
75174 }
75176 drwav_uint64 totalBlockHeaderSizeInBytes;
75177 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
75178 if ((blockCount * fmt.blockAlign) < dataChunkSize) {
75179 blockCount += 1;
75180 }
75181 totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels);
75182 pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
75183 pWav->totalPCMFrameCount += blockCount;
75184 }
75185 }
75187 if (pWav->channels > 2) {
75188 return DRWAV_FALSE;
75189 }
75190 }
75191 if (drwav_get_bytes_per_pcm_frame(pWav) == 0) {
75192 return DRWAV_FALSE;
75193 }
75194#ifdef DR_WAV_LIBSNDFILE_COMPAT
75196 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
75197 pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels;
75198 }
75200 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
75201 pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels;
75202 }
75203#endif
75204 return DRWAV_TRUE;
75205}
75206DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
75207{
75208 return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks);
75209}
75210DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
75211{
75212 if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) {
75213 return DRWAV_FALSE;
75214 }
75215 return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
75216}
75217DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
75218{
75219 if (!drwav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
75220 return DRWAV_FALSE;
75221 }
75223 return drwav_init__internal(pWav, NULL, NULL, flags);
75224}
75226{
75227 drwav_metadata *result = pWav->pMetadata;
75228 pWav->pMetadata = NULL;
75229 pWav->metadataCount = 0;
75230 return result;
75231}
75232DRWAV_PRIVATE size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize)
75233{
75234 DRWAV_ASSERT(pWav != NULL);
75235 DRWAV_ASSERT(pWav->onWrite != NULL);
75236 return pWav->onWrite(pWav->pUserData, pData, dataSize);
75237}
75238DRWAV_PRIVATE size_t drwav__write_byte(drwav* pWav, drwav_uint8 byte)
75239{
75240 DRWAV_ASSERT(pWav != NULL);
75241 DRWAV_ASSERT(pWav->onWrite != NULL);
75242 return pWav->onWrite(pWav->pUserData, &byte, 1);
75243}
75244DRWAV_PRIVATE size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value)
75245{
75246 DRWAV_ASSERT(pWav != NULL);
75247 DRWAV_ASSERT(pWav->onWrite != NULL);
75248 if (!drwav__is_little_endian()) {
75249 value = drwav__bswap16(value);
75250 }
75251 return drwav__write(pWav, &value, 2);
75252}
75253DRWAV_PRIVATE size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value)
75254{
75255 DRWAV_ASSERT(pWav != NULL);
75256 DRWAV_ASSERT(pWav->onWrite != NULL);
75257 if (!drwav__is_little_endian()) {
75258 value = drwav__bswap32(value);
75259 }
75260 return drwav__write(pWav, &value, 4);
75261}
75262DRWAV_PRIVATE size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value)
75263{
75264 DRWAV_ASSERT(pWav != NULL);
75265 DRWAV_ASSERT(pWav->onWrite != NULL);
75266 if (!drwav__is_little_endian()) {
75267 value = drwav__bswap64(value);
75268 }
75269 return drwav__write(pWav, &value, 8);
75270}
75271DRWAV_PRIVATE size_t drwav__write_f32ne_to_le(drwav* pWav, float value)
75272{
75273 union {
75274 drwav_uint32 u32;
75275 float f32;
75276 } u;
75277 DRWAV_ASSERT(pWav != NULL);
75278 DRWAV_ASSERT(pWav->onWrite != NULL);
75279 u.f32 = value;
75280 if (!drwav__is_little_endian()) {
75281 u.u32 = drwav__bswap32(u.u32);
75282 }
75283 return drwav__write(pWav, &u.u32, 4);
75284}
75285DRWAV_PRIVATE size_t drwav__write_or_count(drwav* pWav, const void* pData, size_t dataSize)
75286{
75287 if (pWav == NULL) {
75288 return dataSize;
75289 }
75290 return drwav__write(pWav, pData, dataSize);
75291}
75292DRWAV_PRIVATE size_t drwav__write_or_count_byte(drwav* pWav, drwav_uint8 byte)
75293{
75294 if (pWav == NULL) {
75295 return 1;
75296 }
75297 return drwav__write_byte(pWav, byte);
75298}
75299DRWAV_PRIVATE size_t drwav__write_or_count_u16ne_to_le(drwav* pWav, drwav_uint16 value)
75300{
75301 if (pWav == NULL) {
75302 return 2;
75303 }
75304 return drwav__write_u16ne_to_le(pWav, value);
75305}
75306DRWAV_PRIVATE size_t drwav__write_or_count_u32ne_to_le(drwav* pWav, drwav_uint32 value)
75307{
75308 if (pWav == NULL) {
75309 return 4;
75310 }
75311 return drwav__write_u32ne_to_le(pWav, value);
75312}
75313#if 0
75314DRWAV_PRIVATE size_t drwav__write_or_count_u64ne_to_le(drwav* pWav, drwav_uint64 value)
75315{
75316 if (pWav == NULL) {
75317 return 8;
75318 }
75319 return drwav__write_u64ne_to_le(pWav, value);
75320}
75321#endif
75322DRWAV_PRIVATE size_t drwav__write_or_count_f32ne_to_le(drwav* pWav, float value)
75323{
75324 if (pWav == NULL) {
75325 return 4;
75326 }
75327 return drwav__write_f32ne_to_le(pWav, value);
75328}
75329DRWAV_PRIVATE size_t drwav__write_or_count_string_to_fixed_size_buf(drwav* pWav, char* str, size_t bufFixedSize)
75330{
75331 size_t len;
75332 if (pWav == NULL) {
75333 return bufFixedSize;
75334 }
75335 len = drwav__strlen_clamped(str, bufFixedSize);
75336 drwav__write_or_count(pWav, str, len);
75337 if (len < bufFixedSize) {
75338 size_t i;
75339 for (i = 0; i < bufFixedSize - len; ++i) {
75340 drwav__write_byte(pWav, 0);
75341 }
75342 }
75343 return bufFixedSize;
75344}
75345DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* pMetadatas, drwav_uint32 metadataCount)
75346{
75347 size_t bytesWritten = 0;
75348 drwav_bool32 hasListAdtl = DRWAV_FALSE;
75349 drwav_bool32 hasListInfo = DRWAV_FALSE;
75350 drwav_uint32 iMetadata;
75351 if (pMetadatas == NULL || metadataCount == 0) {
75352 return 0;
75353 }
75354 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
75355 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
75356 drwav_uint32 chunkSize = 0;
75358 hasListInfo = DRWAV_TRUE;
75359 }
75361 hasListAdtl = DRWAV_TRUE;
75362 }
75363 switch (pMetadata->type) {
75364 case drwav_metadata_type_smpl:
75365 {
75366 drwav_uint32 iLoop;
75367 chunkSize = DRWAV_SMPL_BYTES + DRWAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes;
75368 bytesWritten += drwav__write_or_count(pWav, "smpl", 4);
75369 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
75370 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId);
75371 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId);
75372 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds);
75373 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote);
75374 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction);
75375 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat);
75376 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset);
75377 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount);
75378 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);
75379 for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) {
75380 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId);
75381 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type);
75382 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset);
75383 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset);
75384 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction);
75385 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount);
75386 }
75387 if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {
75388 bytesWritten += drwav__write(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);
75389 }
75390 } break;
75392 {
75393 chunkSize = DRWAV_INST_BYTES;
75394 bytesWritten += drwav__write_or_count(pWav, "inst", 4);
75395 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
75396 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1);
75397 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1);
75398 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1);
75399 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1);
75400 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1);
75401 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1);
75402 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1);
75403 } break;
75405 {
75406 drwav_uint32 iCuePoint;
75407 chunkSize = DRWAV_CUE_BYTES + DRWAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount;
75408 bytesWritten += drwav__write_or_count(pWav, "cue ", 4);
75409 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
75410 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount);
75411 for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {
75412 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id);
75413 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition);
75414 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4);
75415 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart);
75416 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart);
75417 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset);
75418 }
75419 } break;
75421 {
75422 chunkSize = DRWAV_ACID_BYTES;
75423 bytesWritten += drwav__write_or_count(pWav, "acid", 4);
75424 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
75425 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags);
75426 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote);
75427 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1);
75428 bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2);
75429 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats);
75430 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator);
75431 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator);
75432 bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo);
75433 } break;
75435 {
75436 char reservedBuf[DRWAV_BEXT_RESERVED_BYTES];
75437 drwav_uint32 timeReferenceLow;
75438 drwav_uint32 timeReferenceHigh;
75439 chunkSize = DRWAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize;
75440 bytesWritten += drwav__write_or_count(pWav, "bext", 4);
75441 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
75442 bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, DRWAV_BEXT_DESCRIPTION_BYTES);
75443 bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, DRWAV_BEXT_ORIGINATOR_NAME_BYTES);
75444 bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, DRWAV_BEXT_ORIGINATOR_REF_BYTES);
75445 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate));
75446 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime));
75447 timeReferenceLow = (drwav_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF);
75448 timeReferenceHigh = (drwav_uint32)(pMetadata->data.bext.timeReference >> 32);
75449 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceLow);
75450 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh);
75451 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version);
75452 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES);
75453 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue);
75454 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange);
75455 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel);
75456 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness);
75457 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness);
75458 memset(reservedBuf, 0, sizeof(reservedBuf));
75459 bytesWritten += drwav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf));
75460 if (pMetadata->data.bext.codingHistorySize > 0) {
75461 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize);
75462 }
75463 } break;
75465 {
75467 chunkSize = pMetadata->data.unknown.dataSizeInBytes;
75468 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
75469 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
75470 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes);
75471 }
75472 } break;
75473 default: break;
75474 }
75475 if ((chunkSize % 2) != 0) {
75476 bytesWritten += drwav__write_or_count_byte(pWav, 0);
75477 }
75478 }
75479 if (hasListInfo) {
75480 drwav_uint32 chunkSize = 4;
75481 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
75482 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
75484 chunkSize += 8;
75485 chunkSize += pMetadata->data.infoText.stringLength + 1;
75487 chunkSize += 8;
75488 chunkSize += pMetadata->data.unknown.dataSizeInBytes;
75489 }
75490 if ((chunkSize % 2) != 0) {
75491 chunkSize += 1;
75492 }
75493 }
75494 bytesWritten += drwav__write_or_count(pWav, "LIST", 4);
75495 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
75496 bytesWritten += drwav__write_or_count(pWav, "INFO", 4);
75497 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
75498 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
75499 drwav_uint32 subchunkSize = 0;
75501 const char* pID = NULL;
75502 switch (pMetadata->type) {
75503 case drwav_metadata_type_list_info_software: pID = "ISFT"; break;
75504 case drwav_metadata_type_list_info_copyright: pID = "ICOP"; break;
75505 case drwav_metadata_type_list_info_title: pID = "INAM"; break;
75506 case drwav_metadata_type_list_info_artist: pID = "IART"; break;
75507 case drwav_metadata_type_list_info_comment: pID = "ICMT"; break;
75508 case drwav_metadata_type_list_info_date: pID = "ICRD"; break;
75509 case drwav_metadata_type_list_info_genre: pID = "IGNR"; break;
75510 case drwav_metadata_type_list_info_album: pID = "IPRD"; break;
75511 case drwav_metadata_type_list_info_tracknumber: pID = "ITRK"; break;
75512 default: break;
75513 }
75514 DRWAV_ASSERT(pID != NULL);
75515 if (pMetadata->data.infoText.stringLength) {
75516 subchunkSize = pMetadata->data.infoText.stringLength + 1;
75517 bytesWritten += drwav__write_or_count(pWav, pID, 4);
75518 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
75519 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength);
75520 bytesWritten += drwav__write_or_count_byte(pWav, '\0');
75521 }
75523 if (pMetadata->data.unknown.dataSizeInBytes) {
75524 subchunkSize = pMetadata->data.unknown.dataSizeInBytes;
75525 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
75526 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes);
75527 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);
75528 }
75529 }
75530 if ((subchunkSize % 2) != 0) {
75531 bytesWritten += drwav__write_or_count_byte(pWav, 0);
75532 }
75533 }
75534 }
75535 if (hasListAdtl) {
75536 drwav_uint32 chunkSize = 4;
75537 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
75538 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
75539 switch (pMetadata->type)
75540 {
75541 case drwav_metadata_type_list_label:
75542 case drwav_metadata_type_list_note:
75543 {
75544 chunkSize += 8;
75545 chunkSize += DRWAV_LIST_LABEL_OR_NOTE_BYTES;
75546 if (pMetadata->data.labelOrNote.stringLength > 0) {
75547 chunkSize += pMetadata->data.labelOrNote.stringLength + 1;
75548 }
75549 } break;
75551 {
75552 chunkSize += 8;
75553 chunkSize += DRWAV_LIST_LABELLED_TEXT_BYTES;
75554 if (pMetadata->data.labelledCueRegion.stringLength > 0) {
75555 chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;
75556 }
75557 } break;
75559 {
75561 chunkSize += 8;
75562 chunkSize += pMetadata->data.unknown.dataSizeInBytes;
75563 }
75564 } break;
75565 default: break;
75566 }
75567 if ((chunkSize % 2) != 0) {
75568 chunkSize += 1;
75569 }
75570 }
75571 bytesWritten += drwav__write_or_count(pWav, "LIST", 4);
75572 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
75573 bytesWritten += drwav__write_or_count(pWav, "adtl", 4);
75574 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
75575 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
75576 drwav_uint32 subchunkSize = 0;
75577 switch (pMetadata->type)
75578 {
75579 case drwav_metadata_type_list_label:
75580 case drwav_metadata_type_list_note:
75581 {
75582 if (pMetadata->data.labelOrNote.stringLength > 0) {
75583 const char *pID = NULL;
75584 if (pMetadata->type == drwav_metadata_type_list_label) {
75585 pID = "labl";
75586 }
75587 else if (pMetadata->type == drwav_metadata_type_list_note) {
75588 pID = "note";
75589 }
75590 DRWAV_ASSERT(pID != NULL);
75591 DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);
75592 subchunkSize = DRWAV_LIST_LABEL_OR_NOTE_BYTES;
75593 bytesWritten += drwav__write_or_count(pWav, pID, 4);
75594 subchunkSize += pMetadata->data.labelOrNote.stringLength + 1;
75595 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
75596 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId);
75597 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength);
75598 bytesWritten += drwav__write_or_count_byte(pWav, '\0');
75599 }
75600 } break;
75602 {
75603 subchunkSize = DRWAV_LIST_LABELLED_TEXT_BYTES;
75604 bytesWritten += drwav__write_or_count(pWav, "ltxt", 4);
75605 if (pMetadata->data.labelledCueRegion.stringLength > 0) {
75606 subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;
75607 }
75608 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
75609 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId);
75610 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength);
75611 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4);
75612 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country);
75613 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language);
75614 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect);
75615 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage);
75616 if (pMetadata->data.labelledCueRegion.stringLength > 0) {
75617 DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);
75618 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength);
75619 bytesWritten += drwav__write_or_count_byte(pWav, '\0');
75620 }
75621 } break;
75623 {
75625 subchunkSize = pMetadata->data.unknown.dataSizeInBytes;
75626 DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL);
75627 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
75628 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
75629 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);
75630 }
75631 } break;
75632 default: break;
75633 }
75634 if ((subchunkSize % 2) != 0) {
75635 bytesWritten += drwav__write_or_count_byte(pWav, 0);
75636 }
75637 }
75638 }
75639 DRWAV_ASSERT((bytesWritten % 2) == 0);
75640 return bytesWritten;
75641}
75642DRWAV_PRIVATE drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize, drwav_metadata* pMetadata, drwav_uint32 metadataCount)
75643{
75644 drwav_uint64 chunkSize = 4 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize);
75645 if (chunkSize > 0xFFFFFFFFUL) {
75646 chunkSize = 0xFFFFFFFFUL;
75647 }
75648 return (drwav_uint32)chunkSize;
75649}
75650DRWAV_PRIVATE drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize)
75651{
75652 if (dataChunkSize <= 0xFFFFFFFFUL) {
75653 return (drwav_uint32)dataChunkSize;
75654 } else {
75655 return 0xFFFFFFFFUL;
75656 }
75657}
75658DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize)
75659{
75660 drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize);
75661 return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize;
75662}
75663DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize)
75664{
75665 return 24 + dataChunkSize;
75666}
75667DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_rf64(drwav_uint64 dataChunkSize, drwav_metadata *metadata, drwav_uint32 numMetadata)
75668{
75669 drwav_uint64 chunkSize = 4 + 36 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize);
75670 if (chunkSize > 0xFFFFFFFFUL) {
75671 chunkSize = 0xFFFFFFFFUL;
75672 }
75673 return chunkSize;
75674}
75675DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_rf64(drwav_uint64 dataChunkSize)
75676{
75677 return dataChunkSize;
75678}
75679DRWAV_PRIVATE drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
75680{
75681 if (pWav == NULL || onWrite == NULL) {
75682 return DRWAV_FALSE;
75683 }
75684 if (!isSequential && onSeek == NULL) {
75685 return DRWAV_FALSE;
75686 }
75687 if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) {
75688 return DRWAV_FALSE;
75689 }
75690 if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) {
75691 return DRWAV_FALSE;
75692 }
75693 DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));
75694 pWav->onWrite = onWrite;
75695 pWav->onSeek = onSeek;
75696 pWav->pUserData = pUserData;
75697 pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
75699 return DRWAV_FALSE;
75700 }
75701 pWav->fmt.formatTag = (drwav_uint16)pFormat->format;
75702 pWav->fmt.channels = (drwav_uint16)pFormat->channels;
75703 pWav->fmt.sampleRate = pFormat->sampleRate;
75704 pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8);
75705 pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8);
75706 pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
75707 pWav->fmt.extendedSize = 0;
75708 pWav->isSequentialWrite = isSequential;
75709 return DRWAV_TRUE;
75710}
75711DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)
75712{
75713 size_t runningPos = 0;
75714 drwav_uint64 initialDataChunkSize = 0;
75715 drwav_uint64 chunkSizeFMT;
75716 if (pWav->isSequentialWrite) {
75717 initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8;
75718 if (pFormat->container == drwav_container_riff) {
75719 if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) {
75720 return DRWAV_FALSE;
75721 }
75722 }
75723 }
75724 pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize;
75725 if (pFormat->container == drwav_container_riff) {
75726 drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize;
75727 runningPos += drwav__write(pWav, "RIFF", 4);
75728 runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF);
75729 runningPos += drwav__write(pWav, "WAVE", 4);
75730 } else if (pFormat->container == drwav_container_w64) {
75731 drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize;
75732 runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16);
75733 runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF);
75734 runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16);
75735 } else if (pFormat->container == drwav_container_rf64) {
75736 runningPos += drwav__write(pWav, "RF64", 4);
75737 runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF);
75738 runningPos += drwav__write(pWav, "WAVE", 4);
75739 }
75740 if (pFormat->container == drwav_container_rf64) {
75741 drwav_uint32 initialds64ChunkSize = 28;
75742 drwav_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize;
75743 runningPos += drwav__write(pWav, "ds64", 4);
75744 runningPos += drwav__write_u32ne_to_le(pWav, initialds64ChunkSize);
75745 runningPos += drwav__write_u64ne_to_le(pWav, initialRiffChunkSize);
75746 runningPos += drwav__write_u64ne_to_le(pWav, initialDataChunkSize);
75747 runningPos += drwav__write_u64ne_to_le(pWav, totalSampleCount);
75748 runningPos += drwav__write_u32ne_to_le(pWav, 0);
75749 }
75750 if (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64) {
75751 chunkSizeFMT = 16;
75752 runningPos += drwav__write(pWav, "fmt ", 4);
75753 runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT);
75754 } else if (pFormat->container == drwav_container_w64) {
75755 chunkSizeFMT = 40;
75756 runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16);
75757 runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT);
75758 }
75759 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag);
75760 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels);
75761 runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate);
75762 runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec);
75763 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign);
75764 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample);
75765 if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64)) {
75766 runningPos += drwav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount);
75767 }
75768 pWav->dataChunkDataPos = runningPos;
75769 if (pFormat->container == drwav_container_riff) {
75770 drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize;
75771 runningPos += drwav__write(pWav, "data", 4);
75772 runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA);
75773 } else if (pFormat->container == drwav_container_w64) {
75774 drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize;
75775 runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16);
75776 runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA);
75777 } else if (pFormat->container == drwav_container_rf64) {
75778 runningPos += drwav__write(pWav, "data", 4);
75779 runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF);
75780 }
75781 pWav->container = pFormat->container;
75782 pWav->channels = (drwav_uint16)pFormat->channels;
75783 pWav->sampleRate = pFormat->sampleRate;
75784 pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
75785 pWav->translatedFormatTag = (drwav_uint16)pFormat->format;
75786 pWav->dataChunkDataPos = runningPos;
75787 return DRWAV_TRUE;
75788}
75789DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
75790{
75791 if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
75792 return DRWAV_FALSE;
75793 }
75794 return drwav_init_write__internal(pWav, pFormat, 0);
75795}
75796DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
75797{
75798 if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) {
75799 return DRWAV_FALSE;
75800 }
75801 return drwav_init_write__internal(pWav, pFormat, totalSampleCount);
75802}
75803DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
75804{
75805 if (pFormat == NULL) {
75806 return DRWAV_FALSE;
75807 }
75808 return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks);
75809}
75810DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount)
75811{
75812 if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
75813 return DRWAV_FALSE;
75814 }
75815 pWav->pMetadata = pMetadata;
75816 pWav->metadataCount = metadataCount;
75817 return drwav_init_write__internal(pWav, pFormat, 0);
75818}
75819DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount)
75820{
75821 drwav_uint64 targetDataSizeBytes = (drwav_uint64)((drwav_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0);
75822 drwav_uint64 riffChunkSizeBytes;
75823 drwav_uint64 fileSizeBytes = 0;
75824 if (pFormat->container == drwav_container_riff) {
75825 riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount);
75826 fileSizeBytes = (8 + riffChunkSizeBytes);
75827 } else if (pFormat->container == drwav_container_w64) {
75828 riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes);
75829 fileSizeBytes = riffChunkSizeBytes;
75830 } else if (pFormat->container == drwav_container_rf64) {
75831 riffChunkSizeBytes = drwav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount);
75832 fileSizeBytes = (8 + riffChunkSizeBytes);
75833 }
75834 return fileSizeBytes;
75835}
75836#ifndef DR_WAV_NO_STDIO
75837#include <errno.h>
75838DRWAV_PRIVATE drwav_result drwav_result_from_errno(int e)
75839{
75840 switch (e)
75841 {
75842 case 0: return DRWAV_SUCCESS;
75843 #ifdef EPERM
75844 case EPERM: return DRWAV_INVALID_OPERATION;
75845 #endif
75846 #ifdef ENOENT
75847 case ENOENT: return DRWAV_DOES_NOT_EXIST;
75848 #endif
75849 #ifdef ESRCH
75850 case ESRCH: return DRWAV_DOES_NOT_EXIST;
75851 #endif
75852 #ifdef EINTR
75853 case EINTR: return DRWAV_INTERRUPT;
75854 #endif
75855 #ifdef EIO
75856 case EIO: return DRWAV_IO_ERROR;
75857 #endif
75858 #ifdef ENXIO
75859 case ENXIO: return DRWAV_DOES_NOT_EXIST;
75860 #endif
75861 #ifdef E2BIG
75862 case E2BIG: return DRWAV_INVALID_ARGS;
75863 #endif
75864 #ifdef ENOEXEC
75865 case ENOEXEC: return DRWAV_INVALID_FILE;
75866 #endif
75867 #ifdef EBADF
75868 case EBADF: return DRWAV_INVALID_FILE;
75869 #endif
75870 #ifdef ECHILD
75871 case ECHILD: return DRWAV_ERROR;
75872 #endif
75873 #ifdef EAGAIN
75874 case EAGAIN: return DRWAV_UNAVAILABLE;
75875 #endif
75876 #ifdef ENOMEM
75877 case ENOMEM: return DRWAV_OUT_OF_MEMORY;
75878 #endif
75879 #ifdef EACCES
75880 case EACCES: return DRWAV_ACCESS_DENIED;
75881 #endif
75882 #ifdef EFAULT
75883 case EFAULT: return DRWAV_BAD_ADDRESS;
75884 #endif
75885 #ifdef ENOTBLK
75886 case ENOTBLK: return DRWAV_ERROR;
75887 #endif
75888 #ifdef EBUSY
75889 case EBUSY: return DRWAV_BUSY;
75890 #endif
75891 #ifdef EEXIST
75892 case EEXIST: return DRWAV_ALREADY_EXISTS;
75893 #endif
75894 #ifdef EXDEV
75895 case EXDEV: return DRWAV_ERROR;
75896 #endif
75897 #ifdef ENODEV
75898 case ENODEV: return DRWAV_DOES_NOT_EXIST;
75899 #endif
75900 #ifdef ENOTDIR
75901 case ENOTDIR: return DRWAV_NOT_DIRECTORY;
75902 #endif
75903 #ifdef EISDIR
75904 case EISDIR: return DRWAV_IS_DIRECTORY;
75905 #endif
75906 #ifdef EINVAL
75907 case EINVAL: return DRWAV_INVALID_ARGS;
75908 #endif
75909 #ifdef ENFILE
75910 case ENFILE: return DRWAV_TOO_MANY_OPEN_FILES;
75911 #endif
75912 #ifdef EMFILE
75913 case EMFILE: return DRWAV_TOO_MANY_OPEN_FILES;
75914 #endif
75915 #ifdef ENOTTY
75916 case ENOTTY: return DRWAV_INVALID_OPERATION;
75917 #endif
75918 #ifdef ETXTBSY
75919 case ETXTBSY: return DRWAV_BUSY;
75920 #endif
75921 #ifdef EFBIG
75922 case EFBIG: return DRWAV_TOO_BIG;
75923 #endif
75924 #ifdef ENOSPC
75925 case ENOSPC: return DRWAV_NO_SPACE;
75926 #endif
75927 #ifdef ESPIPE
75928 case ESPIPE: return DRWAV_BAD_SEEK;
75929 #endif
75930 #ifdef EROFS
75931 case EROFS: return DRWAV_ACCESS_DENIED;
75932 #endif
75933 #ifdef EMLINK
75934 case EMLINK: return DRWAV_TOO_MANY_LINKS;
75935 #endif
75936 #ifdef EPIPE
75937 case EPIPE: return DRWAV_BAD_PIPE;
75938 #endif
75939 #ifdef EDOM
75940 case EDOM: return DRWAV_OUT_OF_RANGE;
75941 #endif
75942 #ifdef ERANGE
75943 case ERANGE: return DRWAV_OUT_OF_RANGE;
75944 #endif
75945 #ifdef EDEADLK
75946 case EDEADLK: return DRWAV_DEADLOCK;
75947 #endif
75948 #ifdef ENAMETOOLONG
75949 case ENAMETOOLONG: return DRWAV_PATH_TOO_LONG;
75950 #endif
75951 #ifdef ENOLCK
75952 case ENOLCK: return DRWAV_ERROR;
75953 #endif
75954 #ifdef ENOSYS
75955 case ENOSYS: return DRWAV_NOT_IMPLEMENTED;
75956 #endif
75957 #ifdef ENOTEMPTY
75958 case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY;
75959 #endif
75960 #ifdef ELOOP
75961 case ELOOP: return DRWAV_TOO_MANY_LINKS;
75962 #endif
75963 #ifdef ENOMSG
75964 case ENOMSG: return DRWAV_NO_MESSAGE;
75965 #endif
75966 #ifdef EIDRM
75967 case EIDRM: return DRWAV_ERROR;
75968 #endif
75969 #ifdef ECHRNG
75970 case ECHRNG: return DRWAV_ERROR;
75971 #endif
75972 #ifdef EL2NSYNC
75973 case EL2NSYNC: return DRWAV_ERROR;
75974 #endif
75975 #ifdef EL3HLT
75976 case EL3HLT: return DRWAV_ERROR;
75977 #endif
75978 #ifdef EL3RST
75979 case EL3RST: return DRWAV_ERROR;
75980 #endif
75981 #ifdef ELNRNG
75982 case ELNRNG: return DRWAV_OUT_OF_RANGE;
75983 #endif
75984 #ifdef EUNATCH
75985 case EUNATCH: return DRWAV_ERROR;
75986 #endif
75987 #ifdef ENOCSI
75988 case ENOCSI: return DRWAV_ERROR;
75989 #endif
75990 #ifdef EL2HLT
75991 case EL2HLT: return DRWAV_ERROR;
75992 #endif
75993 #ifdef EBADE
75994 case EBADE: return DRWAV_ERROR;
75995 #endif
75996 #ifdef EBADR
75997 case EBADR: return DRWAV_ERROR;
75998 #endif
75999 #ifdef EXFULL
76000 case EXFULL: return DRWAV_ERROR;
76001 #endif
76002 #ifdef ENOANO
76003 case ENOANO: return DRWAV_ERROR;
76004 #endif
76005 #ifdef EBADRQC
76006 case EBADRQC: return DRWAV_ERROR;
76007 #endif
76008 #ifdef EBADSLT
76009 case EBADSLT: return DRWAV_ERROR;
76010 #endif
76011 #ifdef EBFONT
76012 case EBFONT: return DRWAV_INVALID_FILE;
76013 #endif
76014 #ifdef ENOSTR
76015 case ENOSTR: return DRWAV_ERROR;
76016 #endif
76017 #ifdef ENODATA
76018 case ENODATA: return DRWAV_NO_DATA_AVAILABLE;
76019 #endif
76020 #ifdef ETIME
76021 case ETIME: return DRWAV_TIMEOUT;
76022 #endif
76023 #ifdef ENOSR
76024 case ENOSR: return DRWAV_NO_DATA_AVAILABLE;
76025 #endif
76026 #ifdef ENONET
76027 case ENONET: return DRWAV_NO_NETWORK;
76028 #endif
76029 #ifdef ENOPKG
76030 case ENOPKG: return DRWAV_ERROR;
76031 #endif
76032 #ifdef EREMOTE
76033 case EREMOTE: return DRWAV_ERROR;
76034 #endif
76035 #ifdef ENOLINK
76036 case ENOLINK: return DRWAV_ERROR;
76037 #endif
76038 #ifdef EADV
76039 case EADV: return DRWAV_ERROR;
76040 #endif
76041 #ifdef ESRMNT
76042 case ESRMNT: return DRWAV_ERROR;
76043 #endif
76044 #ifdef ECOMM
76045 case ECOMM: return DRWAV_ERROR;
76046 #endif
76047 #ifdef EPROTO
76048 case EPROTO: return DRWAV_ERROR;
76049 #endif
76050 #ifdef EMULTIHOP
76051 case EMULTIHOP: return DRWAV_ERROR;
76052 #endif
76053 #ifdef EDOTDOT
76054 case EDOTDOT: return DRWAV_ERROR;
76055 #endif
76056 #ifdef EBADMSG
76057 case EBADMSG: return DRWAV_BAD_MESSAGE;
76058 #endif
76059 #ifdef EOVERFLOW
76060 case EOVERFLOW: return DRWAV_TOO_BIG;
76061 #endif
76062 #ifdef ENOTUNIQ
76063 case ENOTUNIQ: return DRWAV_NOT_UNIQUE;
76064 #endif
76065 #ifdef EBADFD
76066 case EBADFD: return DRWAV_ERROR;
76067 #endif
76068 #ifdef EREMCHG
76069 case EREMCHG: return DRWAV_ERROR;
76070 #endif
76071 #ifdef ELIBACC
76072 case ELIBACC: return DRWAV_ACCESS_DENIED;
76073 #endif
76074 #ifdef ELIBBAD
76075 case ELIBBAD: return DRWAV_INVALID_FILE;
76076 #endif
76077 #ifdef ELIBSCN
76078 case ELIBSCN: return DRWAV_INVALID_FILE;
76079 #endif
76080 #ifdef ELIBMAX
76081 case ELIBMAX: return DRWAV_ERROR;
76082 #endif
76083 #ifdef ELIBEXEC
76084 case ELIBEXEC: return DRWAV_ERROR;
76085 #endif
76086 #ifdef EILSEQ
76087 case EILSEQ: return DRWAV_INVALID_DATA;
76088 #endif
76089 #ifdef ERESTART
76090 case ERESTART: return DRWAV_ERROR;
76091 #endif
76092 #ifdef ESTRPIPE
76093 case ESTRPIPE: return DRWAV_ERROR;
76094 #endif
76095 #ifdef EUSERS
76096 case EUSERS: return DRWAV_ERROR;
76097 #endif
76098 #ifdef ENOTSOCK
76099 case ENOTSOCK: return DRWAV_NOT_SOCKET;
76100 #endif
76101 #ifdef EDESTADDRREQ
76102 case EDESTADDRREQ: return DRWAV_NO_ADDRESS;
76103 #endif
76104 #ifdef EMSGSIZE
76105 case EMSGSIZE: return DRWAV_TOO_BIG;
76106 #endif
76107 #ifdef EPROTOTYPE
76108 case EPROTOTYPE: return DRWAV_BAD_PROTOCOL;
76109 #endif
76110 #ifdef ENOPROTOOPT
76111 case ENOPROTOOPT: return DRWAV_PROTOCOL_UNAVAILABLE;
76112 #endif
76113 #ifdef EPROTONOSUPPORT
76114 case EPROTONOSUPPORT: return DRWAV_PROTOCOL_NOT_SUPPORTED;
76115 #endif
76116 #ifdef ESOCKTNOSUPPORT
76117 case ESOCKTNOSUPPORT: return DRWAV_SOCKET_NOT_SUPPORTED;
76118 #endif
76119 #ifdef EOPNOTSUPP
76120 case EOPNOTSUPP: return DRWAV_INVALID_OPERATION;
76121 #endif
76122 #ifdef EPFNOSUPPORT
76123 case EPFNOSUPPORT: return DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED;
76124 #endif
76125 #ifdef EAFNOSUPPORT
76126 case EAFNOSUPPORT: return DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED;
76127 #endif
76128 #ifdef EADDRINUSE
76129 case EADDRINUSE: return DRWAV_ALREADY_IN_USE;
76130 #endif
76131 #ifdef EADDRNOTAVAIL
76132 case EADDRNOTAVAIL: return DRWAV_ERROR;
76133 #endif
76134 #ifdef ENETDOWN
76135 case ENETDOWN: return DRWAV_NO_NETWORK;
76136 #endif
76137 #ifdef ENETUNREACH
76138 case ENETUNREACH: return DRWAV_NO_NETWORK;
76139 #endif
76140 #ifdef ENETRESET
76141 case ENETRESET: return DRWAV_NO_NETWORK;
76142 #endif
76143 #ifdef ECONNABORTED
76144 case ECONNABORTED: return DRWAV_NO_NETWORK;
76145 #endif
76146 #ifdef ECONNRESET
76147 case ECONNRESET: return DRWAV_CONNECTION_RESET;
76148 #endif
76149 #ifdef ENOBUFS
76150 case ENOBUFS: return DRWAV_NO_SPACE;
76151 #endif
76152 #ifdef EISCONN
76153 case EISCONN: return DRWAV_ALREADY_CONNECTED;
76154 #endif
76155 #ifdef ENOTCONN
76156 case ENOTCONN: return DRWAV_NOT_CONNECTED;
76157 #endif
76158 #ifdef ESHUTDOWN
76159 case ESHUTDOWN: return DRWAV_ERROR;
76160 #endif
76161 #ifdef ETOOMANYREFS
76162 case ETOOMANYREFS: return DRWAV_ERROR;
76163 #endif
76164 #ifdef ETIMEDOUT
76165 case ETIMEDOUT: return DRWAV_TIMEOUT;
76166 #endif
76167 #ifdef ECONNREFUSED
76168 case ECONNREFUSED: return DRWAV_CONNECTION_REFUSED;
76169 #endif
76170 #ifdef EHOSTDOWN
76171 case EHOSTDOWN: return DRWAV_NO_HOST;
76172 #endif
76173 #ifdef EHOSTUNREACH
76174 case EHOSTUNREACH: return DRWAV_NO_HOST;
76175 #endif
76176 #ifdef EALREADY
76177 case EALREADY: return DRWAV_IN_PROGRESS;
76178 #endif
76179 #ifdef EINPROGRESS
76180 case EINPROGRESS: return DRWAV_IN_PROGRESS;
76181 #endif
76182 #ifdef ESTALE
76183 case ESTALE: return DRWAV_INVALID_FILE;
76184 #endif
76185 #ifdef EUCLEAN
76186 case EUCLEAN: return DRWAV_ERROR;
76187 #endif
76188 #ifdef ENOTNAM
76189 case ENOTNAM: return DRWAV_ERROR;
76190 #endif
76191 #ifdef ENAVAIL
76192 case ENAVAIL: return DRWAV_ERROR;
76193 #endif
76194 #ifdef EISNAM
76195 case EISNAM: return DRWAV_ERROR;
76196 #endif
76197 #ifdef EREMOTEIO
76198 case EREMOTEIO: return DRWAV_IO_ERROR;
76199 #endif
76200 #ifdef EDQUOT
76201 case EDQUOT: return DRWAV_NO_SPACE;
76202 #endif
76203 #ifdef ENOMEDIUM
76204 case ENOMEDIUM: return DRWAV_DOES_NOT_EXIST;
76205 #endif
76206 #ifdef EMEDIUMTYPE
76207 case EMEDIUMTYPE: return DRWAV_ERROR;
76208 #endif
76209 #ifdef ECANCELED
76210 case ECANCELED: return DRWAV_CANCELLED;
76211 #endif
76212 #ifdef ENOKEY
76213 case ENOKEY: return DRWAV_ERROR;
76214 #endif
76215 #ifdef EKEYEXPIRED
76216 case EKEYEXPIRED: return DRWAV_ERROR;
76217 #endif
76218 #ifdef EKEYREVOKED
76219 case EKEYREVOKED: return DRWAV_ERROR;
76220 #endif
76221 #ifdef EKEYREJECTED
76222 case EKEYREJECTED: return DRWAV_ERROR;
76223 #endif
76224 #ifdef EOWNERDEAD
76225 case EOWNERDEAD: return DRWAV_ERROR;
76226 #endif
76227 #ifdef ENOTRECOVERABLE
76228 case ENOTRECOVERABLE: return DRWAV_ERROR;
76229 #endif
76230 #ifdef ERFKILL
76231 case ERFKILL: return DRWAV_ERROR;
76232 #endif
76233 #ifdef EHWPOISON
76234 case EHWPOISON: return DRWAV_ERROR;
76235 #endif
76236 default: return DRWAV_ERROR;
76237 }
76238}
76239DRWAV_PRIVATE drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
76240{
76241#if defined(_MSC_VER) && _MSC_VER >= 1400
76242 errno_t err;
76243#endif
76244 if (ppFile != NULL) {
76245 *ppFile = NULL;
76246 }
76247 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
76248 return DRWAV_INVALID_ARGS;
76249 }
76250#if defined(_MSC_VER) && _MSC_VER >= 1400
76251 err = fopen_s(ppFile, pFilePath, pOpenMode);
76252 if (err != 0) {
76253 return drwav_result_from_errno(err);
76254 }
76255#else
76256#if defined(_WIN32) || defined(__APPLE__)
76257 *ppFile = fopen(pFilePath, pOpenMode);
76258#else
76259 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
76260 *ppFile = fopen64(pFilePath, pOpenMode);
76261 #else
76262 *ppFile = fopen(pFilePath, pOpenMode);
76263 #endif
76264#endif
76265 if (*ppFile == NULL) {
76266 drwav_result result = drwav_result_from_errno(errno);
76267 if (result == DRWAV_SUCCESS) {
76268 result = DRWAV_ERROR;
76269 }
76270 return result;
76271 }
76272#endif
76273 return DRWAV_SUCCESS;
76274}
76275#if defined(_WIN32)
76276 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
76277 #define DRWAV_HAS_WFOPEN
76278 #endif
76279#endif
76280DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks)
76281{
76282 if (ppFile != NULL) {
76283 *ppFile = NULL;
76284 }
76285 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
76286 return DRWAV_INVALID_ARGS;
76287 }
76288#if defined(DRWAV_HAS_WFOPEN)
76289 {
76290 #if defined(_MSC_VER) && _MSC_VER >= 1400
76291 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
76292 if (err != 0) {
76293 return drwav_result_from_errno(err);
76294 }
76295 #else
76296 *ppFile = _wfopen(pFilePath, pOpenMode);
76297 if (*ppFile == NULL) {
76298 return drwav_result_from_errno(errno);
76299 }
76300 #endif
76301 (void)pAllocationCallbacks;
76302 }
76303#else
76304 {
76305 mbstate_t mbs;
76306 size_t lenMB;
76307 const wchar_t* pFilePathTemp = pFilePath;
76308 char* pFilePathMB = NULL;
76309 char pOpenModeMB[32] = {0};
76310 DRWAV_ZERO_OBJECT(&mbs);
76311 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
76312 if (lenMB == (size_t)-1) {
76313 return drwav_result_from_errno(errno);
76314 }
76315 pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
76316 if (pFilePathMB == NULL) {
76317 return DRWAV_OUT_OF_MEMORY;
76318 }
76319 pFilePathTemp = pFilePath;
76320 DRWAV_ZERO_OBJECT(&mbs);
76321 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
76322 {
76323 size_t i = 0;
76324 for (;;) {
76325 if (pOpenMode[i] == 0) {
76326 pOpenModeMB[i] = '\0';
76327 break;
76328 }
76329 pOpenModeMB[i] = (char)pOpenMode[i];
76330 i += 1;
76331 }
76332 }
76333 *ppFile = fopen(pFilePathMB, pOpenModeMB);
76334 drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
76335 }
76336 if (*ppFile == NULL) {
76337 return DRWAV_ERROR;
76338 }
76339#endif
76340 return DRWAV_SUCCESS;
76341}
76342DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
76343{
76344 return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
76345}
76346DRWAV_PRIVATE size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite)
76347{
76348 return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData);
76349}
76350DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin)
76351{
76352 return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
76353}
76354DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
76355{
76356 return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
76357}
76358DRWAV_PRIVATE drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFile, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, drwav_metadata_type allowedMetadataTypes, const drwav_allocation_callbacks* pAllocationCallbacks)
76359{
76360 drwav_bool32 result;
76361 result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
76362 if (result != DRWAV_TRUE) {
76363 fclose(pFile);
76364 return result;
76365 }
76366 pWav->allowedMetadataTypes = allowedMetadataTypes;
76367 result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
76368 if (result != DRWAV_TRUE) {
76369 fclose(pFile);
76370 return result;
76371 }
76372 return DRWAV_TRUE;
76373}
76374DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
76375{
76376 FILE* pFile;
76377 if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) {
76378 return DRWAV_FALSE;
76379 }
76380 return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks);
76381}
76382DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
76383{
76384 return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
76385}
76386DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
76387{
76388 FILE* pFile;
76389 if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) {
76390 return DRWAV_FALSE;
76391 }
76392 return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks);
76393}
76394DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
76395{
76396 FILE* pFile;
76397 if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) {
76398 return DRWAV_FALSE;
76399 }
76400 return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks);
76401}
76402DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
76403{
76404 FILE* pFile;
76405 if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) {
76406 return DRWAV_FALSE;
76407 }
76408 return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks);
76409}
76410DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
76411{
76412 drwav_bool32 result;
76413 result = drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
76414 if (result != DRWAV_TRUE) {
76415 fclose(pFile);
76416 return result;
76417 }
76418 result = drwav_init_write__internal(pWav, pFormat, totalSampleCount);
76419 if (result != DRWAV_TRUE) {
76420 fclose(pFile);
76421 return result;
76422 }
76423 return DRWAV_TRUE;
76424}
76425DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
76426{
76427 FILE* pFile;
76428 if (drwav_fopen(&pFile, filename, "wb") != DRWAV_SUCCESS) {
76429 return DRWAV_FALSE;
76430 }
76431 return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
76432}
76433DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
76434{
76435 FILE* pFile;
76436 if (drwav_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != DRWAV_SUCCESS) {
76437 return DRWAV_FALSE;
76438 }
76439 return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
76440}
76441DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
76442{
76443 return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
76444}
76445DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
76446{
76447 return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
76448}
76449DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
76450{
76451 if (pFormat == NULL) {
76452 return DRWAV_FALSE;
76453 }
76454 return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
76455}
76456DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
76457{
76458 return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
76459}
76460DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
76461{
76462 return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
76463}
76464DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
76465{
76466 if (pFormat == NULL) {
76467 return DRWAV_FALSE;
76468 }
76469 return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
76470}
76471#endif
76472DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
76473{
76474 drwav* pWav = (drwav*)pUserData;
76475 size_t bytesRemaining;
76476 DRWAV_ASSERT(pWav != NULL);
76477 DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos);
76478 bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos;
76479 if (bytesToRead > bytesRemaining) {
76480 bytesToRead = bytesRemaining;
76481 }
76482 if (bytesToRead > 0) {
76483 DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead);
76484 pWav->memoryStream.currentReadPos += bytesToRead;
76485 }
76486 return bytesToRead;
76487}
76488DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin)
76489{
76490 drwav* pWav = (drwav*)pUserData;
76491 DRWAV_ASSERT(pWav != NULL);
76492 if (origin == drwav_seek_origin_current) {
76493 if (offset > 0) {
76494 if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) {
76495 return DRWAV_FALSE;
76496 }
76497 } else {
76498 if (pWav->memoryStream.currentReadPos < (size_t)-offset) {
76499 return DRWAV_FALSE;
76500 }
76501 }
76502 pWav->memoryStream.currentReadPos += offset;
76503 } else {
76504 if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) {
76505 pWav->memoryStream.currentReadPos = offset;
76506 } else {
76507 return DRWAV_FALSE;
76508 }
76509 }
76510 return DRWAV_TRUE;
76511}
76512DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)
76513{
76514 drwav* pWav = (drwav*)pUserData;
76515 size_t bytesRemaining;
76516 DRWAV_ASSERT(pWav != NULL);
76518 bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos;
76519 if (bytesRemaining < bytesToWrite) {
76520 void* pNewData;
76521 size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2;
76522 if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) {
76523 newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite;
76524 }
76525 pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks);
76526 if (pNewData == NULL) {
76527 return 0;
76528 }
76529 *pWav->memoryStreamWrite.ppData = pNewData;
76530 pWav->memoryStreamWrite.dataCapacity = newDataCapacity;
76531 }
76532 DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite);
76533 pWav->memoryStreamWrite.currentWritePos += bytesToWrite;
76536 }
76538 return bytesToWrite;
76539}
76540DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin)
76541{
76542 drwav* pWav = (drwav*)pUserData;
76543 DRWAV_ASSERT(pWav != NULL);
76544 if (origin == drwav_seek_origin_current) {
76545 if (offset > 0) {
76546 if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) {
76547 offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos);
76548 }
76549 } else {
76550 if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) {
76551 offset = -(int)pWav->memoryStreamWrite.currentWritePos;
76552 }
76553 }
76554 pWav->memoryStreamWrite.currentWritePos += offset;
76555 } else {
76556 if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) {
76557 pWav->memoryStreamWrite.currentWritePos = offset;
76558 } else {
76560 }
76561 }
76562 return DRWAV_TRUE;
76563}
76564DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks)
76565{
76566 return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks);
76567}
76568DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
76569{
76570 if (data == NULL || dataSize == 0) {
76571 return DRWAV_FALSE;
76572 }
76573 if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) {
76574 return DRWAV_FALSE;
76575 }
76576 pWav->memoryStream.data = (const drwav_uint8*)data;
76577 pWav->memoryStream.dataSize = dataSize;
76578 pWav->memoryStream.currentReadPos = 0;
76579 return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
76580}
76581DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
76582{
76583 if (data == NULL || dataSize == 0) {
76584 return DRWAV_FALSE;
76585 }
76586 if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) {
76587 return DRWAV_FALSE;
76588 }
76589 pWav->memoryStream.data = (const drwav_uint8*)data;
76590 pWav->memoryStream.dataSize = dataSize;
76591 pWav->memoryStream.currentReadPos = 0;
76593 return drwav_init__internal(pWav, NULL, NULL, flags);
76594}
76595DRWAV_PRIVATE drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
76596{
76597 if (ppData == NULL || pDataSize == NULL) {
76598 return DRWAV_FALSE;
76599 }
76600 *ppData = NULL;
76601 *pDataSize = 0;
76602 if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) {
76603 return DRWAV_FALSE;
76604 }
76605 pWav->memoryStreamWrite.ppData = ppData;
76606 pWav->memoryStreamWrite.pDataSize = pDataSize;
76607 pWav->memoryStreamWrite.dataSize = 0;
76610 return drwav_init_write__internal(pWav, pFormat, totalSampleCount);
76611}
76612DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
76613{
76614 return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
76615}
76616DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
76617{
76618 return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
76619}
76620DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
76621{
76622 if (pFormat == NULL) {
76623 return DRWAV_FALSE;
76624 }
76625 return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
76626}
76628{
76629 drwav_result result = DRWAV_SUCCESS;
76630 if (pWav == NULL) {
76631 return DRWAV_INVALID_ARGS;
76632 }
76633 if (pWav->onWrite != NULL) {
76634 drwav_uint32 paddingSize = 0;
76636 paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize);
76637 } else {
76638 paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize);
76639 }
76640 if (paddingSize > 0) {
76641 drwav_uint64 paddingData = 0;
76642 drwav__write(pWav, &paddingData, paddingSize);
76643 }
76644 if (pWav->onSeek && !pWav->isSequentialWrite) {
76645 if (pWav->container == drwav_container_riff) {
76646 if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) {
76647 drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
76648 drwav__write_u32ne_to_le(pWav, riffChunkSize);
76649 }
76650 if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, drwav_seek_origin_start)) {
76651 drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize);
76652 drwav__write_u32ne_to_le(pWav, dataChunkSize);
76653 }
76654 } else if (pWav->container == drwav_container_w64) {
76655 if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) {
76656 drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize);
76657 drwav__write_u64ne_to_le(pWav, riffChunkSize);
76658 }
76659 if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, drwav_seek_origin_start)) {
76660 drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize);
76661 drwav__write_u64ne_to_le(pWav, dataChunkSize);
76662 }
76663 } else if (pWav->container == drwav_container_rf64) {
76664 int ds64BodyPos = 12 + 8;
76665 if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) {
76666 drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
76667 drwav__write_u64ne_to_le(pWav, riffChunkSize);
76668 }
76669 if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) {
76670 drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize);
76671 drwav__write_u64ne_to_le(pWav, dataChunkSize);
76672 }
76673 }
76674 }
76675 if (pWav->isSequentialWrite) {
76677 result = DRWAV_INVALID_FILE;
76678 }
76679 }
76680 } else {
76681 if (pWav->pMetadata != NULL) {
76683 }
76684 }
76685#ifndef DR_WAV_NO_STDIO
76686 if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) {
76687 fclose((FILE*)pWav->pUserData);
76688 }
76689#endif
76690 return result;
76691}
76692DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut)
76693{
76694 size_t bytesRead;
76695 drwav_uint32 bytesPerFrame;
76696 if (pWav == NULL || bytesToRead == 0) {
76697 return 0;
76698 }
76699 if (bytesToRead > pWav->bytesRemaining) {
76700 bytesToRead = (size_t)pWav->bytesRemaining;
76701 }
76702 if (bytesToRead == 0) {
76703 return 0;
76704 }
76705 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
76706 if (bytesPerFrame == 0) {
76707 return 0;
76708 }
76709 if (pBufferOut != NULL) {
76710 bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);
76711 } else {
76712 bytesRead = 0;
76713 while (bytesRead < bytesToRead) {
76714 size_t bytesToSeek = (bytesToRead - bytesRead);
76715 if (bytesToSeek > 0x7FFFFFFF) {
76716 bytesToSeek = 0x7FFFFFFF;
76717 }
76718 if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) {
76719 break;
76720 }
76721 bytesRead += bytesToSeek;
76722 }
76723 while (bytesRead < bytesToRead) {
76724 drwav_uint8 buffer[4096];
76725 size_t bytesSeeked;
76726 size_t bytesToSeek = (bytesToRead - bytesRead);
76727 if (bytesToSeek > sizeof(buffer)) {
76728 bytesToSeek = sizeof(buffer);
76729 }
76730 bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek);
76731 bytesRead += bytesSeeked;
76732 if (bytesSeeked < bytesToSeek) {
76733 break;
76734 }
76735 }
76736 }
76737 pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame;
76738 pWav->bytesRemaining -= bytesRead;
76739 return bytesRead;
76740}
76741DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
76742{
76743 drwav_uint32 bytesPerFrame;
76744 drwav_uint64 bytesToRead;
76745 if (pWav == NULL || framesToRead == 0) {
76746 return 0;
76747 }
76748 if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
76749 return 0;
76750 }
76751 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
76752 if (bytesPerFrame == 0) {
76753 return 0;
76754 }
76755 bytesToRead = framesToRead * bytesPerFrame;
76756 if (bytesToRead > DRWAV_SIZE_MAX) {
76757 bytesToRead = (DRWAV_SIZE_MAX / bytesPerFrame) * bytesPerFrame;
76758 }
76759 if (bytesToRead == 0) {
76760 return 0;
76761 }
76762 return drwav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame;
76763}
76764DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
76765{
76766 drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
76767 if (pBufferOut != NULL) {
76768 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
76769 if (bytesPerFrame == 0) {
76770 return 0;
76771 }
76772 drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels, pWav->translatedFormatTag);
76773 }
76774 return framesRead;
76775}
76776DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
76777{
76778 if (drwav__is_little_endian()) {
76779 return drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
76780 } else {
76781 return drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);
76782 }
76783}
76784DRWAV_PRIVATE drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav)
76785{
76786 if (pWav->onWrite != NULL) {
76787 return DRWAV_FALSE;
76788 }
76789 if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) {
76790 return DRWAV_FALSE;
76791 }
76792 if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
76794 DRWAV_ZERO_OBJECT(&pWav->msadpcm);
76795 } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
76796 DRWAV_ZERO_OBJECT(&pWav->ima);
76797 } else {
76798 DRWAV_ASSERT(DRWAV_FALSE);
76799 }
76800 }
76801 pWav->readCursorInPCMFrames = 0;
76802 pWav->bytesRemaining = pWav->dataChunkDataSize;
76803 return DRWAV_TRUE;
76804}
76806{
76807 if (pWav == NULL || pWav->onSeek == NULL) {
76808 return DRWAV_FALSE;
76809 }
76810 if (pWav->onWrite != NULL) {
76811 return DRWAV_FALSE;
76812 }
76813 if (pWav->totalPCMFrameCount == 0) {
76814 return DRWAV_TRUE;
76815 }
76816 if (targetFrameIndex > pWav->totalPCMFrameCount) {
76817 targetFrameIndex = pWav->totalPCMFrameCount;
76818 }
76819 if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
76820 if (targetFrameIndex < pWav->readCursorInPCMFrames) {
76821 if (!drwav_seek_to_first_pcm_frame(pWav)) {
76822 return DRWAV_FALSE;
76823 }
76824 }
76825 if (targetFrameIndex > pWav->readCursorInPCMFrames) {
76826 drwav_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames;
76827 drwav_int16 devnull[2048];
76828 while (offsetInFrames > 0) {
76829 drwav_uint64 framesRead = 0;
76830 drwav_uint64 framesToRead = offsetInFrames;
76831 if (framesToRead > drwav_countof(devnull)/pWav->channels) {
76832 framesToRead = drwav_countof(devnull)/pWav->channels;
76833 }
76835 framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull);
76836 } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
76837 framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull);
76838 } else {
76839 DRWAV_ASSERT(DRWAV_FALSE);
76840 }
76841 if (framesRead != framesToRead) {
76842 return DRWAV_FALSE;
76843 }
76844 offsetInFrames -= framesRead;
76845 }
76846 }
76847 } else {
76848 drwav_uint64 totalSizeInBytes;
76849 drwav_uint64 currentBytePos;
76850 drwav_uint64 targetBytePos;
76851 drwav_uint64 offset;
76852 drwav_uint32 bytesPerFrame;
76853 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
76854 if (bytesPerFrame == 0) {
76855 return DRWAV_FALSE;
76856 }
76857 totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame;
76858 DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining);
76859 currentBytePos = totalSizeInBytes - pWav->bytesRemaining;
76860 targetBytePos = targetFrameIndex * bytesPerFrame;
76861 if (currentBytePos < targetBytePos) {
76862 offset = (targetBytePos - currentBytePos);
76863 } else {
76864 if (!drwav_seek_to_first_pcm_frame(pWav)) {
76865 return DRWAV_FALSE;
76866 }
76867 offset = targetBytePos;
76868 }
76869 while (offset > 0) {
76870 int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);
76871 if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) {
76872 return DRWAV_FALSE;
76873 }
76874 pWav->readCursorInPCMFrames += offset32 / bytesPerFrame;
76875 pWav->bytesRemaining -= offset32;
76876 offset -= offset32;
76877 }
76878 }
76879 return DRWAV_TRUE;
76880}
76882{
76883 if (pCursor == NULL) {
76884 return DRWAV_INVALID_ARGS;
76885 }
76886 *pCursor = 0;
76887 if (pWav == NULL) {
76888 return DRWAV_INVALID_ARGS;
76889 }
76890 *pCursor = pWav->readCursorInPCMFrames;
76891 return DRWAV_SUCCESS;
76892}
76894{
76895 if (pLength == NULL) {
76896 return DRWAV_INVALID_ARGS;
76897 }
76898 *pLength = 0;
76899 if (pWav == NULL) {
76900 return DRWAV_INVALID_ARGS;
76901 }
76902 *pLength = pWav->totalPCMFrameCount;
76903 return DRWAV_SUCCESS;
76904}
76905DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData)
76906{
76907 size_t bytesWritten;
76908 if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {
76909 return 0;
76910 }
76911 bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);
76912 pWav->dataChunkDataSize += bytesWritten;
76913 return bytesWritten;
76914}
76915DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
76916{
76917 drwav_uint64 bytesToWrite;
76918 drwav_uint64 bytesWritten;
76919 const drwav_uint8* pRunningData;
76920 if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
76921 return 0;
76922 }
76923 bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
76924 if (bytesToWrite > DRWAV_SIZE_MAX) {
76925 return 0;
76926 }
76927 bytesWritten = 0;
76928 pRunningData = (const drwav_uint8*)pData;
76929 while (bytesToWrite > 0) {
76930 size_t bytesJustWritten;
76931 drwav_uint64 bytesToWriteThisIteration;
76932 bytesToWriteThisIteration = bytesToWrite;
76933 DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX);
76934 bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData);
76935 if (bytesJustWritten == 0) {
76936 break;
76937 }
76938 bytesToWrite -= bytesJustWritten;
76939 bytesWritten += bytesJustWritten;
76940 pRunningData += bytesJustWritten;
76941 }
76942 return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
76943}
76944DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
76945{
76946 drwav_uint64 bytesToWrite;
76947 drwav_uint64 bytesWritten;
76948 drwav_uint32 bytesPerSample;
76949 const drwav_uint8* pRunningData;
76950 if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
76951 return 0;
76952 }
76953 bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
76954 if (bytesToWrite > DRWAV_SIZE_MAX) {
76955 return 0;
76956 }
76957 bytesWritten = 0;
76958 pRunningData = (const drwav_uint8*)pData;
76959 bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels;
76960 if (bytesPerSample == 0) {
76961 return 0;
76962 }
76963 while (bytesToWrite > 0) {
76964 drwav_uint8 temp[4096];
76965 drwav_uint32 sampleCount;
76966 size_t bytesJustWritten;
76967 drwav_uint64 bytesToWriteThisIteration;
76968 bytesToWriteThisIteration = bytesToWrite;
76969 DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX);
76970 sampleCount = sizeof(temp)/bytesPerSample;
76971 if (bytesToWriteThisIteration > ((drwav_uint64)sampleCount)*bytesPerSample) {
76972 bytesToWriteThisIteration = ((drwav_uint64)sampleCount)*bytesPerSample;
76973 }
76974 DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration);
76975 drwav__bswap_samples(temp, sampleCount, bytesPerSample, pWav->translatedFormatTag);
76976 bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp);
76977 if (bytesJustWritten == 0) {
76978 break;
76979 }
76980 bytesToWrite -= bytesJustWritten;
76981 bytesWritten += bytesJustWritten;
76982 pRunningData += bytesJustWritten;
76983 }
76984 return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
76985}
76986DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
76987{
76988 if (drwav__is_little_endian()) {
76989 return drwav_write_pcm_frames_le(pWav, framesToWrite, pData);
76990 } else {
76991 return drwav_write_pcm_frames_be(pWav, framesToWrite, pData);
76992 }
76993}
76994DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
76995{
76996 drwav_uint64 totalFramesRead = 0;
76997 DRWAV_ASSERT(pWav != NULL);
76998 DRWAV_ASSERT(framesToRead > 0);
76999 while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
77000 DRWAV_ASSERT(framesToRead > 0);
77001 if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) {
77002 if (pWav->channels == 1) {
77003 drwav_uint8 header[7];
77004 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
77005 return totalFramesRead;
77006 }
77007 pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
77008 pWav->msadpcm.predictor[0] = header[0];
77009 pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 1);
77010 pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 3);
77011 pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 5);
77012 pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0];
77013 pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1];
77014 pWav->msadpcm.cachedFrameCount = 2;
77015 } else {
77016 drwav_uint8 header[14];
77017 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
77018 return totalFramesRead;
77019 }
77020 pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
77021 pWav->msadpcm.predictor[0] = header[0];
77022 pWav->msadpcm.predictor[1] = header[1];
77023 pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 2);
77024 pWav->msadpcm.delta[1] = drwav_bytes_to_s16(header + 4);
77025 pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 6);
77026 pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav_bytes_to_s16(header + 8);
77027 pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 10);
77028 pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav_bytes_to_s16(header + 12);
77029 pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0];
77030 pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0];
77031 pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1];
77032 pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1];
77033 pWav->msadpcm.cachedFrameCount = 2;
77034 }
77035 }
77036 while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
77037 if (pBufferOut != NULL) {
77038 drwav_uint32 iSample = 0;
77039 for (iSample = 0; iSample < pWav->channels; iSample += 1) {
77040 pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample];
77041 }
77042 pBufferOut += pWav->channels;
77043 }
77044 framesToRead -= 1;
77045 totalFramesRead += 1;
77046 pWav->readCursorInPCMFrames += 1;
77047 pWav->msadpcm.cachedFrameCount -= 1;
77048 }
77049 if (framesToRead == 0) {
77050 break;
77051 }
77052 if (pWav->msadpcm.cachedFrameCount == 0) {
77053 if (pWav->msadpcm.bytesRemainingInBlock == 0) {
77054 continue;
77055 } else {
77056 static drwav_int32 adaptationTable[] = {
77057 230, 230, 230, 230, 307, 409, 512, 614,
77058 768, 614, 512, 409, 307, 230, 230, 230
77059 };
77060 static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
77061 static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
77062 drwav_uint8 nibbles;
77063 drwav_int32 nibble0;
77064 drwav_int32 nibble1;
77065 if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {
77066 return totalFramesRead;
77067 }
77068 pWav->msadpcm.bytesRemainingInBlock -= 1;
77069 nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }
77070 nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }
77071 if (pWav->channels == 1) {
77072 drwav_int32 newSample0;
77073 drwav_int32 newSample1;
77074 newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
77075 newSample0 += nibble0 * pWav->msadpcm.delta[0];
77076 newSample0 = drwav_clamp(newSample0, -32768, 32767);
77077 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
77078 if (pWav->msadpcm.delta[0] < 16) {
77079 pWav->msadpcm.delta[0] = 16;
77080 }
77081 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
77082 pWav->msadpcm.prevFrames[0][1] = newSample0;
77083 newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
77084 newSample1 += nibble1 * pWav->msadpcm.delta[0];
77085 newSample1 = drwav_clamp(newSample1, -32768, 32767);
77086 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;
77087 if (pWav->msadpcm.delta[0] < 16) {
77088 pWav->msadpcm.delta[0] = 16;
77089 }
77090 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
77091 pWav->msadpcm.prevFrames[0][1] = newSample1;
77092 pWav->msadpcm.cachedFrames[2] = newSample0;
77093 pWav->msadpcm.cachedFrames[3] = newSample1;
77094 pWav->msadpcm.cachedFrameCount = 2;
77095 } else {
77096 drwav_int32 newSample0;
77097 drwav_int32 newSample1;
77098 newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
77099 newSample0 += nibble0 * pWav->msadpcm.delta[0];
77100 newSample0 = drwav_clamp(newSample0, -32768, 32767);
77101 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
77102 if (pWav->msadpcm.delta[0] < 16) {
77103 pWav->msadpcm.delta[0] = 16;
77104 }
77105 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
77106 pWav->msadpcm.prevFrames[0][1] = newSample0;
77107 newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
77108 newSample1 += nibble1 * pWav->msadpcm.delta[1];
77109 newSample1 = drwav_clamp(newSample1, -32768, 32767);
77110 pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;
77111 if (pWav->msadpcm.delta[1] < 16) {
77112 pWav->msadpcm.delta[1] = 16;
77113 }
77114 pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1];
77115 pWav->msadpcm.prevFrames[1][1] = newSample1;
77116 pWav->msadpcm.cachedFrames[2] = newSample0;
77117 pWav->msadpcm.cachedFrames[3] = newSample1;
77118 pWav->msadpcm.cachedFrameCount = 1;
77119 }
77120 }
77121 }
77122 }
77123 return totalFramesRead;
77124}
77125DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
77126{
77127 drwav_uint64 totalFramesRead = 0;
77128 drwav_uint32 iChannel;
77129 static drwav_int32 indexTable[16] = {
77130 -1, -1, -1, -1, 2, 4, 6, 8,
77131 -1, -1, -1, -1, 2, 4, 6, 8
77132 };
77133 static drwav_int32 stepTable[89] = {
77134 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
77135 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
77136 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
77137 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
77138 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
77139 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
77140 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
77141 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
77142 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
77143 };
77144 DRWAV_ASSERT(pWav != NULL);
77145 DRWAV_ASSERT(framesToRead > 0);
77146 while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
77147 DRWAV_ASSERT(framesToRead > 0);
77148 if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) {
77149 if (pWav->channels == 1) {
77150 drwav_uint8 header[4];
77151 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
77152 return totalFramesRead;
77153 }
77154 pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
77155 if (header[2] >= drwav_countof(stepTable)) {
77157 pWav->ima.bytesRemainingInBlock = 0;
77158 return totalFramesRead;
77159 }
77160 pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0);
77161 pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1);
77162 pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0];
77163 pWav->ima.cachedFrameCount = 1;
77164 } else {
77165 drwav_uint8 header[8];
77166 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
77167 return totalFramesRead;
77168 }
77169 pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
77170 if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) {
77172 pWav->ima.bytesRemainingInBlock = 0;
77173 return totalFramesRead;
77174 }
77175 pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0);
77176 pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1);
77177 pWav->ima.predictor[1] = drwav_bytes_to_s16(header + 4);
77178 pWav->ima.stepIndex[1] = drwav_clamp(header[6], 0, (drwav_int32)drwav_countof(stepTable)-1);
77179 pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0];
77180 pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1];
77181 pWav->ima.cachedFrameCount = 1;
77182 }
77183 }
77184 while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
77185 if (pBufferOut != NULL) {
77186 drwav_uint32 iSample;
77187 for (iSample = 0; iSample < pWav->channels; iSample += 1) {
77188 pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample];
77189 }
77190 pBufferOut += pWav->channels;
77191 }
77192 framesToRead -= 1;
77193 totalFramesRead += 1;
77194 pWav->readCursorInPCMFrames += 1;
77195 pWav->ima.cachedFrameCount -= 1;
77196 }
77197 if (framesToRead == 0) {
77198 break;
77199 }
77200 if (pWav->ima.cachedFrameCount == 0) {
77201 if (pWav->ima.bytesRemainingInBlock == 0) {
77202 continue;
77203 } else {
77204 pWav->ima.cachedFrameCount = 8;
77205 for (iChannel = 0; iChannel < pWav->channels; ++iChannel) {
77206 drwav_uint32 iByte;
77207 drwav_uint8 nibbles[4];
77208 if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) {
77209 pWav->ima.cachedFrameCount = 0;
77210 return totalFramesRead;
77211 }
77212 pWav->ima.bytesRemainingInBlock -= 4;
77213 for (iByte = 0; iByte < 4; ++iByte) {
77214 drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0);
77215 drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4);
77216 drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]];
77217 drwav_int32 predictor = pWav->ima.predictor[iChannel];
77218 drwav_int32 diff = step >> 3;
77219 if (nibble0 & 1) diff += step >> 2;
77220 if (nibble0 & 2) diff += step >> 1;
77221 if (nibble0 & 4) diff += step;
77222 if (nibble0 & 8) diff = -diff;
77223 predictor = drwav_clamp(predictor + diff, -32768, 32767);
77224 pWav->ima.predictor[iChannel] = predictor;
77225 pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1);
77226 pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor;
77227 step = stepTable[pWav->ima.stepIndex[iChannel]];
77228 predictor = pWav->ima.predictor[iChannel];
77229 diff = step >> 3;
77230 if (nibble1 & 1) diff += step >> 2;
77231 if (nibble1 & 2) diff += step >> 1;
77232 if (nibble1 & 4) diff += step;
77233 if (nibble1 & 8) diff = -diff;
77234 predictor = drwav_clamp(predictor + diff, -32768, 32767);
77235 pWav->ima.predictor[iChannel] = predictor;
77236 pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1);
77237 pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor;
77238 }
77239 }
77240 }
77241 }
77242 }
77243 return totalFramesRead;
77244}
77245#ifndef DR_WAV_NO_CONVERSION_API
77246static unsigned short g_drwavAlawTable[256] = {
77247 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
77248 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
77249 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
77250 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,
77251 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,
77252 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,
77253 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,
77254 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,
77255 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,
77256 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,
77257 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,
77258 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,
77259 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,
77260 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,
77261 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,
77262 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
77263};
77264static unsigned short g_drwavMulawTable[256] = {
77265 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
77266 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
77267 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
77268 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,
77269 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,
77270 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,
77271 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,
77272 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,
77273 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,
77274 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,
77275 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,
77276 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,
77277 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,
77278 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,
77279 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,
77280 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
77281};
77282static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn)
77283{
77284 return (short)g_drwavAlawTable[sampleIn];
77285}
77286static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn)
77287{
77288 return (short)g_drwavMulawTable[sampleIn];
77289}
77290DRWAV_PRIVATE void drwav__pcm_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
77291{
77292 size_t i;
77293 if (bytesPerSample == 1) {
77294 drwav_u8_to_s16(pOut, pIn, totalSampleCount);
77295 return;
77296 }
77297 if (bytesPerSample == 2) {
77298 for (i = 0; i < totalSampleCount; ++i) {
77299 *pOut++ = ((const drwav_int16*)pIn)[i];
77300 }
77301 return;
77302 }
77303 if (bytesPerSample == 3) {
77304 drwav_s24_to_s16(pOut, pIn, totalSampleCount);
77305 return;
77306 }
77307 if (bytesPerSample == 4) {
77308 drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount);
77309 return;
77310 }
77311 if (bytesPerSample > 8) {
77312 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
77313 return;
77314 }
77315 for (i = 0; i < totalSampleCount; ++i) {
77316 drwav_uint64 sample = 0;
77317 unsigned int shift = (8 - bytesPerSample) * 8;
77318 unsigned int j;
77319 for (j = 0; j < bytesPerSample; j += 1) {
77320 DRWAV_ASSERT(j < 8);
77321 sample |= (drwav_uint64)(pIn[j]) << shift;
77322 shift += 8;
77323 }
77324 pIn += j;
77325 *pOut++ = (drwav_int16)((drwav_int64)sample >> 48);
77326 }
77327}
77328DRWAV_PRIVATE void drwav__ieee_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
77329{
77330 if (bytesPerSample == 4) {
77331 drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount);
77332 return;
77333 } else if (bytesPerSample == 8) {
77334 drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount);
77335 return;
77336 } else {
77337 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
77338 return;
77339 }
77340}
77341DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
77342{
77343 drwav_uint64 totalFramesRead;
77344 drwav_uint8 sampleData[4096] = {0};
77345 drwav_uint32 bytesPerFrame;
77346 drwav_uint32 bytesPerSample;
77347 drwav_uint64 samplesRead;
77348 if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) {
77349 return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
77350 }
77351 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
77352 if (bytesPerFrame == 0) {
77353 return 0;
77354 }
77355 bytesPerSample = bytesPerFrame / pWav->channels;
77356 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
77357 return 0;
77358 }
77359 totalFramesRead = 0;
77360 while (framesToRead > 0) {
77361 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
77362 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
77363 if (framesRead == 0) {
77364 break;
77365 }
77366 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
77367 samplesRead = framesRead * pWav->channels;
77368 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
77369 DRWAV_ASSERT(DRWAV_FALSE);
77370 break;
77371 }
77372 drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
77373 pBufferOut += samplesRead;
77374 framesToRead -= framesRead;
77375 totalFramesRead += framesRead;
77376 }
77377 return totalFramesRead;
77378}
77379DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
77380{
77381 drwav_uint64 totalFramesRead;
77382 drwav_uint8 sampleData[4096] = {0};
77383 drwav_uint32 bytesPerFrame;
77384 drwav_uint32 bytesPerSample;
77385 drwav_uint64 samplesRead;
77386 if (pBufferOut == NULL) {
77387 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
77388 }
77389 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
77390 if (bytesPerFrame == 0) {
77391 return 0;
77392 }
77393 bytesPerSample = bytesPerFrame / pWav->channels;
77394 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
77395 return 0;
77396 }
77397 totalFramesRead = 0;
77398 while (framesToRead > 0) {
77399 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
77400 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
77401 if (framesRead == 0) {
77402 break;
77403 }
77404 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
77405 samplesRead = framesRead * pWav->channels;
77406 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
77407 DRWAV_ASSERT(DRWAV_FALSE);
77408 break;
77409 }
77410 drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
77411 pBufferOut += samplesRead;
77412 framesToRead -= framesRead;
77413 totalFramesRead += framesRead;
77414 }
77415 return totalFramesRead;
77416}
77417DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
77418{
77419 drwav_uint64 totalFramesRead;
77420 drwav_uint8 sampleData[4096] = {0};
77421 drwav_uint32 bytesPerFrame;
77422 drwav_uint32 bytesPerSample;
77423 drwav_uint64 samplesRead;
77424 if (pBufferOut == NULL) {
77425 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
77426 }
77427 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
77428 if (bytesPerFrame == 0) {
77429 return 0;
77430 }
77431 bytesPerSample = bytesPerFrame / pWav->channels;
77432 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
77433 return 0;
77434 }
77435 totalFramesRead = 0;
77436 while (framesToRead > 0) {
77437 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
77438 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
77439 if (framesRead == 0) {
77440 break;
77441 }
77442 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
77443 samplesRead = framesRead * pWav->channels;
77444 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
77445 DRWAV_ASSERT(DRWAV_FALSE);
77446 break;
77447 }
77448 drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
77449 pBufferOut += samplesRead;
77450 framesToRead -= framesRead;
77451 totalFramesRead += framesRead;
77452 }
77453 return totalFramesRead;
77454}
77455DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
77456{
77457 drwav_uint64 totalFramesRead;
77458 drwav_uint8 sampleData[4096] = {0};
77459 drwav_uint32 bytesPerFrame;
77460 drwav_uint32 bytesPerSample;
77461 drwav_uint64 samplesRead;
77462 if (pBufferOut == NULL) {
77463 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
77464 }
77465 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
77466 if (bytesPerFrame == 0) {
77467 return 0;
77468 }
77469 bytesPerSample = bytesPerFrame / pWav->channels;
77470 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
77471 return 0;
77472 }
77473 totalFramesRead = 0;
77474 while (framesToRead > 0) {
77475 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
77476 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
77477 if (framesRead == 0) {
77478 break;
77479 }
77480 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
77481 samplesRead = framesRead * pWav->channels;
77482 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
77483 DRWAV_ASSERT(DRWAV_FALSE);
77484 break;
77485 }
77486 drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
77487 pBufferOut += samplesRead;
77488 framesToRead -= framesRead;
77489 totalFramesRead += framesRead;
77490 }
77491 return totalFramesRead;
77492}
77494{
77495 if (pWav == NULL || framesToRead == 0) {
77496 return 0;
77497 }
77498 if (pBufferOut == NULL) {
77499 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
77500 }
77501 if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) {
77502 framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels;
77503 }
77505 return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut);
77506 }
77508 return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut);
77509 }
77511 return drwav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut);
77512 }
77514 return drwav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut);
77515 }
77517 return drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut);
77518 }
77520 return drwav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut);
77521 }
77522 return 0;
77523}
77525{
77526 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
77527 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
77528 drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
77529 }
77530 return framesRead;
77531}
77533{
77534 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
77535 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
77536 drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
77537 }
77538 return framesRead;
77539}
77540DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
77541{
77542 int r;
77543 size_t i;
77544 for (i = 0; i < sampleCount; ++i) {
77545 int x = pIn[i];
77546 r = x << 8;
77547 r = r - 32768;
77548 pOut[i] = (short)r;
77549 }
77550}
77551DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
77552{
77553 int r;
77554 size_t i;
77555 for (i = 0; i < sampleCount; ++i) {
77556 int x = ((int)(((unsigned int)(((const drwav_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+2])) << 24)) >> 8;
77557 r = x >> 8;
77558 pOut[i] = (short)r;
77559 }
77560}
77561DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount)
77562{
77563 int r;
77564 size_t i;
77565 for (i = 0; i < sampleCount; ++i) {
77566 int x = pIn[i];
77567 r = x >> 16;
77568 pOut[i] = (short)r;
77569 }
77570}
77571DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount)
77572{
77573 int r;
77574 size_t i;
77575 for (i = 0; i < sampleCount; ++i) {
77576 float x = pIn[i];
77577 float c;
77578 c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
77579 c = c + 1;
77580 r = (int)(c * 32767.5f);
77581 r = r - 32768;
77582 pOut[i] = (short)r;
77583 }
77584}
77585DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount)
77586{
77587 int r;
77588 size_t i;
77589 for (i = 0; i < sampleCount; ++i) {
77590 double x = pIn[i];
77591 double c;
77592 c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
77593 c = c + 1;
77594 r = (int)(c * 32767.5);
77595 r = r - 32768;
77596 pOut[i] = (short)r;
77597 }
77598}
77599DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
77600{
77601 size_t i;
77602 for (i = 0; i < sampleCount; ++i) {
77603 pOut[i] = drwav__alaw_to_s16(pIn[i]);
77604 }
77605}
77606DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
77607{
77608 size_t i;
77609 for (i = 0; i < sampleCount; ++i) {
77610 pOut[i] = drwav__mulaw_to_s16(pIn[i]);
77611 }
77612}
77613DRWAV_PRIVATE void drwav__pcm_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
77614{
77615 unsigned int i;
77616 if (bytesPerSample == 1) {
77617 drwav_u8_to_f32(pOut, pIn, sampleCount);
77618 return;
77619 }
77620 if (bytesPerSample == 2) {
77621 drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount);
77622 return;
77623 }
77624 if (bytesPerSample == 3) {
77625 drwav_s24_to_f32(pOut, pIn, sampleCount);
77626 return;
77627 }
77628 if (bytesPerSample == 4) {
77629 drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount);
77630 return;
77631 }
77632 if (bytesPerSample > 8) {
77633 DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
77634 return;
77635 }
77636 for (i = 0; i < sampleCount; ++i) {
77637 drwav_uint64 sample = 0;
77638 unsigned int shift = (8 - bytesPerSample) * 8;
77639 unsigned int j;
77640 for (j = 0; j < bytesPerSample; j += 1) {
77641 DRWAV_ASSERT(j < 8);
77642 sample |= (drwav_uint64)(pIn[j]) << shift;
77643 shift += 8;
77644 }
77645 pIn += j;
77646 *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0);
77647 }
77648}
77649DRWAV_PRIVATE void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
77650{
77651 if (bytesPerSample == 4) {
77652 unsigned int i;
77653 for (i = 0; i < sampleCount; ++i) {
77654 *pOut++ = ((const float*)pIn)[i];
77655 }
77656 return;
77657 } else if (bytesPerSample == 8) {
77658 drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount);
77659 return;
77660 } else {
77661 DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
77662 return;
77663 }
77664}
77665DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
77666{
77667 drwav_uint64 totalFramesRead;
77668 drwav_uint8 sampleData[4096] = {0};
77669 drwav_uint32 bytesPerFrame;
77670 drwav_uint32 bytesPerSample;
77671 drwav_uint64 samplesRead;
77672 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
77673 if (bytesPerFrame == 0) {
77674 return 0;
77675 }
77676 bytesPerSample = bytesPerFrame / pWav->channels;
77677 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
77678 return 0;
77679 }
77680 totalFramesRead = 0;
77681 while (framesToRead > 0) {
77682 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
77683 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
77684 if (framesRead == 0) {
77685 break;
77686 }
77687 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
77688 samplesRead = framesRead * pWav->channels;
77689 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
77690 DRWAV_ASSERT(DRWAV_FALSE);
77691 break;
77692 }
77693 drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
77694 pBufferOut += samplesRead;
77695 framesToRead -= framesRead;
77696 totalFramesRead += framesRead;
77697 }
77698 return totalFramesRead;
77699}
77700DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
77701{
77702 drwav_uint64 totalFramesRead;
77703 drwav_int16 samples16[2048];
77704 totalFramesRead = 0;
77705 while (framesToRead > 0) {
77706 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels);
77707 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16);
77708 if (framesRead == 0) {
77709 break;
77710 }
77711 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
77712 drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
77713 pBufferOut += framesRead*pWav->channels;
77714 framesToRead -= framesRead;
77715 totalFramesRead += framesRead;
77716 }
77717 return totalFramesRead;
77718}
77719DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
77720{
77721 drwav_uint64 totalFramesRead;
77722 drwav_uint8 sampleData[4096] = {0};
77723 drwav_uint32 bytesPerFrame;
77724 drwav_uint32 bytesPerSample;
77725 drwav_uint64 samplesRead;
77726 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) {
77727 return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
77728 }
77729 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
77730 if (bytesPerFrame == 0) {
77731 return 0;
77732 }
77733 bytesPerSample = bytesPerFrame / pWav->channels;
77734 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
77735 return 0;
77736 }
77737 totalFramesRead = 0;
77738 while (framesToRead > 0) {
77739 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
77740 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
77741 if (framesRead == 0) {
77742 break;
77743 }
77744 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
77745 samplesRead = framesRead * pWav->channels;
77746 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
77747 DRWAV_ASSERT(DRWAV_FALSE);
77748 break;
77749 }
77750 drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
77751 pBufferOut += samplesRead;
77752 framesToRead -= framesRead;
77753 totalFramesRead += framesRead;
77754 }
77755 return totalFramesRead;
77756}
77757DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
77758{
77759 drwav_uint64 totalFramesRead;
77760 drwav_uint8 sampleData[4096] = {0};
77761 drwav_uint32 bytesPerFrame;
77762 drwav_uint32 bytesPerSample;
77763 drwav_uint64 samplesRead;
77764 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
77765 if (bytesPerFrame == 0) {
77766 return 0;
77767 }
77768 bytesPerSample = bytesPerFrame / pWav->channels;
77769 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
77770 return 0;
77771 }
77772 totalFramesRead = 0;
77773 while (framesToRead > 0) {
77774 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
77775 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
77776 if (framesRead == 0) {
77777 break;
77778 }
77779 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
77780 samplesRead = framesRead * pWav->channels;
77781 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
77782 DRWAV_ASSERT(DRWAV_FALSE);
77783 break;
77784 }
77785 drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
77786 pBufferOut += samplesRead;
77787 framesToRead -= framesRead;
77788 totalFramesRead += framesRead;
77789 }
77790 return totalFramesRead;
77791}
77792DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
77793{
77794 drwav_uint64 totalFramesRead;
77795 drwav_uint8 sampleData[4096] = {0};
77796 drwav_uint32 bytesPerFrame;
77797 drwav_uint32 bytesPerSample;
77798 drwav_uint64 samplesRead;
77799 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
77800 if (bytesPerFrame == 0) {
77801 return 0;
77802 }
77803 bytesPerSample = bytesPerFrame / pWav->channels;
77804 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
77805 return 0;
77806 }
77807 totalFramesRead = 0;
77808 while (framesToRead > 0) {
77809 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
77810 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
77811 if (framesRead == 0) {
77812 break;
77813 }
77814 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
77815 samplesRead = framesRead * pWav->channels;
77816 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
77817 DRWAV_ASSERT(DRWAV_FALSE);
77818 break;
77819 }
77820 drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
77821 pBufferOut += samplesRead;
77822 framesToRead -= framesRead;
77823 totalFramesRead += framesRead;
77824 }
77825 return totalFramesRead;
77826}
77827DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
77828{
77829 if (pWav == NULL || framesToRead == 0) {
77830 return 0;
77831 }
77832 if (pBufferOut == NULL) {
77833 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
77834 }
77835 if (framesToRead * pWav->channels * sizeof(float) > DRWAV_SIZE_MAX) {
77836 framesToRead = DRWAV_SIZE_MAX / sizeof(float) / pWav->channels;
77837 }
77839 return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut);
77840 }
77842 return drwav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut);
77843 }
77845 return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut);
77846 }
77848 return drwav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut);
77849 }
77851 return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut);
77852 }
77853 return 0;
77854}
77855DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
77856{
77857 drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
77858 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
77859 drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
77860 }
77861 return framesRead;
77862}
77863DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
77864{
77865 drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
77866 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
77867 drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
77868 }
77869 return framesRead;
77870}
77871DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
77872{
77873 size_t i;
77874 if (pOut == NULL || pIn == NULL) {
77875 return;
77876 }
77877#ifdef DR_WAV_LIBSNDFILE_COMPAT
77878 for (i = 0; i < sampleCount; ++i) {
77879 *pOut++ = (pIn[i] / 256.0f) * 2 - 1;
77880 }
77881#else
77882 for (i = 0; i < sampleCount; ++i) {
77883 float x = pIn[i];
77884 x = x * 0.00784313725490196078f;
77885 x = x - 1;
77886 *pOut++ = x;
77887 }
77888#endif
77889}
77890DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount)
77891{
77892 size_t i;
77893 if (pOut == NULL || pIn == NULL) {
77894 return;
77895 }
77896 for (i = 0; i < sampleCount; ++i) {
77897 *pOut++ = pIn[i] * 0.000030517578125f;
77898 }
77899}
77900DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
77901{
77902 size_t i;
77903 if (pOut == NULL || pIn == NULL) {
77904 return;
77905 }
77906 for (i = 0; i < sampleCount; ++i) {
77907 double x;
77908 drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) << 8);
77909 drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16);
77910 drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24);
77911 x = (double)((drwav_int32)(a | b | c) >> 8);
77912 *pOut++ = (float)(x * 0.00000011920928955078125);
77913 }
77914}
77915DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount)
77916{
77917 size_t i;
77918 if (pOut == NULL || pIn == NULL) {
77919 return;
77920 }
77921 for (i = 0; i < sampleCount; ++i) {
77922 *pOut++ = (float)(pIn[i] / 2147483648.0);
77923 }
77924}
77925DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount)
77926{
77927 size_t i;
77928 if (pOut == NULL || pIn == NULL) {
77929 return;
77930 }
77931 for (i = 0; i < sampleCount; ++i) {
77932 *pOut++ = (float)pIn[i];
77933 }
77934}
77935DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
77936{
77937 size_t i;
77938 if (pOut == NULL || pIn == NULL) {
77939 return;
77940 }
77941 for (i = 0; i < sampleCount; ++i) {
77942 *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f;
77943 }
77944}
77945DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
77946{
77947 size_t i;
77948 if (pOut == NULL || pIn == NULL) {
77949 return;
77950 }
77951 for (i = 0; i < sampleCount; ++i) {
77952 *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f;
77953 }
77954}
77955DRWAV_PRIVATE void drwav__pcm_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
77956{
77957 unsigned int i;
77958 if (bytesPerSample == 1) {
77959 drwav_u8_to_s32(pOut, pIn, totalSampleCount);
77960 return;
77961 }
77962 if (bytesPerSample == 2) {
77963 drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount);
77964 return;
77965 }
77966 if (bytesPerSample == 3) {
77967 drwav_s24_to_s32(pOut, pIn, totalSampleCount);
77968 return;
77969 }
77970 if (bytesPerSample == 4) {
77971 for (i = 0; i < totalSampleCount; ++i) {
77972 *pOut++ = ((const drwav_int32*)pIn)[i];
77973 }
77974 return;
77975 }
77976 if (bytesPerSample > 8) {
77977 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
77978 return;
77979 }
77980 for (i = 0; i < totalSampleCount; ++i) {
77981 drwav_uint64 sample = 0;
77982 unsigned int shift = (8 - bytesPerSample) * 8;
77983 unsigned int j;
77984 for (j = 0; j < bytesPerSample; j += 1) {
77985 DRWAV_ASSERT(j < 8);
77986 sample |= (drwav_uint64)(pIn[j]) << shift;
77987 shift += 8;
77988 }
77989 pIn += j;
77990 *pOut++ = (drwav_int32)((drwav_int64)sample >> 32);
77991 }
77992}
77993DRWAV_PRIVATE void drwav__ieee_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
77994{
77995 if (bytesPerSample == 4) {
77996 drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount);
77997 return;
77998 } else if (bytesPerSample == 8) {
77999 drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount);
78000 return;
78001 } else {
78002 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
78003 return;
78004 }
78005}
78006DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
78007{
78008 drwav_uint64 totalFramesRead;
78009 drwav_uint8 sampleData[4096] = {0};
78010 drwav_uint32 bytesPerFrame;
78011 drwav_uint32 bytesPerSample;
78012 drwav_uint64 samplesRead;
78013 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) {
78014 return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
78015 }
78016 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
78017 if (bytesPerFrame == 0) {
78018 return 0;
78019 }
78020 bytesPerSample = bytesPerFrame / pWav->channels;
78021 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
78022 return 0;
78023 }
78024 totalFramesRead = 0;
78025 while (framesToRead > 0) {
78026 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
78027 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
78028 if (framesRead == 0) {
78029 break;
78030 }
78031 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
78032 samplesRead = framesRead * pWav->channels;
78033 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
78034 DRWAV_ASSERT(DRWAV_FALSE);
78035 break;
78036 }
78037 drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
78038 pBufferOut += samplesRead;
78039 framesToRead -= framesRead;
78040 totalFramesRead += framesRead;
78041 }
78042 return totalFramesRead;
78043}
78044DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
78045{
78046 drwav_uint64 totalFramesRead = 0;
78047 drwav_int16 samples16[2048];
78048 while (framesToRead > 0) {
78049 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels);
78050 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16);
78051 if (framesRead == 0) {
78052 break;
78053 }
78054 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
78055 drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
78056 pBufferOut += framesRead*pWav->channels;
78057 framesToRead -= framesRead;
78058 totalFramesRead += framesRead;
78059 }
78060 return totalFramesRead;
78061}
78062DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
78063{
78064 drwav_uint64 totalFramesRead;
78065 drwav_uint8 sampleData[4096] = {0};
78066 drwav_uint32 bytesPerFrame;
78067 drwav_uint32 bytesPerSample;
78068 drwav_uint64 samplesRead;
78069 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
78070 if (bytesPerFrame == 0) {
78071 return 0;
78072 }
78073 bytesPerSample = bytesPerFrame / pWav->channels;
78074 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
78075 return 0;
78076 }
78077 totalFramesRead = 0;
78078 while (framesToRead > 0) {
78079 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
78080 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
78081 if (framesRead == 0) {
78082 break;
78083 }
78084 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
78085 samplesRead = framesRead * pWav->channels;
78086 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
78087 DRWAV_ASSERT(DRWAV_FALSE);
78088 break;
78089 }
78090 drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
78091 pBufferOut += samplesRead;
78092 framesToRead -= framesRead;
78093 totalFramesRead += framesRead;
78094 }
78095 return totalFramesRead;
78096}
78097DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
78098{
78099 drwav_uint64 totalFramesRead;
78100 drwav_uint8 sampleData[4096] = {0};
78101 drwav_uint32 bytesPerFrame;
78102 drwav_uint32 bytesPerSample;
78103 drwav_uint64 samplesRead;
78104 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
78105 if (bytesPerFrame == 0) {
78106 return 0;
78107 }
78108 bytesPerSample = bytesPerFrame / pWav->channels;
78109 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
78110 return 0;
78111 }
78112 totalFramesRead = 0;
78113 while (framesToRead > 0) {
78114 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
78115 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
78116 if (framesRead == 0) {
78117 break;
78118 }
78119 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
78120 samplesRead = framesRead * pWav->channels;
78121 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
78122 DRWAV_ASSERT(DRWAV_FALSE);
78123 break;
78124 }
78125 drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
78126 pBufferOut += samplesRead;
78127 framesToRead -= framesRead;
78128 totalFramesRead += framesRead;
78129 }
78130 return totalFramesRead;
78131}
78132DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
78133{
78134 drwav_uint64 totalFramesRead;
78135 drwav_uint8 sampleData[4096] = {0};
78136 drwav_uint32 bytesPerFrame;
78137 drwav_uint32 bytesPerSample;
78138 drwav_uint64 samplesRead;
78139 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
78140 if (bytesPerFrame == 0) {
78141 return 0;
78142 }
78143 bytesPerSample = bytesPerFrame / pWav->channels;
78144 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
78145 return 0;
78146 }
78147 totalFramesRead = 0;
78148 while (framesToRead > 0) {
78149 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
78150 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
78151 if (framesRead == 0) {
78152 break;
78153 }
78154 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
78155 samplesRead = framesRead * pWav->channels;
78156 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
78157 DRWAV_ASSERT(DRWAV_FALSE);
78158 break;
78159 }
78160 drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
78161 pBufferOut += samplesRead;
78162 framesToRead -= framesRead;
78163 totalFramesRead += framesRead;
78164 }
78165 return totalFramesRead;
78166}
78168{
78169 if (pWav == NULL || framesToRead == 0) {
78170 return 0;
78171 }
78172 if (pBufferOut == NULL) {
78173 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
78174 }
78175 if (framesToRead * pWav->channels * sizeof(drwav_int32) > DRWAV_SIZE_MAX) {
78176 framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32) / pWav->channels;
78177 }
78179 return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut);
78180 }
78182 return drwav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut);
78183 }
78185 return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut);
78186 }
78188 return drwav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut);
78189 }
78191 return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut);
78192 }
78193 return 0;
78194}
78196{
78197 drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
78198 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
78199 drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
78200 }
78201 return framesRead;
78202}
78204{
78205 drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
78206 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
78207 drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
78208 }
78209 return framesRead;
78210}
78211DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
78212{
78213 size_t i;
78214 if (pOut == NULL || pIn == NULL) {
78215 return;
78216 }
78217 for (i = 0; i < sampleCount; ++i) {
78218 *pOut++ = ((int)pIn[i] - 128) << 24;
78219 }
78220}
78221DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount)
78222{
78223 size_t i;
78224 if (pOut == NULL || pIn == NULL) {
78225 return;
78226 }
78227 for (i = 0; i < sampleCount; ++i) {
78228 *pOut++ = pIn[i] << 16;
78229 }
78230}
78231DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
78232{
78233 size_t i;
78234 if (pOut == NULL || pIn == NULL) {
78235 return;
78236 }
78237 for (i = 0; i < sampleCount; ++i) {
78238 unsigned int s0 = pIn[i*3 + 0];
78239 unsigned int s1 = pIn[i*3 + 1];
78240 unsigned int s2 = pIn[i*3 + 2];
78241 drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));
78242 *pOut++ = sample32;
78243 }
78244}
78245DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount)
78246{
78247 size_t i;
78248 if (pOut == NULL || pIn == NULL) {
78249 return;
78250 }
78251 for (i = 0; i < sampleCount; ++i) {
78252 *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
78253 }
78254}
78255DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount)
78256{
78257 size_t i;
78258 if (pOut == NULL || pIn == NULL) {
78259 return;
78260 }
78261 for (i = 0; i < sampleCount; ++i) {
78262 *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
78263 }
78264}
78265DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
78266{
78267 size_t i;
78268 if (pOut == NULL || pIn == NULL) {
78269 return;
78270 }
78271 for (i = 0; i < sampleCount; ++i) {
78272 *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16;
78273 }
78274}
78275DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
78276{
78277 size_t i;
78278 if (pOut == NULL || pIn == NULL) {
78279 return;
78280 }
78281 for (i= 0; i < sampleCount; ++i) {
78282 *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16;
78283 }
78284}
78285DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
78286{
78287 drwav_uint64 sampleDataSize;
78288 drwav_int16* pSampleData;
78289 drwav_uint64 framesRead;
78290 DRWAV_ASSERT(pWav != NULL);
78291 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16);
78292 if (sampleDataSize > DRWAV_SIZE_MAX) {
78293 drwav_uninit(pWav);
78294 return NULL;
78295 }
78296 pSampleData = (drwav_int16*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
78297 if (pSampleData == NULL) {
78298 drwav_uninit(pWav);
78299 return NULL;
78300 }
78301 framesRead = drwav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
78302 if (framesRead != pWav->totalPCMFrameCount) {
78303 drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
78304 drwav_uninit(pWav);
78305 return NULL;
78306 }
78307 drwav_uninit(pWav);
78308 if (sampleRate) {
78309 *sampleRate = pWav->sampleRate;
78310 }
78311 if (channels) {
78312 *channels = pWav->channels;
78313 }
78314 if (totalFrameCount) {
78315 *totalFrameCount = pWav->totalPCMFrameCount;
78316 }
78317 return pSampleData;
78318}
78319DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
78320{
78321 drwav_uint64 sampleDataSize;
78322 float* pSampleData;
78323 drwav_uint64 framesRead;
78324 DRWAV_ASSERT(pWav != NULL);
78325 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float);
78326 if (sampleDataSize > DRWAV_SIZE_MAX) {
78327 drwav_uninit(pWav);
78328 return NULL;
78329 }
78330 pSampleData = (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
78331 if (pSampleData == NULL) {
78332 drwav_uninit(pWav);
78333 return NULL;
78334 }
78335 framesRead = drwav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
78336 if (framesRead != pWav->totalPCMFrameCount) {
78337 drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
78338 drwav_uninit(pWav);
78339 return NULL;
78340 }
78341 drwav_uninit(pWav);
78342 if (sampleRate) {
78343 *sampleRate = pWav->sampleRate;
78344 }
78345 if (channels) {
78346 *channels = pWav->channels;
78347 }
78348 if (totalFrameCount) {
78349 *totalFrameCount = pWav->totalPCMFrameCount;
78350 }
78351 return pSampleData;
78352}
78353DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
78354{
78355 drwav_uint64 sampleDataSize;
78356 drwav_int32* pSampleData;
78357 drwav_uint64 framesRead;
78358 DRWAV_ASSERT(pWav != NULL);
78359 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32);
78360 if (sampleDataSize > DRWAV_SIZE_MAX) {
78361 drwav_uninit(pWav);
78362 return NULL;
78363 }
78364 pSampleData = (drwav_int32*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
78365 if (pSampleData == NULL) {
78366 drwav_uninit(pWav);
78367 return NULL;
78368 }
78369 framesRead = drwav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
78370 if (framesRead != pWav->totalPCMFrameCount) {
78371 drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
78372 drwav_uninit(pWav);
78373 return NULL;
78374 }
78375 drwav_uninit(pWav);
78376 if (sampleRate) {
78377 *sampleRate = pWav->sampleRate;
78378 }
78379 if (channels) {
78380 *channels = pWav->channels;
78381 }
78382 if (totalFrameCount) {
78383 *totalFrameCount = pWav->totalPCMFrameCount;
78384 }
78385 return pSampleData;
78386}
78387DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
78388{
78389 drwav wav;
78390 if (channelsOut) {
78391 *channelsOut = 0;
78392 }
78393 if (sampleRateOut) {
78394 *sampleRateOut = 0;
78395 }
78396 if (totalFrameCountOut) {
78397 *totalFrameCountOut = 0;
78398 }
78399 if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
78400 return NULL;
78401 }
78402 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
78403}
78404DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
78405{
78406 drwav wav;
78407 if (channelsOut) {
78408 *channelsOut = 0;
78409 }
78410 if (sampleRateOut) {
78411 *sampleRateOut = 0;
78412 }
78413 if (totalFrameCountOut) {
78414 *totalFrameCountOut = 0;
78415 }
78416 if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
78417 return NULL;
78418 }
78419 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
78420}
78421DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
78422{
78423 drwav wav;
78424 if (channelsOut) {
78425 *channelsOut = 0;
78426 }
78427 if (sampleRateOut) {
78428 *sampleRateOut = 0;
78429 }
78430 if (totalFrameCountOut) {
78431 *totalFrameCountOut = 0;
78432 }
78433 if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
78434 return NULL;
78435 }
78436 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
78437}
78438#ifndef DR_WAV_NO_STDIO
78439DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
78440{
78441 drwav wav;
78442 if (channelsOut) {
78443 *channelsOut = 0;
78444 }
78445 if (sampleRateOut) {
78446 *sampleRateOut = 0;
78447 }
78448 if (totalFrameCountOut) {
78449 *totalFrameCountOut = 0;
78450 }
78451 if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
78452 return NULL;
78453 }
78454 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
78455}
78456DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
78457{
78458 drwav wav;
78459 if (channelsOut) {
78460 *channelsOut = 0;
78461 }
78462 if (sampleRateOut) {
78463 *sampleRateOut = 0;
78464 }
78465 if (totalFrameCountOut) {
78466 *totalFrameCountOut = 0;
78467 }
78468 if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
78469 return NULL;
78470 }
78471 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
78472}
78473DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
78474{
78475 drwav wav;
78476 if (channelsOut) {
78477 *channelsOut = 0;
78478 }
78479 if (sampleRateOut) {
78480 *sampleRateOut = 0;
78481 }
78482 if (totalFrameCountOut) {
78483 *totalFrameCountOut = 0;
78484 }
78485 if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
78486 return NULL;
78487 }
78488 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
78489}
78490DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
78491{
78492 drwav wav;
78493 if (sampleRateOut) {
78494 *sampleRateOut = 0;
78495 }
78496 if (channelsOut) {
78497 *channelsOut = 0;
78498 }
78499 if (totalFrameCountOut) {
78500 *totalFrameCountOut = 0;
78501 }
78502 if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
78503 return NULL;
78504 }
78505 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
78506}
78507DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
78508{
78509 drwav wav;
78510 if (sampleRateOut) {
78511 *sampleRateOut = 0;
78512 }
78513 if (channelsOut) {
78514 *channelsOut = 0;
78515 }
78516 if (totalFrameCountOut) {
78517 *totalFrameCountOut = 0;
78518 }
78519 if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
78520 return NULL;
78521 }
78522 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
78523}
78524DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
78525{
78526 drwav wav;
78527 if (sampleRateOut) {
78528 *sampleRateOut = 0;
78529 }
78530 if (channelsOut) {
78531 *channelsOut = 0;
78532 }
78533 if (totalFrameCountOut) {
78534 *totalFrameCountOut = 0;
78535 }
78536 if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
78537 return NULL;
78538 }
78539 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
78540}
78541#endif
78542DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
78543{
78544 drwav wav;
78545 if (channelsOut) {
78546 *channelsOut = 0;
78547 }
78548 if (sampleRateOut) {
78549 *sampleRateOut = 0;
78550 }
78551 if (totalFrameCountOut) {
78552 *totalFrameCountOut = 0;
78553 }
78554 if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
78555 return NULL;
78556 }
78557 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
78558}
78559DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
78560{
78561 drwav wav;
78562 if (channelsOut) {
78563 *channelsOut = 0;
78564 }
78565 if (sampleRateOut) {
78566 *sampleRateOut = 0;
78567 }
78568 if (totalFrameCountOut) {
78569 *totalFrameCountOut = 0;
78570 }
78571 if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
78572 return NULL;
78573 }
78574 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
78575}
78576DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
78577{
78578 drwav wav;
78579 if (channelsOut) {
78580 *channelsOut = 0;
78581 }
78582 if (sampleRateOut) {
78583 *sampleRateOut = 0;
78584 }
78585 if (totalFrameCountOut) {
78586 *totalFrameCountOut = 0;
78587 }
78588 if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
78589 return NULL;
78590 }
78591 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
78592}
78593#endif
78594DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks)
78595{
78596 if (pAllocationCallbacks != NULL) {
78597 drwav__free_from_callbacks(p, pAllocationCallbacks);
78598 } else {
78599 drwav__free_default(p, NULL);
78600 }
78601}
78603{
78604 return ((drwav_uint16)data[0] << 0) | ((drwav_uint16)data[1] << 8);
78605}
78607{
78608 return (drwav_int16)drwav_bytes_to_u16(data);
78609}
78611{
78612 return ((drwav_uint32)data[0] << 0) | ((drwav_uint32)data[1] << 8) | ((drwav_uint32)data[2] << 16) | ((drwav_uint32)data[3] << 24);
78613}
78614DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data)
78615{
78616 union {
78617 drwav_uint32 u32;
78618 float f32;
78619 } value;
78620 value.u32 = drwav_bytes_to_u32(data);
78621 return value.f32;
78622}
78624{
78625 return (drwav_int32)drwav_bytes_to_u32(data);
78626}
78628{
78629 return
78630 ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) |
78631 ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56);
78632}
78634{
78635 return (drwav_int64)drwav_bytes_to_u64(data);
78636}
78638{
78639 int i;
78640 for (i = 0; i < 16; i += 1) {
78641 if (a[i] != b[i]) {
78642 return DRWAV_FALSE;
78643 }
78644 }
78645 return DRWAV_TRUE;
78646}
78648{
78649 return
78650 a[0] == b[0] &&
78651 a[1] == b[1] &&
78652 a[2] == b[2] &&
78653 a[3] == b[3];
78654}
78655#endif
78656/* dr_wav_c end */
78657#endif /* DRWAV_IMPLEMENTATION */
78658#endif /* MA_NO_WAV */
78659
78660#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
78661#if !defined(DR_FLAC_IMPLEMENTATION) && !defined(DRFLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
78662/* dr_flac_c begin */
78663#ifndef dr_flac_c
78664#define dr_flac_c
78665#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
78666 #pragma GCC diagnostic push
78667 #if __GNUC__ >= 7
78668 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
78669 #endif
78670#endif
78671#ifdef __linux__
78672 #ifndef _BSD_SOURCE
78673 #define _BSD_SOURCE
78674 #endif
78675 #ifndef _DEFAULT_SOURCE
78676 #define _DEFAULT_SOURCE
78677 #endif
78678 #ifndef __USE_BSD
78679 #define __USE_BSD
78680 #endif
78681 #include <endian.h>
78682#endif
78683#include <stdlib.h>
78684#include <string.h>
78685#ifdef _MSC_VER
78686 #define DRFLAC_INLINE __forceinline
78687#elif defined(__GNUC__)
78688 #if defined(__STRICT_ANSI__)
78689 #define DRFLAC_INLINE __inline__ __attribute__((always_inline))
78690 #else
78691 #define DRFLAC_INLINE inline __attribute__((always_inline))
78692 #endif
78693#elif defined(__WATCOMC__)
78694 #define DRFLAC_INLINE __inline
78695#else
78696 #define DRFLAC_INLINE
78697#endif
78698#if defined(__x86_64__) || defined(_M_X64)
78699 #define DRFLAC_X64
78700#elif defined(__i386) || defined(_M_IX86)
78701 #define DRFLAC_X86
78702#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64)
78703 #define DRFLAC_ARM
78704#endif
78705#if !defined(DR_FLAC_NO_SIMD)
78706 #if defined(DRFLAC_X64) || defined(DRFLAC_X86)
78707 #if defined(_MSC_VER) && !defined(__clang__)
78708 #if _MSC_VER >= 1400 && !defined(DRFLAC_NO_SSE2)
78709 #define DRFLAC_SUPPORT_SSE2
78710 #endif
78711 #if _MSC_VER >= 1600 && !defined(DRFLAC_NO_SSE41)
78712 #define DRFLAC_SUPPORT_SSE41
78713 #endif
78714 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
78715 #if defined(__SSE2__) && !defined(DRFLAC_NO_SSE2)
78716 #define DRFLAC_SUPPORT_SSE2
78717 #endif
78718 #if defined(__SSE4_1__) && !defined(DRFLAC_NO_SSE41)
78719 #define DRFLAC_SUPPORT_SSE41
78720 #endif
78721 #endif
78722 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
78723 #if !defined(DRFLAC_SUPPORT_SSE2) && !defined(DRFLAC_NO_SSE2) && __has_include(<emmintrin.h>)
78724 #define DRFLAC_SUPPORT_SSE2
78725 #endif
78726 #if !defined(DRFLAC_SUPPORT_SSE41) && !defined(DRFLAC_NO_SSE41) && __has_include(<smmintrin.h>)
78727 #define DRFLAC_SUPPORT_SSE41
78728 #endif
78729 #endif
78730 #if defined(DRFLAC_SUPPORT_SSE41)
78731 #include <smmintrin.h>
78732 #elif defined(DRFLAC_SUPPORT_SSE2)
78733 #include <emmintrin.h>
78734 #endif
78735 #endif
78736 #if defined(DRFLAC_ARM)
78737 #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
78738 #define DRFLAC_SUPPORT_NEON
78739 #include <arm_neon.h>
78740 #endif
78741 #endif
78742#endif
78743#if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64))
78744 #if defined(_MSC_VER) && !defined(__clang__)
78745 #if _MSC_VER >= 1400
78746 #include <intrin.h>
78747 static void drflac__cpuid(int info[4], int fid)
78748 {
78749 __cpuid(info, fid);
78750 }
78751 #else
78752 #define DRFLAC_NO_CPUID
78753 #endif
78754 #else
78755 #if defined(__GNUC__) || defined(__clang__)
78756 static void drflac__cpuid(int info[4], int fid)
78757 {
78758 #if defined(DRFLAC_X86) && defined(__PIC__)
78759 __asm__ __volatile__ (
78760 "xchg{l} {%%}ebx, %k1;"
78761 "cpuid;"
78762 "xchg{l} {%%}ebx, %k1;"
78763 : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
78764 );
78765 #else
78766 __asm__ __volatile__ (
78767 "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
78768 );
78769 #endif
78770 }
78771 #else
78772 #define DRFLAC_NO_CPUID
78773 #endif
78774 #endif
78775#else
78776 #define DRFLAC_NO_CPUID
78777#endif
78778static DRFLAC_INLINE drflac_bool32 drflac_has_sse2(void)
78779{
78780#if defined(DRFLAC_SUPPORT_SSE2)
78781 #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2)
78782 #if defined(DRFLAC_X64)
78783 return DRFLAC_TRUE;
78784 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
78785 return DRFLAC_TRUE;
78786 #else
78787 #if defined(DRFLAC_NO_CPUID)
78788 return DRFLAC_FALSE;
78789 #else
78790 int info[4];
78791 drflac__cpuid(info, 1);
78792 return (info[3] & (1 << 26)) != 0;
78793 #endif
78794 #endif
78795 #else
78796 return DRFLAC_FALSE;
78797 #endif
78798#else
78799 return DRFLAC_FALSE;
78800#endif
78801}
78802static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void)
78803{
78804#if defined(DRFLAC_SUPPORT_SSE41)
78805 #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41)
78806 #if defined(DRFLAC_X64)
78807 return DRFLAC_TRUE;
78808 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE4_1__)
78809 return DRFLAC_TRUE;
78810 #else
78811 #if defined(DRFLAC_NO_CPUID)
78812 return DRFLAC_FALSE;
78813 #else
78814 int info[4];
78815 drflac__cpuid(info, 1);
78816 return (info[2] & (1 << 19)) != 0;
78817 #endif
78818 #endif
78819 #else
78820 return DRFLAC_FALSE;
78821 #endif
78822#else
78823 return DRFLAC_FALSE;
78824#endif
78825}
78826#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) && !defined(__clang__)
78827 #define DRFLAC_HAS_LZCNT_INTRINSIC
78828#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
78829 #define DRFLAC_HAS_LZCNT_INTRINSIC
78830#elif defined(__clang__)
78831 #if defined(__has_builtin)
78832 #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl)
78833 #define DRFLAC_HAS_LZCNT_INTRINSIC
78834 #endif
78835 #endif
78836#endif
78837#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__)
78838 #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
78839 #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
78840 #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
78841#elif defined(__clang__)
78842 #if defined(__has_builtin)
78843 #if __has_builtin(__builtin_bswap16)
78844 #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
78845 #endif
78846 #if __has_builtin(__builtin_bswap32)
78847 #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
78848 #endif
78849 #if __has_builtin(__builtin_bswap64)
78850 #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
78851 #endif
78852 #endif
78853#elif defined(__GNUC__)
78854 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
78855 #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
78856 #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
78857 #endif
78858 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
78859 #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
78860 #endif
78861#elif defined(__WATCOMC__) && defined(__386__)
78862 #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
78863 #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
78864 #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
78865 extern __inline drflac_uint16 _watcom_bswap16(drflac_uint16);
78866 extern __inline drflac_uint32 _watcom_bswap32(drflac_uint32);
78867 extern __inline drflac_uint64 _watcom_bswap64(drflac_uint64);
78868#pragma aux _watcom_bswap16 = \
78869 "xchg al, ah" \
78870 parm [ax] \
78871 modify [ax];
78872#pragma aux _watcom_bswap32 = \
78873 "bswap eax" \
78874 parm [eax] \
78875 modify [eax];
78876#pragma aux _watcom_bswap64 = \
78877 "bswap eax" \
78878 "bswap edx" \
78879 "xchg eax,edx" \
78880 parm [eax edx] \
78881 modify [eax edx];
78882#endif
78883#ifndef DRFLAC_ASSERT
78884#include <assert.h>
78885#define DRFLAC_ASSERT(expression) assert(expression)
78886#endif
78887#ifndef DRFLAC_MALLOC
78888#define DRFLAC_MALLOC(sz) malloc((sz))
78889#endif
78890#ifndef DRFLAC_REALLOC
78891#define DRFLAC_REALLOC(p, sz) realloc((p), (sz))
78892#endif
78893#ifndef DRFLAC_FREE
78894#define DRFLAC_FREE(p) free((p))
78895#endif
78896#ifndef DRFLAC_COPY_MEMORY
78897#define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
78898#endif
78899#ifndef DRFLAC_ZERO_MEMORY
78900#define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
78901#endif
78902#ifndef DRFLAC_ZERO_OBJECT
78903#define DRFLAC_ZERO_OBJECT(p) DRFLAC_ZERO_MEMORY((p), sizeof(*(p)))
78904#endif
78905#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64
78906typedef drflac_int32 drflac_result;
78907#define DRFLAC_SUCCESS 0
78908#define DRFLAC_ERROR -1
78909#define DRFLAC_INVALID_ARGS -2
78910#define DRFLAC_INVALID_OPERATION -3
78911#define DRFLAC_OUT_OF_MEMORY -4
78912#define DRFLAC_OUT_OF_RANGE -5
78913#define DRFLAC_ACCESS_DENIED -6
78914#define DRFLAC_DOES_NOT_EXIST -7
78915#define DRFLAC_ALREADY_EXISTS -8
78916#define DRFLAC_TOO_MANY_OPEN_FILES -9
78917#define DRFLAC_INVALID_FILE -10
78918#define DRFLAC_TOO_BIG -11
78919#define DRFLAC_PATH_TOO_LONG -12
78920#define DRFLAC_NAME_TOO_LONG -13
78921#define DRFLAC_NOT_DIRECTORY -14
78922#define DRFLAC_IS_DIRECTORY -15
78923#define DRFLAC_DIRECTORY_NOT_EMPTY -16
78924#define DRFLAC_END_OF_FILE -17
78925#define DRFLAC_NO_SPACE -18
78926#define DRFLAC_BUSY -19
78927#define DRFLAC_IO_ERROR -20
78928#define DRFLAC_INTERRUPT -21
78929#define DRFLAC_UNAVAILABLE -22
78930#define DRFLAC_ALREADY_IN_USE -23
78931#define DRFLAC_BAD_ADDRESS -24
78932#define DRFLAC_BAD_SEEK -25
78933#define DRFLAC_BAD_PIPE -26
78934#define DRFLAC_DEADLOCK -27
78935#define DRFLAC_TOO_MANY_LINKS -28
78936#define DRFLAC_NOT_IMPLEMENTED -29
78937#define DRFLAC_NO_MESSAGE -30
78938#define DRFLAC_BAD_MESSAGE -31
78939#define DRFLAC_NO_DATA_AVAILABLE -32
78940#define DRFLAC_INVALID_DATA -33
78941#define DRFLAC_TIMEOUT -34
78942#define DRFLAC_NO_NETWORK -35
78943#define DRFLAC_NOT_UNIQUE -36
78944#define DRFLAC_NOT_SOCKET -37
78945#define DRFLAC_NO_ADDRESS -38
78946#define DRFLAC_BAD_PROTOCOL -39
78947#define DRFLAC_PROTOCOL_UNAVAILABLE -40
78948#define DRFLAC_PROTOCOL_NOT_SUPPORTED -41
78949#define DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED -42
78950#define DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED -43
78951#define DRFLAC_SOCKET_NOT_SUPPORTED -44
78952#define DRFLAC_CONNECTION_RESET -45
78953#define DRFLAC_ALREADY_CONNECTED -46
78954#define DRFLAC_NOT_CONNECTED -47
78955#define DRFLAC_CONNECTION_REFUSED -48
78956#define DRFLAC_NO_HOST -49
78957#define DRFLAC_IN_PROGRESS -50
78958#define DRFLAC_CANCELLED -51
78959#define DRFLAC_MEMORY_ALREADY_MAPPED -52
78960#define DRFLAC_AT_END -53
78961#define DRFLAC_CRC_MISMATCH -128
78962#define DRFLAC_SUBFRAME_CONSTANT 0
78963#define DRFLAC_SUBFRAME_VERBATIM 1
78964#define DRFLAC_SUBFRAME_FIXED 8
78965#define DRFLAC_SUBFRAME_LPC 32
78966#define DRFLAC_SUBFRAME_RESERVED 255
78967#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0
78968#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1
78969#define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0
78970#define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8
78971#define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9
78972#define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10
78973#define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
78974DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision)
78975{
78976 if (pMajor) {
78977 *pMajor = DRFLAC_VERSION_MAJOR;
78978 }
78979 if (pMinor) {
78980 *pMinor = DRFLAC_VERSION_MINOR;
78981 }
78982 if (pRevision) {
78983 *pRevision = DRFLAC_VERSION_REVISION;
78984 }
78985}
78986DRFLAC_API const char* drflac_version_string(void)
78987{
78988 return DRFLAC_VERSION_STRING;
78989}
78990#if defined(__has_feature)
78991 #if __has_feature(thread_sanitizer)
78992 #define DRFLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread")))
78993 #else
78994 #define DRFLAC_NO_THREAD_SANITIZE
78995 #endif
78996#else
78997 #define DRFLAC_NO_THREAD_SANITIZE
78998#endif
78999#if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
79000static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE;
79001#endif
79002#ifndef DRFLAC_NO_CPUID
79003static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE;
79004static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE;
79005DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void)
79006{
79007 static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE;
79008 if (!isCPUCapsInitialized) {
79009#if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
79010 int info[4] = {0};
79011 drflac__cpuid(info, 0x80000001);
79012 drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0;
79013#endif
79014 drflac__gIsSSE2Supported = drflac_has_sse2();
79015 drflac__gIsSSE41Supported = drflac_has_sse41();
79016 isCPUCapsInitialized = DRFLAC_TRUE;
79017 }
79018}
79019#else
79020static drflac_bool32 drflac__gIsNEONSupported = DRFLAC_FALSE;
79021static DRFLAC_INLINE drflac_bool32 drflac__has_neon(void)
79022{
79023#if defined(DRFLAC_SUPPORT_NEON)
79024 #if defined(DRFLAC_ARM) && !defined(DRFLAC_NO_NEON)
79025 #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
79026 return DRFLAC_TRUE;
79027 #else
79028 return DRFLAC_FALSE;
79029 #endif
79030 #else
79031 return DRFLAC_FALSE;
79032 #endif
79033#else
79034 return DRFLAC_FALSE;
79035#endif
79036}
79037DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void)
79038{
79039 drflac__gIsNEONSupported = drflac__has_neon();
79040#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
79041 drflac__gIsLZCNTSupported = DRFLAC_TRUE;
79042#endif
79043}
79044#endif
79045static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian(void)
79046{
79047#if defined(DRFLAC_X86) || defined(DRFLAC_X64)
79048 return DRFLAC_TRUE;
79049#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
79050 return DRFLAC_TRUE;
79051#else
79052 int n = 1;
79053 return (*(char*)&n) == 1;
79054#endif
79055}
79056static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n)
79057{
79058#ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC
79059 #if defined(_MSC_VER) && !defined(__clang__)
79060 return _byteswap_ushort(n);
79061 #elif defined(__GNUC__) || defined(__clang__)
79062 return __builtin_bswap16(n);
79063 #elif defined(__WATCOMC__) && defined(__386__)
79064 return _watcom_bswap16(n);
79065 #else
79066 #error "This compiler does not support the byte swap intrinsic."
79067 #endif
79068#else
79069 return ((n & 0xFF00) >> 8) |
79070 ((n & 0x00FF) << 8);
79071#endif
79072}
79073static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n)
79074{
79075#ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC
79076 #if defined(_MSC_VER) && !defined(__clang__)
79077 return _byteswap_ulong(n);
79078 #elif defined(__GNUC__) || defined(__clang__)
79079 #if defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRFLAC_64BIT)
79080 drflac_uint32 r;
79081 __asm__ __volatile__ (
79082 #if defined(DRFLAC_64BIT)
79083 "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n)
79084 #else
79085 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
79086 #endif
79087 );
79088 return r;
79089 #else
79090 return __builtin_bswap32(n);
79091 #endif
79092 #elif defined(__WATCOMC__) && defined(__386__)
79093 return _watcom_bswap32(n);
79094 #else
79095 #error "This compiler does not support the byte swap intrinsic."
79096 #endif
79097#else
79098 return ((n & 0xFF000000) >> 24) |
79099 ((n & 0x00FF0000) >> 8) |
79100 ((n & 0x0000FF00) << 8) |
79101 ((n & 0x000000FF) << 24);
79102#endif
79103}
79104static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n)
79105{
79106#ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC
79107 #if defined(_MSC_VER) && !defined(__clang__)
79108 return _byteswap_uint64(n);
79109 #elif defined(__GNUC__) || defined(__clang__)
79110 return __builtin_bswap64(n);
79111 #elif defined(__WATCOMC__) && defined(__386__)
79112 return _watcom_bswap64(n);
79113 #else
79114 #error "This compiler does not support the byte swap intrinsic."
79115 #endif
79116#else
79117 return ((n & ((drflac_uint64)0xFF000000 << 32)) >> 56) |
79118 ((n & ((drflac_uint64)0x00FF0000 << 32)) >> 40) |
79119 ((n & ((drflac_uint64)0x0000FF00 << 32)) >> 24) |
79120 ((n & ((drflac_uint64)0x000000FF << 32)) >> 8) |
79121 ((n & ((drflac_uint64)0xFF000000 )) << 8) |
79122 ((n & ((drflac_uint64)0x00FF0000 )) << 24) |
79123 ((n & ((drflac_uint64)0x0000FF00 )) << 40) |
79124 ((n & ((drflac_uint64)0x000000FF )) << 56);
79125#endif
79126}
79127static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n)
79128{
79129 if (drflac__is_little_endian()) {
79130 return drflac__swap_endian_uint16(n);
79131 }
79132 return n;
79133}
79134static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n)
79135{
79136 if (drflac__is_little_endian()) {
79137 return drflac__swap_endian_uint32(n);
79138 }
79139 return n;
79140}
79141static DRFLAC_INLINE drflac_uint32 drflac__be2host_32_ptr_unaligned(const void* pData)
79142{
79143 const drflac_uint8* pNum = (drflac_uint8*)pData;
79144 return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3);
79145}
79146static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n)
79147{
79148 if (drflac__is_little_endian()) {
79149 return drflac__swap_endian_uint64(n);
79150 }
79151 return n;
79152}
79153static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n)
79154{
79155 if (!drflac__is_little_endian()) {
79156 return drflac__swap_endian_uint32(n);
79157 }
79158 return n;
79159}
79160static DRFLAC_INLINE drflac_uint32 drflac__le2host_32_ptr_unaligned(const void* pData)
79161{
79162 const drflac_uint8* pNum = (drflac_uint8*)pData;
79163 return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24;
79164}
79165static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n)
79166{
79167 drflac_uint32 result = 0;
79168 result |= (n & 0x7F000000) >> 3;
79169 result |= (n & 0x007F0000) >> 2;
79170 result |= (n & 0x00007F00) >> 1;
79171 result |= (n & 0x0000007F) >> 0;
79172 return result;
79173}
79174static drflac_uint8 drflac__crc8_table[] = {
79175 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
79176 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
79177 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
79178 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
79179 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
79180 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
79181 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
79182 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
79183 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
79184 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
79185 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
79186 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
79187 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
79188 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
79189 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
79190 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
79191};
79192static drflac_uint16 drflac__crc16_table[] = {
79193 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
79194 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
79195 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
79196 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
79197 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
79198 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
79199 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
79200 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
79201 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
79202 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
79203 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
79204 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
79205 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
79206 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
79207 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
79208 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
79209 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
79210 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
79211 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
79212 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
79213 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
79214 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
79215 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
79216 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
79217 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
79218 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
79219 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
79220 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
79221 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
79222 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
79223 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
79224 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202
79225};
79226static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data)
79227{
79228 return drflac__crc8_table[crc ^ data];
79229}
79230static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count)
79231{
79232#ifdef DR_FLAC_NO_CRC
79233 (void)crc;
79234 (void)data;
79235 (void)count;
79236 return 0;
79237#else
79238#if 0
79239 drflac_uint8 p = 0x07;
79240 for (int i = count-1; i >= 0; --i) {
79241 drflac_uint8 bit = (data & (1 << i)) >> i;
79242 if (crc & 0x80) {
79243 crc = ((crc << 1) | bit) ^ p;
79244 } else {
79245 crc = ((crc << 1) | bit);
79246 }
79247 }
79248 return crc;
79249#else
79250 drflac_uint32 wholeBytes;
79251 drflac_uint32 leftoverBits;
79252 drflac_uint64 leftoverDataMask;
79253 static drflac_uint64 leftoverDataMaskTable[8] = {
79254 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
79255 };
79256 DRFLAC_ASSERT(count <= 32);
79257 wholeBytes = count >> 3;
79258 leftoverBits = count - (wholeBytes*8);
79259 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
79260 switch (wholeBytes) {
79261 case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
79262 case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
79263 case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
79264 case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
79265 case 0: if (leftoverBits > 0) crc = (drflac_uint8)((crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]);
79266 }
79267 return crc;
79268#endif
79269#endif
79270}
79271static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data)
79272{
79273 return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data];
79274}
79275static DRFLAC_INLINE drflac_uint16 drflac_crc16_cache(drflac_uint16 crc, drflac_cache_t data)
79276{
79277#ifdef DRFLAC_64BIT
79278 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF));
79279 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF));
79280 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF));
79281 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF));
79282#endif
79283 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF));
79284 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF));
79285 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF));
79286 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF));
79287 return crc;
79288}
79289static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount)
79290{
79291 switch (byteCount)
79292 {
79293#ifdef DRFLAC_64BIT
79294 case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF));
79295 case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF));
79296 case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF));
79297 case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF));
79298#endif
79299 case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF));
79300 case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF));
79301 case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF));
79302 case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF));
79303 }
79304 return crc;
79305}
79306#if 0
79307static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count)
79308{
79309#ifdef DR_FLAC_NO_CRC
79310 (void)crc;
79311 (void)data;
79312 (void)count;
79313 return 0;
79314#else
79315#if 0
79316 drflac_uint16 p = 0x8005;
79317 for (int i = count-1; i >= 0; --i) {
79318 drflac_uint16 bit = (data & (1ULL << i)) >> i;
79319 if (r & 0x8000) {
79320 r = ((r << 1) | bit) ^ p;
79321 } else {
79322 r = ((r << 1) | bit);
79323 }
79324 }
79325 return crc;
79326#else
79327 drflac_uint32 wholeBytes;
79328 drflac_uint32 leftoverBits;
79329 drflac_uint64 leftoverDataMask;
79330 static drflac_uint64 leftoverDataMaskTable[8] = {
79331 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
79332 };
79333 DRFLAC_ASSERT(count <= 64);
79334 wholeBytes = count >> 3;
79335 leftoverBits = count & 7;
79336 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
79337 switch (wholeBytes) {
79338 default:
79339 case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
79340 case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
79341 case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
79342 case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
79343 case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
79344 }
79345 return crc;
79346#endif
79347#endif
79348}
79349static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count)
79350{
79351#ifdef DR_FLAC_NO_CRC
79352 (void)crc;
79353 (void)data;
79354 (void)count;
79355 return 0;
79356#else
79357 drflac_uint32 wholeBytes;
79358 drflac_uint32 leftoverBits;
79359 drflac_uint64 leftoverDataMask;
79360 static drflac_uint64 leftoverDataMaskTable[8] = {
79361 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
79362 };
79363 DRFLAC_ASSERT(count <= 64);
79364 wholeBytes = count >> 3;
79365 leftoverBits = count & 7;
79366 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
79367 switch (wholeBytes) {
79368 default:
79369 case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits)));
79370 case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits)));
79371 case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits)));
79372 case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits)));
79373 case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits)));
79374 case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits)));
79375 case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits)));
79376 case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits)));
79377 case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
79378 }
79379 return crc;
79380#endif
79381}
79382static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count)
79383{
79384#ifdef DRFLAC_64BIT
79385 return drflac_crc16__64bit(crc, data, count);
79386#else
79387 return drflac_crc16__32bit(crc, data, count);
79388#endif
79389}
79390#endif
79391#ifdef DRFLAC_64BIT
79392#define drflac__be2host__cache_line drflac__be2host_64
79393#else
79394#define drflac__be2host__cache_line drflac__be2host_32
79395#endif
79396#define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache))
79397#define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8)
79398#define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits)
79399#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount)))
79400#define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount))
79401#define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount))
79402#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)))
79403#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1)))
79404#define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2))
79405#define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0]))
79406#define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line)
79407#ifndef DR_FLAC_NO_CRC
79408static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs)
79409{
79410 bs->crc16 = 0;
79411 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
79412}
79413static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs)
79414{
79415 if (bs->crc16CacheIgnoredBytes == 0) {
79416 bs->crc16 = drflac_crc16_cache(bs->crc16, bs->crc16Cache);
79417 } else {
79418 bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes);
79419 bs->crc16CacheIgnoredBytes = 0;
79420 }
79421}
79422static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs)
79423{
79424 DRFLAC_ASSERT((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0);
79425 if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) {
79426 drflac__update_crc16(bs);
79427 } else {
79428 bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes);
79429 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
79430 }
79431 return bs->crc16;
79432}
79433#endif
79434static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs)
79435{
79436 size_t bytesRead;
79437 size_t alignedL1LineCount;
79438 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
79439 bs->cache = bs->cacheL2[bs->nextL2Line++];
79440 return DRFLAC_TRUE;
79441 }
79442 if (bs->unalignedByteCount > 0) {
79443 return DRFLAC_FALSE;
79444 }
79445 bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs));
79446 bs->nextL2Line = 0;
79447 if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) {
79448 bs->cache = bs->cacheL2[bs->nextL2Line++];
79449 return DRFLAC_TRUE;
79450 }
79451 alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs);
79452 bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs));
79453 if (bs->unalignedByteCount > 0) {
79454 bs->unalignedCache = bs->cacheL2[alignedL1LineCount];
79455 }
79456 if (alignedL1LineCount > 0) {
79457 size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount;
79458 size_t i;
79459 for (i = alignedL1LineCount; i > 0; --i) {
79460 bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1];
79461 }
79462 bs->nextL2Line = (drflac_uint32)offset;
79463 bs->cache = bs->cacheL2[bs->nextL2Line++];
79464 return DRFLAC_TRUE;
79465 } else {
79466 bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs);
79467 return DRFLAC_FALSE;
79468 }
79469}
79470static drflac_bool32 drflac__reload_cache(drflac_bs* bs)
79471{
79472 size_t bytesRead;
79473#ifndef DR_FLAC_NO_CRC
79474 drflac__update_crc16(bs);
79475#endif
79476 if (drflac__reload_l1_cache_from_l2(bs)) {
79477 bs->cache = drflac__be2host__cache_line(bs->cache);
79478 bs->consumedBits = 0;
79479#ifndef DR_FLAC_NO_CRC
79480 bs->crc16Cache = bs->cache;
79481#endif
79482 return DRFLAC_TRUE;
79483 }
79484 bytesRead = bs->unalignedByteCount;
79485 if (bytesRead == 0) {
79486 bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
79487 return DRFLAC_FALSE;
79488 }
79489 DRFLAC_ASSERT(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs));
79490 bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8;
79491 bs->cache = drflac__be2host__cache_line(bs->unalignedCache);
79492 bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_BITS_REMAINING(bs));
79493 bs->unalignedByteCount = 0;
79494#ifndef DR_FLAC_NO_CRC
79495 bs->crc16Cache = bs->cache >> bs->consumedBits;
79496 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
79497#endif
79498 return DRFLAC_TRUE;
79499}
79500static void drflac__reset_cache(drflac_bs* bs)
79501{
79502 bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs);
79503 bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
79504 bs->cache = 0;
79505 bs->unalignedByteCount = 0;
79506 bs->unalignedCache = 0;
79507#ifndef DR_FLAC_NO_CRC
79508 bs->crc16Cache = 0;
79509 bs->crc16CacheIgnoredBytes = 0;
79510#endif
79511}
79512static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut)
79513{
79514 DRFLAC_ASSERT(bs != NULL);
79515 DRFLAC_ASSERT(pResultOut != NULL);
79516 DRFLAC_ASSERT(bitCount > 0);
79517 DRFLAC_ASSERT(bitCount <= 32);
79518 if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
79519 if (!drflac__reload_cache(bs)) {
79520 return DRFLAC_FALSE;
79521 }
79522 }
79523 if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
79524#ifdef DRFLAC_64BIT
79525 *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
79526 bs->consumedBits += bitCount;
79527 bs->cache <<= bitCount;
79528#else
79529 if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
79530 *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
79531 bs->consumedBits += bitCount;
79532 bs->cache <<= bitCount;
79533 } else {
79534 *pResultOut = (drflac_uint32)bs->cache;
79535 bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
79536 bs->cache = 0;
79537 }
79538#endif
79539 return DRFLAC_TRUE;
79540 } else {
79541 drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs);
79542 drflac_uint32 bitCountLo = bitCount - bitCountHi;
79543 drflac_uint32 resultHi;
79544 DRFLAC_ASSERT(bitCountHi > 0);
79545 DRFLAC_ASSERT(bitCountHi < 32);
79546 resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi);
79547 if (!drflac__reload_cache(bs)) {
79548 return DRFLAC_FALSE;
79549 }
79550 if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
79551 return DRFLAC_FALSE;
79552 }
79553 *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo);
79554 bs->consumedBits += bitCountLo;
79555 bs->cache <<= bitCountLo;
79556 return DRFLAC_TRUE;
79557 }
79558}
79559static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult)
79560{
79561 drflac_uint32 result;
79562 DRFLAC_ASSERT(bs != NULL);
79563 DRFLAC_ASSERT(pResult != NULL);
79564 DRFLAC_ASSERT(bitCount > 0);
79565 DRFLAC_ASSERT(bitCount <= 32);
79566 if (!drflac__read_uint32(bs, bitCount, &result)) {
79567 return DRFLAC_FALSE;
79568 }
79569 if (bitCount < 32) {
79570 drflac_uint32 signbit;
79571 signbit = ((result >> (bitCount-1)) & 0x01);
79572 result |= (~signbit + 1) << bitCount;
79573 }
79574 *pResult = (drflac_int32)result;
79575 return DRFLAC_TRUE;
79576}
79577#ifdef DRFLAC_64BIT
79578static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut)
79579{
79580 drflac_uint32 resultHi;
79581 drflac_uint32 resultLo;
79582 DRFLAC_ASSERT(bitCount <= 64);
79583 DRFLAC_ASSERT(bitCount > 32);
79584 if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) {
79585 return DRFLAC_FALSE;
79586 }
79587 if (!drflac__read_uint32(bs, 32, &resultLo)) {
79588 return DRFLAC_FALSE;
79589 }
79590 *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo);
79591 return DRFLAC_TRUE;
79592}
79593#endif
79594#if 0
79595static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut)
79596{
79597 drflac_uint64 result;
79598 drflac_uint64 signbit;
79599 DRFLAC_ASSERT(bitCount <= 64);
79600 if (!drflac__read_uint64(bs, bitCount, &result)) {
79601 return DRFLAC_FALSE;
79602 }
79603 signbit = ((result >> (bitCount-1)) & 0x01);
79604 result |= (~signbit + 1) << bitCount;
79605 *pResultOut = (drflac_int64)result;
79606 return DRFLAC_TRUE;
79607}
79608#endif
79609static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult)
79610{
79611 drflac_uint32 result;
79612 DRFLAC_ASSERT(bs != NULL);
79613 DRFLAC_ASSERT(pResult != NULL);
79614 DRFLAC_ASSERT(bitCount > 0);
79615 DRFLAC_ASSERT(bitCount <= 16);
79616 if (!drflac__read_uint32(bs, bitCount, &result)) {
79617 return DRFLAC_FALSE;
79618 }
79619 *pResult = (drflac_uint16)result;
79620 return DRFLAC_TRUE;
79621}
79622#if 0
79623static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult)
79624{
79625 drflac_int32 result;
79626 DRFLAC_ASSERT(bs != NULL);
79627 DRFLAC_ASSERT(pResult != NULL);
79628 DRFLAC_ASSERT(bitCount > 0);
79629 DRFLAC_ASSERT(bitCount <= 16);
79630 if (!drflac__read_int32(bs, bitCount, &result)) {
79631 return DRFLAC_FALSE;
79632 }
79633 *pResult = (drflac_int16)result;
79634 return DRFLAC_TRUE;
79635}
79636#endif
79637static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult)
79638{
79639 drflac_uint32 result;
79640 DRFLAC_ASSERT(bs != NULL);
79641 DRFLAC_ASSERT(pResult != NULL);
79642 DRFLAC_ASSERT(bitCount > 0);
79643 DRFLAC_ASSERT(bitCount <= 8);
79644 if (!drflac__read_uint32(bs, bitCount, &result)) {
79645 return DRFLAC_FALSE;
79646 }
79647 *pResult = (drflac_uint8)result;
79648 return DRFLAC_TRUE;
79649}
79650static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult)
79651{
79652 drflac_int32 result;
79653 DRFLAC_ASSERT(bs != NULL);
79654 DRFLAC_ASSERT(pResult != NULL);
79655 DRFLAC_ASSERT(bitCount > 0);
79656 DRFLAC_ASSERT(bitCount <= 8);
79657 if (!drflac__read_int32(bs, bitCount, &result)) {
79658 return DRFLAC_FALSE;
79659 }
79660 *pResult = (drflac_int8)result;
79661 return DRFLAC_TRUE;
79662}
79663static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek)
79664{
79665 if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
79666 bs->consumedBits += (drflac_uint32)bitsToSeek;
79667 bs->cache <<= bitsToSeek;
79668 return DRFLAC_TRUE;
79669 } else {
79670 bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs);
79671 bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs);
79672 bs->cache = 0;
79673#ifdef DRFLAC_64BIT
79674 while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
79675 drflac_uint64 bin;
79676 if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
79677 return DRFLAC_FALSE;
79678 }
79679 bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs);
79680 }
79681#else
79682 while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
79683 drflac_uint32 bin;
79684 if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
79685 return DRFLAC_FALSE;
79686 }
79687 bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs);
79688 }
79689#endif
79690 while (bitsToSeek >= 8) {
79691 drflac_uint8 bin;
79692 if (!drflac__read_uint8(bs, 8, &bin)) {
79693 return DRFLAC_FALSE;
79694 }
79695 bitsToSeek -= 8;
79696 }
79697 if (bitsToSeek > 0) {
79698 drflac_uint8 bin;
79699 if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) {
79700 return DRFLAC_FALSE;
79701 }
79702 bitsToSeek = 0;
79703 }
79704 DRFLAC_ASSERT(bitsToSeek == 0);
79705 return DRFLAC_TRUE;
79706 }
79707}
79708static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs)
79709{
79710 DRFLAC_ASSERT(bs != NULL);
79711 if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
79712 return DRFLAC_FALSE;
79713 }
79714 for (;;) {
79715 drflac_uint8 hi;
79716#ifndef DR_FLAC_NO_CRC
79717 drflac__reset_crc16(bs);
79718#endif
79719 if (!drflac__read_uint8(bs, 8, &hi)) {
79720 return DRFLAC_FALSE;
79721 }
79722 if (hi == 0xFF) {
79723 drflac_uint8 lo;
79724 if (!drflac__read_uint8(bs, 6, &lo)) {
79725 return DRFLAC_FALSE;
79726 }
79727 if (lo == 0x3E) {
79728 return DRFLAC_TRUE;
79729 } else {
79730 if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
79731 return DRFLAC_FALSE;
79732 }
79733 }
79734 }
79735 }
79736}
79737#if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
79738#define DRFLAC_IMPLEMENT_CLZ_LZCNT
79739#endif
79740#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(__clang__)
79741#define DRFLAC_IMPLEMENT_CLZ_MSVC
79742#endif
79743#if defined(__WATCOMC__) && defined(__386__)
79744#define DRFLAC_IMPLEMENT_CLZ_WATCOM
79745#endif
79746static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x)
79747{
79748 drflac_uint32 n;
79749 static drflac_uint32 clz_table_4[] = {
79750 0,
79751 4,
79752 3, 3,
79753 2, 2, 2, 2,
79754 1, 1, 1, 1, 1, 1, 1, 1
79755 };
79756 if (x == 0) {
79757 return sizeof(x)*8;
79758 }
79759 n = clz_table_4[x >> (sizeof(x)*8 - 4)];
79760 if (n == 0) {
79761#ifdef DRFLAC_64BIT
79762 if ((x & ((drflac_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; }
79763 if ((x & ((drflac_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; }
79764 if ((x & ((drflac_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; }
79765 if ((x & ((drflac_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; }
79766#else
79767 if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; }
79768 if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; }
79769 if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; }
79770#endif
79771 n += clz_table_4[x >> (sizeof(x)*8 - 4)];
79772 }
79773 return n - 1;
79774}
79775#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT
79776static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void)
79777{
79778#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
79779 return DRFLAC_TRUE;
79780#else
79781 #ifdef DRFLAC_HAS_LZCNT_INTRINSIC
79782 return drflac__gIsLZCNTSupported;
79783 #else
79784 return DRFLAC_FALSE;
79785 #endif
79786#endif
79787}
79788static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x)
79789{
79790#if defined(_MSC_VER)
79791 #ifdef DRFLAC_64BIT
79792 return (drflac_uint32)__lzcnt64(x);
79793 #else
79794 return (drflac_uint32)__lzcnt(x);
79795 #endif
79796#else
79797 #if defined(__GNUC__) || defined(__clang__)
79798 #if defined(DRFLAC_X64)
79799 {
79800 drflac_uint64 r;
79801 __asm__ __volatile__ (
79802 "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
79803 );
79804 return (drflac_uint32)r;
79805 }
79806 #elif defined(DRFLAC_X86)
79807 {
79808 drflac_uint32 r;
79809 __asm__ __volatile__ (
79810 "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
79811 );
79812 return r;
79813 }
79814 #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(DRFLAC_64BIT)
79815 {
79816 unsigned int r;
79817 __asm__ __volatile__ (
79818 #if defined(DRFLAC_64BIT)
79819 "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x)
79820 #else
79821 "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x)
79822 #endif
79823 );
79824 return r;
79825 }
79826 #else
79827 if (x == 0) {
79828 return sizeof(x)*8;
79829 }
79830 #ifdef DRFLAC_64BIT
79831 return (drflac_uint32)__builtin_clzll((drflac_uint64)x);
79832 #else
79833 return (drflac_uint32)__builtin_clzl((drflac_uint32)x);
79834 #endif
79835 #endif
79836 #else
79837 #error "This compiler does not support the lzcnt intrinsic."
79838 #endif
79839#endif
79840}
79841#endif
79842#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC
79843#include <intrin.h>
79844static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x)
79845{
79846 drflac_uint32 n;
79847 if (x == 0) {
79848 return sizeof(x)*8;
79849 }
79850#ifdef DRFLAC_64BIT
79851 _BitScanReverse64((unsigned long*)&n, x);
79852#else
79853 _BitScanReverse((unsigned long*)&n, x);
79854#endif
79855 return sizeof(x)*8 - n - 1;
79856}
79857#endif
79858#ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM
79859static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32);
79860#pragma aux drflac__clz_watcom = \
79861 "bsr eax, eax" \
79862 "xor eax, 31" \
79863 parm [eax] nomemory \
79864 value [eax] \
79865 modify exact [eax] nomemory;
79866#endif
79867static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x)
79868{
79869#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT
79870 if (drflac__is_lzcnt_supported()) {
79871 return drflac__clz_lzcnt(x);
79872 } else
79873#endif
79874 {
79875#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC
79876 return drflac__clz_msvc(x);
79877#elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM)
79878 return (x == 0) ? sizeof(x)*8 : drflac__clz_watcom(x);
79879#else
79880 return drflac__clz_software(x);
79881#endif
79882 }
79883}
79884static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut)
79885{
79886 drflac_uint32 zeroCounter = 0;
79887 drflac_uint32 setBitOffsetPlus1;
79888 while (bs->cache == 0) {
79889 zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
79890 if (!drflac__reload_cache(bs)) {
79891 return DRFLAC_FALSE;
79892 }
79893 }
79894 if (bs->cache == 1) {
79895 *pOffsetOut = zeroCounter + (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs) - 1;
79896 if (!drflac__reload_cache(bs)) {
79897 return DRFLAC_FALSE;
79898 }
79899 return DRFLAC_TRUE;
79900 }
79901 setBitOffsetPlus1 = drflac__clz(bs->cache);
79902 setBitOffsetPlus1 += 1;
79903 if (setBitOffsetPlus1 > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
79904 return DRFLAC_FALSE;
79905 }
79906 bs->consumedBits += setBitOffsetPlus1;
79907 bs->cache <<= setBitOffsetPlus1;
79908 *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1;
79909 return DRFLAC_TRUE;
79910}
79911static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart)
79912{
79913 DRFLAC_ASSERT(bs != NULL);
79914 DRFLAC_ASSERT(offsetFromStart > 0);
79915 if (offsetFromStart > 0x7FFFFFFF) {
79916 drflac_uint64 bytesRemaining = offsetFromStart;
79917 if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
79918 return DRFLAC_FALSE;
79919 }
79920 bytesRemaining -= 0x7FFFFFFF;
79921 while (bytesRemaining > 0x7FFFFFFF) {
79922 if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
79923 return DRFLAC_FALSE;
79924 }
79925 bytesRemaining -= 0x7FFFFFFF;
79926 }
79927 if (bytesRemaining > 0) {
79928 if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) {
79929 return DRFLAC_FALSE;
79930 }
79931 }
79932 } else {
79933 if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) {
79934 return DRFLAC_FALSE;
79935 }
79936 }
79937 drflac__reset_cache(bs);
79938 return DRFLAC_TRUE;
79939}
79940static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut)
79941{
79942 drflac_uint8 crc;
79943 drflac_uint64 result;
79944 drflac_uint8 utf8[7] = {0};
79945 int byteCount;
79946 int i;
79947 DRFLAC_ASSERT(bs != NULL);
79948 DRFLAC_ASSERT(pNumberOut != NULL);
79949 DRFLAC_ASSERT(pCRCOut != NULL);
79950 crc = *pCRCOut;
79951 if (!drflac__read_uint8(bs, 8, utf8)) {
79952 *pNumberOut = 0;
79953 return DRFLAC_AT_END;
79954 }
79955 crc = drflac_crc8(crc, utf8[0], 8);
79956 if ((utf8[0] & 0x80) == 0) {
79957 *pNumberOut = utf8[0];
79958 *pCRCOut = crc;
79959 return DRFLAC_SUCCESS;
79960 }
79961 if ((utf8[0] & 0xE0) == 0xC0) {
79962 byteCount = 2;
79963 } else if ((utf8[0] & 0xF0) == 0xE0) {
79964 byteCount = 3;
79965 } else if ((utf8[0] & 0xF8) == 0xF0) {
79966 byteCount = 4;
79967 } else if ((utf8[0] & 0xFC) == 0xF8) {
79968 byteCount = 5;
79969 } else if ((utf8[0] & 0xFE) == 0xFC) {
79970 byteCount = 6;
79971 } else if ((utf8[0] & 0xFF) == 0xFE) {
79972 byteCount = 7;
79973 } else {
79974 *pNumberOut = 0;
79975 return DRFLAC_CRC_MISMATCH;
79976 }
79977 DRFLAC_ASSERT(byteCount > 1);
79978 result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1)));
79979 for (i = 1; i < byteCount; ++i) {
79980 if (!drflac__read_uint8(bs, 8, utf8 + i)) {
79981 *pNumberOut = 0;
79982 return DRFLAC_AT_END;
79983 }
79984 crc = drflac_crc8(crc, utf8[i], 8);
79985 result = (result << 6) | (utf8[i] & 0x3F);
79986 }
79987 *pNumberOut = result;
79988 *pCRCOut = crc;
79989 return DRFLAC_SUCCESS;
79990}
79991static DRFLAC_INLINE drflac_uint32 drflac__ilog2_u32(drflac_uint32 x)
79992{
79993#if 1
79994 drflac_uint32 result = 0;
79995 while (x > 0) {
79996 result += 1;
79997 x >>= 1;
79998 }
79999 return result;
80000#endif
80001}
80002static DRFLAC_INLINE drflac_bool32 drflac__use_64_bit_prediction(drflac_uint32 bitsPerSample, drflac_uint32 order, drflac_uint32 precision)
80003{
80004 return bitsPerSample + precision + drflac__ilog2_u32(order) > 32;
80005}
80006#if defined(__clang__)
80007__attribute__((no_sanitize("signed-integer-overflow")))
80008#endif
80009static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
80010{
80011 drflac_int32 prediction = 0;
80012 DRFLAC_ASSERT(order <= 32);
80013 switch (order)
80014 {
80015 case 32: prediction += coefficients[31] * pDecodedSamples[-32];
80016 case 31: prediction += coefficients[30] * pDecodedSamples[-31];
80017 case 30: prediction += coefficients[29] * pDecodedSamples[-30];
80018 case 29: prediction += coefficients[28] * pDecodedSamples[-29];
80019 case 28: prediction += coefficients[27] * pDecodedSamples[-28];
80020 case 27: prediction += coefficients[26] * pDecodedSamples[-27];
80021 case 26: prediction += coefficients[25] * pDecodedSamples[-26];
80022 case 25: prediction += coefficients[24] * pDecodedSamples[-25];
80023 case 24: prediction += coefficients[23] * pDecodedSamples[-24];
80024 case 23: prediction += coefficients[22] * pDecodedSamples[-23];
80025 case 22: prediction += coefficients[21] * pDecodedSamples[-22];
80026 case 21: prediction += coefficients[20] * pDecodedSamples[-21];
80027 case 20: prediction += coefficients[19] * pDecodedSamples[-20];
80028 case 19: prediction += coefficients[18] * pDecodedSamples[-19];
80029 case 18: prediction += coefficients[17] * pDecodedSamples[-18];
80030 case 17: prediction += coefficients[16] * pDecodedSamples[-17];
80031 case 16: prediction += coefficients[15] * pDecodedSamples[-16];
80032 case 15: prediction += coefficients[14] * pDecodedSamples[-15];
80033 case 14: prediction += coefficients[13] * pDecodedSamples[-14];
80034 case 13: prediction += coefficients[12] * pDecodedSamples[-13];
80035 case 12: prediction += coefficients[11] * pDecodedSamples[-12];
80036 case 11: prediction += coefficients[10] * pDecodedSamples[-11];
80037 case 10: prediction += coefficients[ 9] * pDecodedSamples[-10];
80038 case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9];
80039 case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8];
80040 case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7];
80041 case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6];
80042 case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5];
80043 case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4];
80044 case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3];
80045 case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2];
80046 case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1];
80047 }
80048 return (drflac_int32)(prediction >> shift);
80049}
80050static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
80051{
80052 drflac_int64 prediction;
80053 DRFLAC_ASSERT(order <= 32);
80054#ifndef DRFLAC_64BIT
80055 if (order == 8)
80056 {
80057 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
80058 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
80059 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
80060 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
80061 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
80062 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
80063 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
80064 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
80065 }
80066 else if (order == 7)
80067 {
80068 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
80069 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
80070 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
80071 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
80072 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
80073 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
80074 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
80075 }
80076 else if (order == 3)
80077 {
80078 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
80079 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
80080 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
80081 }
80082 else if (order == 6)
80083 {
80084 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
80085 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
80086 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
80087 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
80088 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
80089 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
80090 }
80091 else if (order == 5)
80092 {
80093 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
80094 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
80095 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
80096 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
80097 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
80098 }
80099 else if (order == 4)
80100 {
80101 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
80102 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
80103 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
80104 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
80105 }
80106 else if (order == 12)
80107 {
80108 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
80109 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
80110 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
80111 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
80112 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
80113 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
80114 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
80115 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
80116 prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
80117 prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
80118 prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
80119 prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12];
80120 }
80121 else if (order == 2)
80122 {
80123 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
80124 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
80125 }
80126 else if (order == 1)
80127 {
80128 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
80129 }
80130 else if (order == 10)
80131 {
80132 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
80133 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
80134 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
80135 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
80136 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
80137 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
80138 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
80139 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
80140 prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
80141 prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
80142 }
80143 else if (order == 9)
80144 {
80145 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
80146 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
80147 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
80148 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
80149 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
80150 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
80151 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
80152 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
80153 prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
80154 }
80155 else if (order == 11)
80156 {
80157 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
80158 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
80159 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
80160 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
80161 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
80162 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
80163 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
80164 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
80165 prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
80166 prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
80167 prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
80168 }
80169 else
80170 {
80171 int j;
80172 prediction = 0;
80173 for (j = 0; j < (int)order; ++j) {
80174 prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1];
80175 }
80176 }
80177#endif
80178#ifdef DRFLAC_64BIT
80179 prediction = 0;
80180 switch (order)
80181 {
80182 case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32];
80183 case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31];
80184 case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30];
80185 case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29];
80186 case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28];
80187 case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27];
80188 case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26];
80189 case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25];
80190 case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24];
80191 case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23];
80192 case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22];
80193 case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21];
80194 case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20];
80195 case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19];
80196 case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18];
80197 case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17];
80198 case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16];
80199 case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15];
80200 case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14];
80201 case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13];
80202 case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12];
80203 case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
80204 case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10];
80205 case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9];
80206 case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8];
80207 case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7];
80208 case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6];
80209 case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5];
80210 case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4];
80211 case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3];
80212 case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2];
80213 case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1];
80214 }
80215#endif
80216 return (drflac_int32)(prediction >> shift);
80217}
80218#if 0
80219static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
80220{
80221 drflac_uint32 i;
80222 DRFLAC_ASSERT(bs != NULL);
80223 DRFLAC_ASSERT(pSamplesOut != NULL);
80224 for (i = 0; i < count; ++i) {
80225 drflac_uint32 zeroCounter = 0;
80226 for (;;) {
80227 drflac_uint8 bit;
80228 if (!drflac__read_uint8(bs, 1, &bit)) {
80229 return DRFLAC_FALSE;
80230 }
80231 if (bit == 0) {
80232 zeroCounter += 1;
80233 } else {
80234 break;
80235 }
80236 }
80237 drflac_uint32 decodedRice;
80238 if (riceParam > 0) {
80239 if (!drflac__read_uint32(bs, riceParam, &decodedRice)) {
80240 return DRFLAC_FALSE;
80241 }
80242 } else {
80243 decodedRice = 0;
80244 }
80245 decodedRice |= (zeroCounter << riceParam);
80246 if ((decodedRice & 0x01)) {
80247 decodedRice = ~(decodedRice >> 1);
80248 } else {
80249 decodedRice = (decodedRice >> 1);
80250 }
80251 if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
80252 pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
80253 } else {
80254 pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
80255 }
80256 }
80257 return DRFLAC_TRUE;
80258}
80259#endif
80260#if 0
80261static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
80262{
80263 drflac_uint32 zeroCounter = 0;
80264 drflac_uint32 decodedRice;
80265 for (;;) {
80266 drflac_uint8 bit;
80267 if (!drflac__read_uint8(bs, 1, &bit)) {
80268 return DRFLAC_FALSE;
80269 }
80270 if (bit == 0) {
80271 zeroCounter += 1;
80272 } else {
80273 break;
80274 }
80275 }
80276 if (riceParam > 0) {
80277 if (!drflac__read_uint32(bs, riceParam, &decodedRice)) {
80278 return DRFLAC_FALSE;
80279 }
80280 } else {
80281 decodedRice = 0;
80282 }
80283 *pZeroCounterOut = zeroCounter;
80284 *pRiceParamPartOut = decodedRice;
80285 return DRFLAC_TRUE;
80286}
80287#endif
80288#if 0
80289static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
80290{
80291 drflac_cache_t riceParamMask;
80292 drflac_uint32 zeroCounter;
80293 drflac_uint32 setBitOffsetPlus1;
80294 drflac_uint32 riceParamPart;
80295 drflac_uint32 riceLength;
80296 DRFLAC_ASSERT(riceParam > 0);
80297 riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam);
80298 zeroCounter = 0;
80299 while (bs->cache == 0) {
80300 zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
80301 if (!drflac__reload_cache(bs)) {
80302 return DRFLAC_FALSE;
80303 }
80304 }
80305 setBitOffsetPlus1 = drflac__clz(bs->cache);
80306 zeroCounter += setBitOffsetPlus1;
80307 setBitOffsetPlus1 += 1;
80308 riceLength = setBitOffsetPlus1 + riceParam;
80309 if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
80310 riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength));
80311 bs->consumedBits += riceLength;
80312 bs->cache <<= riceLength;
80313 } else {
80314 drflac_uint32 bitCountLo;
80315 drflac_cache_t resultHi;
80316 bs->consumedBits += riceLength;
80317 bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1);
80318 bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs);
80319 resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam);
80320 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
80321#ifndef DR_FLAC_NO_CRC
80322 drflac__update_crc16(bs);
80323#endif
80324 bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
80325 bs->consumedBits = 0;
80326#ifndef DR_FLAC_NO_CRC
80327 bs->crc16Cache = bs->cache;
80328#endif
80329 } else {
80330 if (!drflac__reload_cache(bs)) {
80331 return DRFLAC_FALSE;
80332 }
80333 if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
80334 return DRFLAC_FALSE;
80335 }
80336 }
80337 riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo));
80338 bs->consumedBits += bitCountLo;
80339 bs->cache <<= bitCountLo;
80340 }
80341 pZeroCounterOut[0] = zeroCounter;
80342 pRiceParamPartOut[0] = riceParamPart;
80343 return DRFLAC_TRUE;
80344}
80345#endif
80346static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
80347{
80348 drflac_uint32 riceParamPlus1 = riceParam + 1;
80349 drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1);
80350 drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
80351 drflac_cache_t bs_cache = bs->cache;
80352 drflac_uint32 bs_consumedBits = bs->consumedBits;
80353 drflac_uint32 lzcount = drflac__clz(bs_cache);
80354 if (lzcount < sizeof(bs_cache)*8) {
80355 pZeroCounterOut[0] = lzcount;
80356 extract_rice_param_part:
80357 bs_cache <<= lzcount;
80358 bs_consumedBits += lzcount;
80359 if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
80360 pRiceParamPartOut[0] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
80361 bs_cache <<= riceParamPlus1;
80362 bs_consumedBits += riceParamPlus1;
80363 } else {
80364 drflac_uint32 riceParamPartHi;
80365 drflac_uint32 riceParamPartLo;
80366 drflac_uint32 riceParamPartLoBitCount;
80367 riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
80368 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
80369 DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
80370 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
80371 #ifndef DR_FLAC_NO_CRC
80372 drflac__update_crc16(bs);
80373 #endif
80374 bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
80375 bs_consumedBits = riceParamPartLoBitCount;
80376 #ifndef DR_FLAC_NO_CRC
80377 bs->crc16Cache = bs_cache;
80378 #endif
80379 } else {
80380 if (!drflac__reload_cache(bs)) {
80381 return DRFLAC_FALSE;
80382 }
80383 if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
80384 return DRFLAC_FALSE;
80385 }
80386 bs_cache = bs->cache;
80387 bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
80388 }
80389 riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount)));
80390 pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo;
80391 bs_cache <<= riceParamPartLoBitCount;
80392 }
80393 } else {
80394 drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits);
80395 for (;;) {
80396 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
80397 #ifndef DR_FLAC_NO_CRC
80398 drflac__update_crc16(bs);
80399 #endif
80400 bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
80401 bs_consumedBits = 0;
80402 #ifndef DR_FLAC_NO_CRC
80403 bs->crc16Cache = bs_cache;
80404 #endif
80405 } else {
80406 if (!drflac__reload_cache(bs)) {
80407 return DRFLAC_FALSE;
80408 }
80409 bs_cache = bs->cache;
80410 bs_consumedBits = bs->consumedBits;
80411 }
80412 lzcount = drflac__clz(bs_cache);
80413 zeroCounter += lzcount;
80414 if (lzcount < sizeof(bs_cache)*8) {
80415 break;
80416 }
80417 }
80418 pZeroCounterOut[0] = zeroCounter;
80419 goto extract_rice_param_part;
80420 }
80421 bs->cache = bs_cache;
80422 bs->consumedBits = bs_consumedBits;
80423 return DRFLAC_TRUE;
80424}
80425static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac_uint8 riceParam)
80426{
80427 drflac_uint32 riceParamPlus1 = riceParam + 1;
80428 drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
80429 drflac_cache_t bs_cache = bs->cache;
80430 drflac_uint32 bs_consumedBits = bs->consumedBits;
80431 drflac_uint32 lzcount = drflac__clz(bs_cache);
80432 if (lzcount < sizeof(bs_cache)*8) {
80433 extract_rice_param_part:
80434 bs_cache <<= lzcount;
80435 bs_consumedBits += lzcount;
80436 if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
80437 bs_cache <<= riceParamPlus1;
80438 bs_consumedBits += riceParamPlus1;
80439 } else {
80440 drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
80441 DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
80442 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
80443 #ifndef DR_FLAC_NO_CRC
80444 drflac__update_crc16(bs);
80445 #endif
80446 bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
80447 bs_consumedBits = riceParamPartLoBitCount;
80448 #ifndef DR_FLAC_NO_CRC
80449 bs->crc16Cache = bs_cache;
80450 #endif
80451 } else {
80452 if (!drflac__reload_cache(bs)) {
80453 return DRFLAC_FALSE;
80454 }
80455 if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
80456 return DRFLAC_FALSE;
80457 }
80458 bs_cache = bs->cache;
80459 bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
80460 }
80461 bs_cache <<= riceParamPartLoBitCount;
80462 }
80463 } else {
80464 for (;;) {
80465 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
80466 #ifndef DR_FLAC_NO_CRC
80467 drflac__update_crc16(bs);
80468 #endif
80469 bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
80470 bs_consumedBits = 0;
80471 #ifndef DR_FLAC_NO_CRC
80472 bs->crc16Cache = bs_cache;
80473 #endif
80474 } else {
80475 if (!drflac__reload_cache(bs)) {
80476 return DRFLAC_FALSE;
80477 }
80478 bs_cache = bs->cache;
80479 bs_consumedBits = bs->consumedBits;
80480 }
80481 lzcount = drflac__clz(bs_cache);
80482 if (lzcount < sizeof(bs_cache)*8) {
80483 break;
80484 }
80485 }
80486 goto extract_rice_param_part;
80487 }
80488 bs->cache = bs_cache;
80489 bs->consumedBits = bs_consumedBits;
80490 return DRFLAC_TRUE;
80491}
80492static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorder(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
80493{
80494 drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
80495 drflac_uint32 zeroCountPart0;
80496 drflac_uint32 riceParamPart0;
80497 drflac_uint32 riceParamMask;
80498 drflac_uint32 i;
80499 DRFLAC_ASSERT(bs != NULL);
80500 DRFLAC_ASSERT(pSamplesOut != NULL);
80501 (void)bitsPerSample;
80502 (void)order;
80503 (void)shift;
80504 (void)coefficients;
80505 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
80506 i = 0;
80507 while (i < count) {
80508 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
80509 return DRFLAC_FALSE;
80510 }
80511 riceParamPart0 &= riceParamMask;
80512 riceParamPart0 |= (zeroCountPart0 << riceParam);
80513 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
80514 pSamplesOut[i] = riceParamPart0;
80515 i += 1;
80516 }
80517 return DRFLAC_TRUE;
80518}
80519static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
80520{
80521 drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
80522 drflac_uint32 zeroCountPart0 = 0;
80523 drflac_uint32 zeroCountPart1 = 0;
80524 drflac_uint32 zeroCountPart2 = 0;
80525 drflac_uint32 zeroCountPart3 = 0;
80526 drflac_uint32 riceParamPart0 = 0;
80527 drflac_uint32 riceParamPart1 = 0;
80528 drflac_uint32 riceParamPart2 = 0;
80529 drflac_uint32 riceParamPart3 = 0;
80530 drflac_uint32 riceParamMask;
80531 const drflac_int32* pSamplesOutEnd;
80532 drflac_uint32 i;
80533 DRFLAC_ASSERT(bs != NULL);
80534 DRFLAC_ASSERT(pSamplesOut != NULL);
80535 if (lpcOrder == 0) {
80536 return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
80537 }
80538 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
80539 pSamplesOutEnd = pSamplesOut + (count & ~3);
80540 if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
80541 while (pSamplesOut < pSamplesOutEnd) {
80542 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
80543 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
80544 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
80545 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
80546 return DRFLAC_FALSE;
80547 }
80548 riceParamPart0 &= riceParamMask;
80549 riceParamPart1 &= riceParamMask;
80550 riceParamPart2 &= riceParamMask;
80551 riceParamPart3 &= riceParamMask;
80552 riceParamPart0 |= (zeroCountPart0 << riceParam);
80553 riceParamPart1 |= (zeroCountPart1 << riceParam);
80554 riceParamPart2 |= (zeroCountPart2 << riceParam);
80555 riceParamPart3 |= (zeroCountPart3 << riceParam);
80556 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
80557 riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
80558 riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
80559 riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
80560 pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
80561 pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1);
80562 pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2);
80563 pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3);
80564 pSamplesOut += 4;
80565 }
80566 } else {
80567 while (pSamplesOut < pSamplesOutEnd) {
80568 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
80569 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
80570 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
80571 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
80572 return DRFLAC_FALSE;
80573 }
80574 riceParamPart0 &= riceParamMask;
80575 riceParamPart1 &= riceParamMask;
80576 riceParamPart2 &= riceParamMask;
80577 riceParamPart3 &= riceParamMask;
80578 riceParamPart0 |= (zeroCountPart0 << riceParam);
80579 riceParamPart1 |= (zeroCountPart1 << riceParam);
80580 riceParamPart2 |= (zeroCountPart2 << riceParam);
80581 riceParamPart3 |= (zeroCountPart3 << riceParam);
80582 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
80583 riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
80584 riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
80585 riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
80586 pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
80587 pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1);
80588 pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2);
80589 pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3);
80590 pSamplesOut += 4;
80591 }
80592 }
80593 i = (count & ~3);
80594 while (i < count) {
80595 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
80596 return DRFLAC_FALSE;
80597 }
80598 riceParamPart0 &= riceParamMask;
80599 riceParamPart0 |= (zeroCountPart0 << riceParam);
80600 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
80601 if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
80602 pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
80603 } else {
80604 pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
80605 }
80606 i += 1;
80607 pSamplesOut += 1;
80608 }
80609 return DRFLAC_TRUE;
80610}
80611#if defined(DRFLAC_SUPPORT_SSE2)
80612static DRFLAC_INLINE __m128i drflac__mm_packs_interleaved_epi32(__m128i a, __m128i b)
80613{
80614 __m128i r;
80615 r = _mm_packs_epi32(a, b);
80616 r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0));
80617 r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));
80618 r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));
80619 return r;
80620}
80621#endif
80622#if defined(DRFLAC_SUPPORT_SSE41)
80623static DRFLAC_INLINE __m128i drflac__mm_not_si128(__m128i a)
80624{
80625 return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128()));
80626}
80627static DRFLAC_INLINE __m128i drflac__mm_hadd_epi32(__m128i x)
80628{
80629 __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));
80630 __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2));
80631 return _mm_add_epi32(x64, x32);
80632}
80633static DRFLAC_INLINE __m128i drflac__mm_hadd_epi64(__m128i x)
80634{
80635 return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));
80636}
80637static DRFLAC_INLINE __m128i drflac__mm_srai_epi64(__m128i x, int count)
80638{
80639 __m128i lo = _mm_srli_epi64(x, count);
80640 __m128i hi = _mm_srai_epi32(x, count);
80641 hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0));
80642 return _mm_or_si128(lo, hi);
80643}
80644static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
80645{
80646 int i;
80647 drflac_uint32 riceParamMask;
80648 drflac_int32* pDecodedSamples = pSamplesOut;
80649 drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
80650 drflac_uint32 zeroCountParts0 = 0;
80651 drflac_uint32 zeroCountParts1 = 0;
80652 drflac_uint32 zeroCountParts2 = 0;
80653 drflac_uint32 zeroCountParts3 = 0;
80654 drflac_uint32 riceParamParts0 = 0;
80655 drflac_uint32 riceParamParts1 = 0;
80656 drflac_uint32 riceParamParts2 = 0;
80657 drflac_uint32 riceParamParts3 = 0;
80658 __m128i coefficients128_0;
80659 __m128i coefficients128_4;
80660 __m128i coefficients128_8;
80661 __m128i samples128_0;
80662 __m128i samples128_4;
80663 __m128i samples128_8;
80664 __m128i riceParamMask128;
80665 const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
80666 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
80667 riceParamMask128 = _mm_set1_epi32(riceParamMask);
80668 coefficients128_0 = _mm_setzero_si128();
80669 coefficients128_4 = _mm_setzero_si128();
80670 coefficients128_8 = _mm_setzero_si128();
80671 samples128_0 = _mm_setzero_si128();
80672 samples128_4 = _mm_setzero_si128();
80673 samples128_8 = _mm_setzero_si128();
80674#if 1
80675 {
80676 int runningOrder = order;
80677 if (runningOrder >= 4) {
80678 coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));
80679 samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4));
80680 runningOrder -= 4;
80681 } else {
80682 switch (runningOrder) {
80683 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;
80684 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;
80685 case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break;
80686 }
80687 runningOrder = 0;
80688 }
80689 if (runningOrder >= 4) {
80690 coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));
80691 samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8));
80692 runningOrder -= 4;
80693 } else {
80694 switch (runningOrder) {
80695 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;
80696 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;
80697 case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break;
80698 }
80699 runningOrder = 0;
80700 }
80701 if (runningOrder == 4) {
80702 coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));
80703 samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12));
80704 runningOrder -= 4;
80705 } else {
80706 switch (runningOrder) {
80707 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;
80708 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;
80709 case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break;
80710 }
80711 runningOrder = 0;
80712 }
80713 coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));
80714 coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));
80715 coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));
80716 }
80717#else
80718 switch (order)
80719 {
80720 case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12];
80721 case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11];
80722 case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10];
80723 case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9];
80724 case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8];
80725 case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7];
80726 case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6];
80727 case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5];
80728 case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4];
80729 case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3];
80730 case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2];
80731 case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1];
80732 }
80733#endif
80734 while (pDecodedSamples < pDecodedSamplesEnd) {
80735 __m128i prediction128;
80736 __m128i zeroCountPart128;
80737 __m128i riceParamPart128;
80738 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
80739 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
80740 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
80741 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
80742 return DRFLAC_FALSE;
80743 }
80744 zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
80745 riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
80746 riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
80747 riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
80748 riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01)));
80749 if (order <= 4) {
80750 for (i = 0; i < 4; i += 1) {
80751 prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0);
80752 prediction128 = drflac__mm_hadd_epi32(prediction128);
80753 prediction128 = _mm_srai_epi32(prediction128, shift);
80754 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
80755 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
80756 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
80757 }
80758 } else if (order <= 8) {
80759 for (i = 0; i < 4; i += 1) {
80760 prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4);
80761 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));
80762 prediction128 = drflac__mm_hadd_epi32(prediction128);
80763 prediction128 = _mm_srai_epi32(prediction128, shift);
80764 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
80765 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
80766 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
80767 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
80768 }
80769 } else {
80770 for (i = 0; i < 4; i += 1) {
80771 prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8);
80772 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4));
80773 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));
80774 prediction128 = drflac__mm_hadd_epi32(prediction128);
80775 prediction128 = _mm_srai_epi32(prediction128, shift);
80776 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
80777 samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4);
80778 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
80779 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
80780 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
80781 }
80782 }
80783 _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);
80784 pDecodedSamples += 4;
80785 }
80786 i = (count & ~3);
80787 while (i < (int)count) {
80788 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
80789 return DRFLAC_FALSE;
80790 }
80791 riceParamParts0 &= riceParamMask;
80792 riceParamParts0 |= (zeroCountParts0 << riceParam);
80793 riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
80794 pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);
80795 i += 1;
80796 pDecodedSamples += 1;
80797 }
80798 return DRFLAC_TRUE;
80799}
80800static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
80801{
80802 int i;
80803 drflac_uint32 riceParamMask;
80804 drflac_int32* pDecodedSamples = pSamplesOut;
80805 drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
80806 drflac_uint32 zeroCountParts0 = 0;
80807 drflac_uint32 zeroCountParts1 = 0;
80808 drflac_uint32 zeroCountParts2 = 0;
80809 drflac_uint32 zeroCountParts3 = 0;
80810 drflac_uint32 riceParamParts0 = 0;
80811 drflac_uint32 riceParamParts1 = 0;
80812 drflac_uint32 riceParamParts2 = 0;
80813 drflac_uint32 riceParamParts3 = 0;
80814 __m128i coefficients128_0;
80815 __m128i coefficients128_4;
80816 __m128i coefficients128_8;
80817 __m128i samples128_0;
80818 __m128i samples128_4;
80819 __m128i samples128_8;
80820 __m128i prediction128;
80821 __m128i riceParamMask128;
80822 const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
80823 DRFLAC_ASSERT(order <= 12);
80824 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
80825 riceParamMask128 = _mm_set1_epi32(riceParamMask);
80826 prediction128 = _mm_setzero_si128();
80827 coefficients128_0 = _mm_setzero_si128();
80828 coefficients128_4 = _mm_setzero_si128();
80829 coefficients128_8 = _mm_setzero_si128();
80830 samples128_0 = _mm_setzero_si128();
80831 samples128_4 = _mm_setzero_si128();
80832 samples128_8 = _mm_setzero_si128();
80833#if 1
80834 {
80835 int runningOrder = order;
80836 if (runningOrder >= 4) {
80837 coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));
80838 samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4));
80839 runningOrder -= 4;
80840 } else {
80841 switch (runningOrder) {
80842 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;
80843 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;
80844 case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break;
80845 }
80846 runningOrder = 0;
80847 }
80848 if (runningOrder >= 4) {
80849 coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));
80850 samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8));
80851 runningOrder -= 4;
80852 } else {
80853 switch (runningOrder) {
80854 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;
80855 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;
80856 case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break;
80857 }
80858 runningOrder = 0;
80859 }
80860 if (runningOrder == 4) {
80861 coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));
80862 samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12));
80863 runningOrder -= 4;
80864 } else {
80865 switch (runningOrder) {
80866 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;
80867 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;
80868 case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break;
80869 }
80870 runningOrder = 0;
80871 }
80872 coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));
80873 coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));
80874 coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));
80875 }
80876#else
80877 switch (order)
80878 {
80879 case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12];
80880 case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11];
80881 case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10];
80882 case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9];
80883 case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8];
80884 case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7];
80885 case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6];
80886 case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5];
80887 case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4];
80888 case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3];
80889 case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2];
80890 case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1];
80891 }
80892#endif
80893 while (pDecodedSamples < pDecodedSamplesEnd) {
80894 __m128i zeroCountPart128;
80895 __m128i riceParamPart128;
80896 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
80897 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
80898 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
80899 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
80900 return DRFLAC_FALSE;
80901 }
80902 zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
80903 riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
80904 riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
80905 riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
80906 riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1)));
80907 for (i = 0; i < 4; i += 1) {
80908 prediction128 = _mm_xor_si128(prediction128, prediction128);
80909 switch (order)
80910 {
80911 case 12:
80912 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))));
80913 case 10:
80914 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))));
80915 case 8:
80916 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))));
80917 case 6:
80918 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))));
80919 case 4:
80920 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))));
80921 case 2:
80922 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))));
80923 }
80924 prediction128 = drflac__mm_hadd_epi64(prediction128);
80925 prediction128 = drflac__mm_srai_epi64(prediction128, shift);
80926 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
80927 samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4);
80928 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
80929 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
80930 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
80931 }
80932 _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);
80933 pDecodedSamples += 4;
80934 }
80935 i = (count & ~3);
80936 while (i < (int)count) {
80937 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
80938 return DRFLAC_FALSE;
80939 }
80940 riceParamParts0 &= riceParamMask;
80941 riceParamParts0 |= (zeroCountParts0 << riceParam);
80942 riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
80943 pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);
80944 i += 1;
80945 pDecodedSamples += 1;
80946 }
80947 return DRFLAC_TRUE;
80948}
80949static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
80950{
80951 DRFLAC_ASSERT(bs != NULL);
80952 DRFLAC_ASSERT(pSamplesOut != NULL);
80953 if (lpcOrder > 0 && lpcOrder <= 12) {
80954 if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
80955 return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
80956 } else {
80957 return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
80958 }
80959 } else {
80960 return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
80961 }
80962}
80963#endif
80964#if defined(DRFLAC_SUPPORT_NEON)
80965static DRFLAC_INLINE void drflac__vst2q_s32(drflac_int32* p, int32x4x2_t x)
80966{
80967 vst1q_s32(p+0, x.val[0]);
80968 vst1q_s32(p+4, x.val[1]);
80969}
80970static DRFLAC_INLINE void drflac__vst2q_u32(drflac_uint32* p, uint32x4x2_t x)
80971{
80972 vst1q_u32(p+0, x.val[0]);
80973 vst1q_u32(p+4, x.val[1]);
80974}
80975static DRFLAC_INLINE void drflac__vst2q_f32(float* p, float32x4x2_t x)
80976{
80977 vst1q_f32(p+0, x.val[0]);
80978 vst1q_f32(p+4, x.val[1]);
80979}
80980static DRFLAC_INLINE void drflac__vst2q_s16(drflac_int16* p, int16x4x2_t x)
80981{
80982 vst1q_s16(p, vcombine_s16(x.val[0], x.val[1]));
80983}
80984static DRFLAC_INLINE void drflac__vst2q_u16(drflac_uint16* p, uint16x4x2_t x)
80985{
80986 vst1q_u16(p, vcombine_u16(x.val[0], x.val[1]));
80987}
80988static DRFLAC_INLINE int32x4_t drflac__vdupq_n_s32x4(drflac_int32 x3, drflac_int32 x2, drflac_int32 x1, drflac_int32 x0)
80989{
80990 drflac_int32 x[4];
80991 x[3] = x3;
80992 x[2] = x2;
80993 x[1] = x1;
80994 x[0] = x0;
80995 return vld1q_s32(x);
80996}
80997static DRFLAC_INLINE int32x4_t drflac__valignrq_s32_1(int32x4_t a, int32x4_t b)
80998{
80999 return vextq_s32(b, a, 1);
81000}
81001static DRFLAC_INLINE uint32x4_t drflac__valignrq_u32_1(uint32x4_t a, uint32x4_t b)
81002{
81003 return vextq_u32(b, a, 1);
81004}
81005static DRFLAC_INLINE int32x2_t drflac__vhaddq_s32(int32x4_t x)
81006{
81007 int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x));
81008 return vpadd_s32(r, r);
81009}
81010static DRFLAC_INLINE int64x1_t drflac__vhaddq_s64(int64x2_t x)
81011{
81012 return vadd_s64(vget_high_s64(x), vget_low_s64(x));
81013}
81014static DRFLAC_INLINE int32x4_t drflac__vrevq_s32(int32x4_t x)
81015{
81016 return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x)));
81017}
81018static DRFLAC_INLINE int32x4_t drflac__vnotq_s32(int32x4_t x)
81019{
81020 return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF));
81021}
81022static DRFLAC_INLINE uint32x4_t drflac__vnotq_u32(uint32x4_t x)
81023{
81024 return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF));
81025}
81026static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
81027{
81028 int i;
81029 drflac_uint32 riceParamMask;
81030 drflac_int32* pDecodedSamples = pSamplesOut;
81031 drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
81032 drflac_uint32 zeroCountParts[4];
81033 drflac_uint32 riceParamParts[4];
81034 int32x4_t coefficients128_0;
81035 int32x4_t coefficients128_4;
81036 int32x4_t coefficients128_8;
81037 int32x4_t samples128_0;
81038 int32x4_t samples128_4;
81039 int32x4_t samples128_8;
81040 uint32x4_t riceParamMask128;
81041 int32x4_t riceParam128;
81042 int32x2_t shift64;
81043 uint32x4_t one128;
81044 const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
81045 riceParamMask = ~((~0UL) << riceParam);
81046 riceParamMask128 = vdupq_n_u32(riceParamMask);
81047 riceParam128 = vdupq_n_s32(riceParam);
81048 shift64 = vdup_n_s32(-shift);
81049 one128 = vdupq_n_u32(1);
81050 {
81051 int runningOrder = order;
81052 drflac_int32 tempC[4] = {0, 0, 0, 0};
81053 drflac_int32 tempS[4] = {0, 0, 0, 0};
81054 if (runningOrder >= 4) {
81055 coefficients128_0 = vld1q_s32(coefficients + 0);
81056 samples128_0 = vld1q_s32(pSamplesOut - 4);
81057 runningOrder -= 4;
81058 } else {
81059 switch (runningOrder) {
81060 case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3];
81061 case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2];
81062 case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1];
81063 }
81064 coefficients128_0 = vld1q_s32(tempC);
81065 samples128_0 = vld1q_s32(tempS);
81066 runningOrder = 0;
81067 }
81068 if (runningOrder >= 4) {
81069 coefficients128_4 = vld1q_s32(coefficients + 4);
81070 samples128_4 = vld1q_s32(pSamplesOut - 8);
81071 runningOrder -= 4;
81072 } else {
81073 switch (runningOrder) {
81074 case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7];
81075 case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6];
81076 case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5];
81077 }
81078 coefficients128_4 = vld1q_s32(tempC);
81079 samples128_4 = vld1q_s32(tempS);
81080 runningOrder = 0;
81081 }
81082 if (runningOrder == 4) {
81083 coefficients128_8 = vld1q_s32(coefficients + 8);
81084 samples128_8 = vld1q_s32(pSamplesOut - 12);
81085 runningOrder -= 4;
81086 } else {
81087 switch (runningOrder) {
81088 case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11];
81089 case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10];
81090 case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9];
81091 }
81092 coefficients128_8 = vld1q_s32(tempC);
81093 samples128_8 = vld1q_s32(tempS);
81094 runningOrder = 0;
81095 }
81096 coefficients128_0 = drflac__vrevq_s32(coefficients128_0);
81097 coefficients128_4 = drflac__vrevq_s32(coefficients128_4);
81098 coefficients128_8 = drflac__vrevq_s32(coefficients128_8);
81099 }
81100 while (pDecodedSamples < pDecodedSamplesEnd) {
81101 int32x4_t prediction128;
81102 int32x2_t prediction64;
81103 uint32x4_t zeroCountPart128;
81104 uint32x4_t riceParamPart128;
81105 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||
81106 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||
81107 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||
81108 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {
81109 return DRFLAC_FALSE;
81110 }
81111 zeroCountPart128 = vld1q_u32(zeroCountParts);
81112 riceParamPart128 = vld1q_u32(riceParamParts);
81113 riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);
81114 riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));
81115 riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));
81116 if (order <= 4) {
81117 for (i = 0; i < 4; i += 1) {
81118 prediction128 = vmulq_s32(coefficients128_0, samples128_0);
81119 prediction64 = drflac__vhaddq_s32(prediction128);
81120 prediction64 = vshl_s32(prediction64, shift64);
81121 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
81122 samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
81123 riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
81124 }
81125 } else if (order <= 8) {
81126 for (i = 0; i < 4; i += 1) {
81127 prediction128 = vmulq_s32(coefficients128_4, samples128_4);
81128 prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);
81129 prediction64 = drflac__vhaddq_s32(prediction128);
81130 prediction64 = vshl_s32(prediction64, shift64);
81131 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
81132 samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4);
81133 samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
81134 riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
81135 }
81136 } else {
81137 for (i = 0; i < 4; i += 1) {
81138 prediction128 = vmulq_s32(coefficients128_8, samples128_8);
81139 prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4);
81140 prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);
81141 prediction64 = drflac__vhaddq_s32(prediction128);
81142 prediction64 = vshl_s32(prediction64, shift64);
81143 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
81144 samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8);
81145 samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4);
81146 samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
81147 riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
81148 }
81149 }
81150 vst1q_s32(pDecodedSamples, samples128_0);
81151 pDecodedSamples += 4;
81152 }
81153 i = (count & ~3);
81154 while (i < (int)count) {
81155 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {
81156 return DRFLAC_FALSE;
81157 }
81158 riceParamParts[0] &= riceParamMask;
81159 riceParamParts[0] |= (zeroCountParts[0] << riceParam);
81160 riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];
81161 pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);
81162 i += 1;
81163 pDecodedSamples += 1;
81164 }
81165 return DRFLAC_TRUE;
81166}
81167static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
81168{
81169 int i;
81170 drflac_uint32 riceParamMask;
81171 drflac_int32* pDecodedSamples = pSamplesOut;
81172 drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
81173 drflac_uint32 zeroCountParts[4];
81174 drflac_uint32 riceParamParts[4];
81175 int32x4_t coefficients128_0;
81176 int32x4_t coefficients128_4;
81177 int32x4_t coefficients128_8;
81178 int32x4_t samples128_0;
81179 int32x4_t samples128_4;
81180 int32x4_t samples128_8;
81181 uint32x4_t riceParamMask128;
81182 int32x4_t riceParam128;
81183 int64x1_t shift64;
81184 uint32x4_t one128;
81185 const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
81186 riceParamMask = ~((~0UL) << riceParam);
81187 riceParamMask128 = vdupq_n_u32(riceParamMask);
81188 riceParam128 = vdupq_n_s32(riceParam);
81189 shift64 = vdup_n_s64(-shift);
81190 one128 = vdupq_n_u32(1);
81191 {
81192 int runningOrder = order;
81193 drflac_int32 tempC[4] = {0, 0, 0, 0};
81194 drflac_int32 tempS[4] = {0, 0, 0, 0};
81195 if (runningOrder >= 4) {
81196 coefficients128_0 = vld1q_s32(coefficients + 0);
81197 samples128_0 = vld1q_s32(pSamplesOut - 4);
81198 runningOrder -= 4;
81199 } else {
81200 switch (runningOrder) {
81201 case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3];
81202 case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2];
81203 case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1];
81204 }
81205 coefficients128_0 = vld1q_s32(tempC);
81206 samples128_0 = vld1q_s32(tempS);
81207 runningOrder = 0;
81208 }
81209 if (runningOrder >= 4) {
81210 coefficients128_4 = vld1q_s32(coefficients + 4);
81211 samples128_4 = vld1q_s32(pSamplesOut - 8);
81212 runningOrder -= 4;
81213 } else {
81214 switch (runningOrder) {
81215 case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7];
81216 case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6];
81217 case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5];
81218 }
81219 coefficients128_4 = vld1q_s32(tempC);
81220 samples128_4 = vld1q_s32(tempS);
81221 runningOrder = 0;
81222 }
81223 if (runningOrder == 4) {
81224 coefficients128_8 = vld1q_s32(coefficients + 8);
81225 samples128_8 = vld1q_s32(pSamplesOut - 12);
81226 runningOrder -= 4;
81227 } else {
81228 switch (runningOrder) {
81229 case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11];
81230 case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10];
81231 case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9];
81232 }
81233 coefficients128_8 = vld1q_s32(tempC);
81234 samples128_8 = vld1q_s32(tempS);
81235 runningOrder = 0;
81236 }
81237 coefficients128_0 = drflac__vrevq_s32(coefficients128_0);
81238 coefficients128_4 = drflac__vrevq_s32(coefficients128_4);
81239 coefficients128_8 = drflac__vrevq_s32(coefficients128_8);
81240 }
81241 while (pDecodedSamples < pDecodedSamplesEnd) {
81242 int64x2_t prediction128;
81243 uint32x4_t zeroCountPart128;
81244 uint32x4_t riceParamPart128;
81245 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||
81246 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||
81247 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||
81248 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {
81249 return DRFLAC_FALSE;
81250 }
81251 zeroCountPart128 = vld1q_u32(zeroCountParts);
81252 riceParamPart128 = vld1q_u32(riceParamParts);
81253 riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);
81254 riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));
81255 riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));
81256 for (i = 0; i < 4; i += 1) {
81257 int64x1_t prediction64;
81258 prediction128 = veorq_s64(prediction128, prediction128);
81259 switch (order)
81260 {
81261 case 12:
81262 case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8)));
81263 case 10:
81264 case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8)));
81265 case 8:
81266 case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4)));
81267 case 6:
81268 case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4)));
81269 case 4:
81270 case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0)));
81271 case 2:
81272 case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0)));
81273 }
81274 prediction64 = drflac__vhaddq_s64(prediction128);
81275 prediction64 = vshl_s64(prediction64, shift64);
81276 prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0)));
81277 samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8);
81278 samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4);
81279 samples128_0 = drflac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0);
81280 riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
81281 }
81282 vst1q_s32(pDecodedSamples, samples128_0);
81283 pDecodedSamples += 4;
81284 }
81285 i = (count & ~3);
81286 while (i < (int)count) {
81287 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {
81288 return DRFLAC_FALSE;
81289 }
81290 riceParamParts[0] &= riceParamMask;
81291 riceParamParts[0] |= (zeroCountParts[0] << riceParam);
81292 riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];
81293 pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);
81294 i += 1;
81295 pDecodedSamples += 1;
81296 }
81297 return DRFLAC_TRUE;
81298}
81299static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
81300{
81301 DRFLAC_ASSERT(bs != NULL);
81302 DRFLAC_ASSERT(pSamplesOut != NULL);
81303 if (lpcOrder > 0 && lpcOrder <= 12) {
81304 if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
81305 return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
81306 } else {
81307 return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
81308 }
81309 } else {
81310 return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
81311 }
81312}
81313#endif
81314static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
81315{
81316#if defined(DRFLAC_SUPPORT_SSE41)
81317 if (drflac__gIsSSE41Supported) {
81318 return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
81319 } else
81320#elif defined(DRFLAC_SUPPORT_NEON)
81321 if (drflac__gIsNEONSupported) {
81322 return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
81323 } else
81324#endif
81325 {
81326 #if 0
81327 return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
81328 #else
81329 return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
81330 #endif
81331 }
81332}
81333static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam)
81334{
81335 drflac_uint32 i;
81336 DRFLAC_ASSERT(bs != NULL);
81337 for (i = 0; i < count; ++i) {
81338 if (!drflac__seek_rice_parts(bs, riceParam)) {
81339 return DRFLAC_FALSE;
81340 }
81341 }
81342 return DRFLAC_TRUE;
81343}
81344#if defined(__clang__)
81345__attribute__((no_sanitize("signed-integer-overflow")))
81346#endif
81347static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
81348{
81349 drflac_uint32 i;
81350 DRFLAC_ASSERT(bs != NULL);
81351 DRFLAC_ASSERT(unencodedBitsPerSample <= 31);
81352 DRFLAC_ASSERT(pSamplesOut != NULL);
81353 for (i = 0; i < count; ++i) {
81354 if (unencodedBitsPerSample > 0) {
81355 if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) {
81356 return DRFLAC_FALSE;
81357 }
81358 } else {
81359 pSamplesOut[i] = 0;
81360 }
81361 if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
81362 pSamplesOut[i] += drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
81363 } else {
81364 pSamplesOut[i] += drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
81365 }
81366 }
81367 return DRFLAC_TRUE;
81368}
81369static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
81370{
81371 drflac_uint8 residualMethod;
81372 drflac_uint8 partitionOrder;
81373 drflac_uint32 samplesInPartition;
81374 drflac_uint32 partitionsRemaining;
81375 DRFLAC_ASSERT(bs != NULL);
81376 DRFLAC_ASSERT(blockSize != 0);
81377 DRFLAC_ASSERT(pDecodedSamples != NULL);
81378 if (!drflac__read_uint8(bs, 2, &residualMethod)) {
81379 return DRFLAC_FALSE;
81380 }
81381 if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
81382 return DRFLAC_FALSE;
81383 }
81384 pDecodedSamples += lpcOrder;
81385 if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
81386 return DRFLAC_FALSE;
81387 }
81388 if (partitionOrder > 8) {
81389 return DRFLAC_FALSE;
81390 }
81391 if ((blockSize / (1 << partitionOrder)) < lpcOrder) {
81392 return DRFLAC_FALSE;
81393 }
81394 samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder;
81395 partitionsRemaining = (1 << partitionOrder);
81396 for (;;) {
81397 drflac_uint8 riceParam = 0;
81398 if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
81399 if (!drflac__read_uint8(bs, 4, &riceParam)) {
81400 return DRFLAC_FALSE;
81401 }
81402 if (riceParam == 15) {
81403 riceParam = 0xFF;
81404 }
81405 } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
81406 if (!drflac__read_uint8(bs, 5, &riceParam)) {
81407 return DRFLAC_FALSE;
81408 }
81409 if (riceParam == 31) {
81410 riceParam = 0xFF;
81411 }
81412 }
81413 if (riceParam != 0xFF) {
81414 if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
81415 return DRFLAC_FALSE;
81416 }
81417 } else {
81418 drflac_uint8 unencodedBitsPerSample = 0;
81419 if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
81420 return DRFLAC_FALSE;
81421 }
81422 if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
81423 return DRFLAC_FALSE;
81424 }
81425 }
81426 pDecodedSamples += samplesInPartition;
81427 if (partitionsRemaining == 1) {
81428 break;
81429 }
81430 partitionsRemaining -= 1;
81431 if (partitionOrder != 0) {
81432 samplesInPartition = blockSize / (1 << partitionOrder);
81433 }
81434 }
81435 return DRFLAC_TRUE;
81436}
81437static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order)
81438{
81439 drflac_uint8 residualMethod;
81440 drflac_uint8 partitionOrder;
81441 drflac_uint32 samplesInPartition;
81442 drflac_uint32 partitionsRemaining;
81443 DRFLAC_ASSERT(bs != NULL);
81444 DRFLAC_ASSERT(blockSize != 0);
81445 if (!drflac__read_uint8(bs, 2, &residualMethod)) {
81446 return DRFLAC_FALSE;
81447 }
81448 if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
81449 return DRFLAC_FALSE;
81450 }
81451 if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
81452 return DRFLAC_FALSE;
81453 }
81454 if (partitionOrder > 8) {
81455 return DRFLAC_FALSE;
81456 }
81457 if ((blockSize / (1 << partitionOrder)) <= order) {
81458 return DRFLAC_FALSE;
81459 }
81460 samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
81461 partitionsRemaining = (1 << partitionOrder);
81462 for (;;)
81463 {
81464 drflac_uint8 riceParam = 0;
81465 if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
81466 if (!drflac__read_uint8(bs, 4, &riceParam)) {
81467 return DRFLAC_FALSE;
81468 }
81469 if (riceParam == 15) {
81470 riceParam = 0xFF;
81471 }
81472 } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
81473 if (!drflac__read_uint8(bs, 5, &riceParam)) {
81474 return DRFLAC_FALSE;
81475 }
81476 if (riceParam == 31) {
81477 riceParam = 0xFF;
81478 }
81479 }
81480 if (riceParam != 0xFF) {
81481 if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) {
81482 return DRFLAC_FALSE;
81483 }
81484 } else {
81485 drflac_uint8 unencodedBitsPerSample = 0;
81486 if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
81487 return DRFLAC_FALSE;
81488 }
81489 if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) {
81490 return DRFLAC_FALSE;
81491 }
81492 }
81493 if (partitionsRemaining == 1) {
81494 break;
81495 }
81496 partitionsRemaining -= 1;
81497 samplesInPartition = blockSize / (1 << partitionOrder);
81498 }
81499 return DRFLAC_TRUE;
81500}
81501static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples)
81502{
81503 drflac_uint32 i;
81505 if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) {
81506 return DRFLAC_FALSE;
81507 }
81508 for (i = 0; i < blockSize; ++i) {
81509 pDecodedSamples[i] = sample;
81510 }
81511 return DRFLAC_TRUE;
81512}
81513static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples)
81514{
81515 drflac_uint32 i;
81516 for (i = 0; i < blockSize; ++i) {
81518 if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) {
81519 return DRFLAC_FALSE;
81520 }
81521 pDecodedSamples[i] = sample;
81522 }
81523 return DRFLAC_TRUE;
81524}
81525static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples)
81526{
81527 drflac_uint32 i;
81528 static drflac_int32 lpcCoefficientsTable[5][4] = {
81529 {0, 0, 0, 0},
81530 {1, 0, 0, 0},
81531 {2, -1, 0, 0},
81532 {3, -3, 1, 0},
81533 {4, -6, 4, -1}
81534 };
81535 for (i = 0; i < lpcOrder; ++i) {
81537 if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) {
81538 return DRFLAC_FALSE;
81539 }
81540 pDecodedSamples[i] = sample;
81541 }
81542 if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) {
81543 return DRFLAC_FALSE;
81544 }
81545 return DRFLAC_TRUE;
81546}
81547static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples)
81548{
81549 drflac_uint8 i;
81550 drflac_uint8 lpcPrecision;
81551 drflac_int8 lpcShift;
81552 drflac_int32 coefficients[32];
81553 for (i = 0; i < lpcOrder; ++i) {
81555 if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
81556 return DRFLAC_FALSE;
81557 }
81558 pDecodedSamples[i] = sample;
81559 }
81560 if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
81561 return DRFLAC_FALSE;
81562 }
81563 if (lpcPrecision == 15) {
81564 return DRFLAC_FALSE;
81565 }
81566 lpcPrecision += 1;
81567 if (!drflac__read_int8(bs, 5, &lpcShift)) {
81568 return DRFLAC_FALSE;
81569 }
81570 if (lpcShift < 0) {
81571 return DRFLAC_FALSE;
81572 }
81573 DRFLAC_ZERO_MEMORY(coefficients, sizeof(coefficients));
81574 for (i = 0; i < lpcOrder; ++i) {
81575 if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) {
81576 return DRFLAC_FALSE;
81577 }
81578 }
81579 if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
81580 return DRFLAC_FALSE;
81581 }
81582 return DRFLAC_TRUE;
81583}
81584static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header)
81585{
81586 const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000};
81587 const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1};
81588 DRFLAC_ASSERT(bs != NULL);
81589 DRFLAC_ASSERT(header != NULL);
81590 for (;;) {
81591 drflac_uint8 crc8 = 0xCE;
81592 drflac_uint8 reserved = 0;
81593 drflac_uint8 blockingStrategy = 0;
81594 drflac_uint8 blockSize = 0;
81595 drflac_uint8 sampleRate = 0;
81596 drflac_uint8 channelAssignment = 0;
81597 drflac_uint8 bitsPerSample = 0;
81598 drflac_bool32 isVariableBlockSize;
81599 if (!drflac__find_and_seek_to_next_sync_code(bs)) {
81600 return DRFLAC_FALSE;
81601 }
81602 if (!drflac__read_uint8(bs, 1, &reserved)) {
81603 return DRFLAC_FALSE;
81604 }
81605 if (reserved == 1) {
81606 continue;
81607 }
81608 crc8 = drflac_crc8(crc8, reserved, 1);
81609 if (!drflac__read_uint8(bs, 1, &blockingStrategy)) {
81610 return DRFLAC_FALSE;
81611 }
81612 crc8 = drflac_crc8(crc8, blockingStrategy, 1);
81613 if (!drflac__read_uint8(bs, 4, &blockSize)) {
81614 return DRFLAC_FALSE;
81615 }
81616 if (blockSize == 0) {
81617 continue;
81618 }
81619 crc8 = drflac_crc8(crc8, blockSize, 4);
81620 if (!drflac__read_uint8(bs, 4, &sampleRate)) {
81621 return DRFLAC_FALSE;
81622 }
81623 crc8 = drflac_crc8(crc8, sampleRate, 4);
81624 if (!drflac__read_uint8(bs, 4, &channelAssignment)) {
81625 return DRFLAC_FALSE;
81626 }
81627 if (channelAssignment > 10) {
81628 continue;
81629 }
81630 crc8 = drflac_crc8(crc8, channelAssignment, 4);
81631 if (!drflac__read_uint8(bs, 3, &bitsPerSample)) {
81632 return DRFLAC_FALSE;
81633 }
81634 if (bitsPerSample == 3 || bitsPerSample == 7) {
81635 continue;
81636 }
81637 crc8 = drflac_crc8(crc8, bitsPerSample, 3);
81638 if (!drflac__read_uint8(bs, 1, &reserved)) {
81639 return DRFLAC_FALSE;
81640 }
81641 if (reserved == 1) {
81642 continue;
81643 }
81644 crc8 = drflac_crc8(crc8, reserved, 1);
81645 isVariableBlockSize = blockingStrategy == 1;
81646 if (isVariableBlockSize) {
81647 drflac_uint64 pcmFrameNumber;
81648 drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8);
81649 if (result != DRFLAC_SUCCESS) {
81650 if (result == DRFLAC_AT_END) {
81651 return DRFLAC_FALSE;
81652 } else {
81653 continue;
81654 }
81655 }
81656 header->flacFrameNumber = 0;
81657 header->pcmFrameNumber = pcmFrameNumber;
81658 } else {
81659 drflac_uint64 flacFrameNumber = 0;
81660 drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8);
81661 if (result != DRFLAC_SUCCESS) {
81662 if (result == DRFLAC_AT_END) {
81663 return DRFLAC_FALSE;
81664 } else {
81665 continue;
81666 }
81667 }
81668 header->flacFrameNumber = (drflac_uint32)flacFrameNumber;
81669 header->pcmFrameNumber = 0;
81670 }
81671 DRFLAC_ASSERT(blockSize > 0);
81672 if (blockSize == 1) {
81673 header->blockSizeInPCMFrames = 192;
81674 } else if (blockSize <= 5) {
81675 DRFLAC_ASSERT(blockSize >= 2);
81676 header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2));
81677 } else if (blockSize == 6) {
81678 if (!drflac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) {
81679 return DRFLAC_FALSE;
81680 }
81681 crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 8);
81682 header->blockSizeInPCMFrames += 1;
81683 } else if (blockSize == 7) {
81684 if (!drflac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) {
81685 return DRFLAC_FALSE;
81686 }
81687 crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16);
81688 if (header->blockSizeInPCMFrames == 0xFFFF) {
81689 return DRFLAC_FALSE;
81690 }
81691 header->blockSizeInPCMFrames += 1;
81692 } else {
81693 DRFLAC_ASSERT(blockSize >= 8);
81694 header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8));
81695 }
81696 if (sampleRate <= 11) {
81697 header->sampleRate = sampleRateTable[sampleRate];
81698 } else if (sampleRate == 12) {
81699 if (!drflac__read_uint32(bs, 8, &header->sampleRate)) {
81700 return DRFLAC_FALSE;
81701 }
81702 crc8 = drflac_crc8(crc8, header->sampleRate, 8);
81703 header->sampleRate *= 1000;
81704 } else if (sampleRate == 13) {
81705 if (!drflac__read_uint32(bs, 16, &header->sampleRate)) {
81706 return DRFLAC_FALSE;
81707 }
81708 crc8 = drflac_crc8(crc8, header->sampleRate, 16);
81709 } else if (sampleRate == 14) {
81710 if (!drflac__read_uint32(bs, 16, &header->sampleRate)) {
81711 return DRFLAC_FALSE;
81712 }
81713 crc8 = drflac_crc8(crc8, header->sampleRate, 16);
81714 header->sampleRate *= 10;
81715 } else {
81716 continue;
81717 }
81718 header->channelAssignment = channelAssignment;
81719 header->bitsPerSample = bitsPerSampleTable[bitsPerSample];
81720 if (header->bitsPerSample == 0) {
81721 header->bitsPerSample = streaminfoBitsPerSample;
81722 }
81723 if (header->bitsPerSample != streaminfoBitsPerSample) {
81724 return DRFLAC_FALSE;
81725 }
81726 if (!drflac__read_uint8(bs, 8, &header->crc8)) {
81727 return DRFLAC_FALSE;
81728 }
81729#ifndef DR_FLAC_NO_CRC
81730 if (header->crc8 != crc8) {
81731 continue;
81732 }
81733#endif
81734 return DRFLAC_TRUE;
81735 }
81736}
81737static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe)
81738{
81739 drflac_uint8 header;
81740 int type;
81741 if (!drflac__read_uint8(bs, 8, &header)) {
81742 return DRFLAC_FALSE;
81743 }
81744 if ((header & 0x80) != 0) {
81745 return DRFLAC_FALSE;
81746 }
81747 type = (header & 0x7E) >> 1;
81748 if (type == 0) {
81749 pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT;
81750 } else if (type == 1) {
81751 pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM;
81752 } else {
81753 if ((type & 0x20) != 0) {
81754 pSubframe->subframeType = DRFLAC_SUBFRAME_LPC;
81755 pSubframe->lpcOrder = (drflac_uint8)(type & 0x1F) + 1;
81756 } else if ((type & 0x08) != 0) {
81757 pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED;
81758 pSubframe->lpcOrder = (drflac_uint8)(type & 0x07);
81759 if (pSubframe->lpcOrder > 4) {
81760 pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED;
81761 pSubframe->lpcOrder = 0;
81762 }
81763 } else {
81764 pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED;
81765 }
81766 }
81767 if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) {
81768 return DRFLAC_FALSE;
81769 }
81770 pSubframe->wastedBitsPerSample = 0;
81771 if ((header & 0x01) == 1) {
81772 unsigned int wastedBitsPerSample;
81773 if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) {
81774 return DRFLAC_FALSE;
81775 }
81776 pSubframe->wastedBitsPerSample = (drflac_uint8)wastedBitsPerSample + 1;
81777 }
81778 return DRFLAC_TRUE;
81779}
81780static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut)
81781{
81782 drflac_subframe* pSubframe;
81783 drflac_uint32 subframeBitsPerSample;
81784 DRFLAC_ASSERT(bs != NULL);
81785 DRFLAC_ASSERT(frame != NULL);
81786 pSubframe = frame->subframes + subframeIndex;
81787 if (!drflac__read_subframe_header(bs, pSubframe)) {
81788 return DRFLAC_FALSE;
81789 }
81790 subframeBitsPerSample = frame->header.bitsPerSample;
81791 if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
81792 subframeBitsPerSample += 1;
81793 } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
81794 subframeBitsPerSample += 1;
81795 }
81796 if (subframeBitsPerSample > 32) {
81797 return DRFLAC_FALSE;
81798 }
81799 if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
81800 return DRFLAC_FALSE;
81801 }
81802 subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
81803 pSubframe->pSamplesS32 = pDecodedSamplesOut;
81804 switch (pSubframe->subframeType)
81805 {
81806 case DRFLAC_SUBFRAME_CONSTANT:
81807 {
81808 drflac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
81809 } break;
81810 case DRFLAC_SUBFRAME_VERBATIM:
81811 {
81812 drflac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
81813 } break;
81814 case DRFLAC_SUBFRAME_FIXED:
81815 {
81816 drflac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
81817 } break;
81818 case DRFLAC_SUBFRAME_LPC:
81819 {
81820 drflac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
81821 } break;
81822 default: return DRFLAC_FALSE;
81823 }
81824 return DRFLAC_TRUE;
81825}
81826static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex)
81827{
81828 drflac_subframe* pSubframe;
81829 drflac_uint32 subframeBitsPerSample;
81830 DRFLAC_ASSERT(bs != NULL);
81831 DRFLAC_ASSERT(frame != NULL);
81832 pSubframe = frame->subframes + subframeIndex;
81833 if (!drflac__read_subframe_header(bs, pSubframe)) {
81834 return DRFLAC_FALSE;
81835 }
81836 subframeBitsPerSample = frame->header.bitsPerSample;
81837 if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
81838 subframeBitsPerSample += 1;
81839 } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
81840 subframeBitsPerSample += 1;
81841 }
81842 if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
81843 return DRFLAC_FALSE;
81844 }
81845 subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
81846 pSubframe->pSamplesS32 = NULL;
81847 switch (pSubframe->subframeType)
81848 {
81849 case DRFLAC_SUBFRAME_CONSTANT:
81850 {
81851 if (!drflac__seek_bits(bs, subframeBitsPerSample)) {
81852 return DRFLAC_FALSE;
81853 }
81854 } break;
81855 case DRFLAC_SUBFRAME_VERBATIM:
81856 {
81857 unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample;
81858 if (!drflac__seek_bits(bs, bitsToSeek)) {
81859 return DRFLAC_FALSE;
81860 }
81861 } break;
81862 case DRFLAC_SUBFRAME_FIXED:
81863 {
81864 unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;
81865 if (!drflac__seek_bits(bs, bitsToSeek)) {
81866 return DRFLAC_FALSE;
81867 }
81868 if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {
81869 return DRFLAC_FALSE;
81870 }
81871 } break;
81872 case DRFLAC_SUBFRAME_LPC:
81873 {
81874 drflac_uint8 lpcPrecision;
81875 unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;
81876 if (!drflac__seek_bits(bs, bitsToSeek)) {
81877 return DRFLAC_FALSE;
81878 }
81879 if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
81880 return DRFLAC_FALSE;
81881 }
81882 if (lpcPrecision == 15) {
81883 return DRFLAC_FALSE;
81884 }
81885 lpcPrecision += 1;
81886 bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5;
81887 if (!drflac__seek_bits(bs, bitsToSeek)) {
81888 return DRFLAC_FALSE;
81889 }
81890 if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {
81891 return DRFLAC_FALSE;
81892 }
81893 } break;
81894 default: return DRFLAC_FALSE;
81895 }
81896 return DRFLAC_TRUE;
81897}
81898static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment)
81899{
81900 drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2};
81901 DRFLAC_ASSERT(channelAssignment <= 10);
81902 return lookup[channelAssignment];
81903}
81904static drflac_result drflac__decode_flac_frame(drflac* pFlac)
81905{
81906 int channelCount;
81907 int i;
81908 drflac_uint8 paddingSizeInBits;
81909 drflac_uint16 desiredCRC16;
81910#ifndef DR_FLAC_NO_CRC
81911 drflac_uint16 actualCRC16;
81912#endif
81913 DRFLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes));
81915 return DRFLAC_ERROR;
81916 }
81917 channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
81918 if (channelCount != (int)pFlac->channels) {
81919 return DRFLAC_ERROR;
81920 }
81921 for (i = 0; i < channelCount; ++i) {
81922 if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) {
81923 return DRFLAC_ERROR;
81924 }
81925 }
81926 paddingSizeInBits = (drflac_uint8)(DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7);
81927 if (paddingSizeInBits > 0) {
81928 drflac_uint8 padding = 0;
81929 if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) {
81930 return DRFLAC_AT_END;
81931 }
81932 }
81933#ifndef DR_FLAC_NO_CRC
81934 actualCRC16 = drflac__flush_crc16(&pFlac->bs);
81935#endif
81936 if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
81937 return DRFLAC_AT_END;
81938 }
81939#ifndef DR_FLAC_NO_CRC
81940 if (actualCRC16 != desiredCRC16) {
81941 return DRFLAC_CRC_MISMATCH;
81942 }
81943#endif
81945 return DRFLAC_SUCCESS;
81946}
81947static drflac_result drflac__seek_flac_frame(drflac* pFlac)
81948{
81949 int channelCount;
81950 int i;
81951 drflac_uint16 desiredCRC16;
81952#ifndef DR_FLAC_NO_CRC
81953 drflac_uint16 actualCRC16;
81954#endif
81955 channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
81956 for (i = 0; i < channelCount; ++i) {
81957 if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) {
81958 return DRFLAC_ERROR;
81959 }
81960 }
81961 if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) {
81962 return DRFLAC_ERROR;
81963 }
81964#ifndef DR_FLAC_NO_CRC
81965 actualCRC16 = drflac__flush_crc16(&pFlac->bs);
81966#endif
81967 if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
81968 return DRFLAC_AT_END;
81969 }
81970#ifndef DR_FLAC_NO_CRC
81971 if (actualCRC16 != desiredCRC16) {
81972 return DRFLAC_CRC_MISMATCH;
81973 }
81974#endif
81975 return DRFLAC_SUCCESS;
81976}
81977static drflac_bool32 drflac__read_and_decode_next_flac_frame(drflac* pFlac)
81978{
81979 DRFLAC_ASSERT(pFlac != NULL);
81980 for (;;) {
81981 drflac_result result;
81982 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
81983 return DRFLAC_FALSE;
81984 }
81985 result = drflac__decode_flac_frame(pFlac);
81986 if (result != DRFLAC_SUCCESS) {
81987 if (result == DRFLAC_CRC_MISMATCH) {
81988 continue;
81989 } else {
81990 return DRFLAC_FALSE;
81991 }
81992 }
81993 return DRFLAC_TRUE;
81994 }
81995}
81996static void drflac__get_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drflac_uint64* pFirstPCMFrame, drflac_uint64* pLastPCMFrame)
81997{
81998 drflac_uint64 firstPCMFrame;
81999 drflac_uint64 lastPCMFrame;
82000 DRFLAC_ASSERT(pFlac != NULL);
82001 firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber;
82002 if (firstPCMFrame == 0) {
82004 }
82005 lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
82006 if (lastPCMFrame > 0) {
82007 lastPCMFrame -= 1;
82008 }
82009 if (pFirstPCMFrame) {
82010 *pFirstPCMFrame = firstPCMFrame;
82011 }
82012 if (pLastPCMFrame) {
82013 *pLastPCMFrame = lastPCMFrame;
82014 }
82015}
82016static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac)
82017{
82018 drflac_bool32 result;
82019 DRFLAC_ASSERT(pFlac != NULL);
82020 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes);
82021 DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));
82022 pFlac->currentPCMFrame = 0;
82023 return result;
82024}
82025static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac)
82026{
82027 DRFLAC_ASSERT(pFlac != NULL);
82028 return drflac__seek_flac_frame(pFlac);
82029}
82030static drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek)
82031{
82032 drflac_uint64 pcmFramesRead = 0;
82033 while (pcmFramesToSeek > 0) {
82034 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
82035 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
82036 break;
82037 }
82038 } else {
82039 if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) {
82040 pcmFramesRead += pcmFramesToSeek;
82041 pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)pcmFramesToSeek;
82042 pcmFramesToSeek = 0;
82043 } else {
82044 pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining;
82045 pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining;
82047 }
82048 }
82049 }
82050 pFlac->currentPCMFrame += pcmFramesRead;
82051 return pcmFramesRead;
82052}
82053static drflac_bool32 drflac__seek_to_pcm_frame__brute_force(drflac* pFlac, drflac_uint64 pcmFrameIndex)
82054{
82055 drflac_bool32 isMidFrame = DRFLAC_FALSE;
82056 drflac_uint64 runningPCMFrameCount;
82057 DRFLAC_ASSERT(pFlac != NULL);
82058 if (pcmFrameIndex >= pFlac->currentPCMFrame) {
82059 runningPCMFrameCount = pFlac->currentPCMFrame;
82060 if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
82061 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
82062 return DRFLAC_FALSE;
82063 }
82064 } else {
82065 isMidFrame = DRFLAC_TRUE;
82066 }
82067 } else {
82068 runningPCMFrameCount = 0;
82069 if (!drflac__seek_to_first_frame(pFlac)) {
82070 return DRFLAC_FALSE;
82071 }
82072 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
82073 return DRFLAC_FALSE;
82074 }
82075 }
82076 for (;;) {
82077 drflac_uint64 pcmFrameCountInThisFLACFrame;
82078 drflac_uint64 firstPCMFrameInFLACFrame = 0;
82079 drflac_uint64 lastPCMFrameInFLACFrame = 0;
82080 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
82081 pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
82082 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {
82083 drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;
82084 if (!isMidFrame) {
82085 drflac_result result = drflac__decode_flac_frame(pFlac);
82086 if (result == DRFLAC_SUCCESS) {
82087 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
82088 } else {
82089 if (result == DRFLAC_CRC_MISMATCH) {
82090 goto next_iteration;
82091 } else {
82092 return DRFLAC_FALSE;
82093 }
82094 }
82095 } else {
82096 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
82097 }
82098 } else {
82099 if (!isMidFrame) {
82100 drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
82101 if (result == DRFLAC_SUCCESS) {
82102 runningPCMFrameCount += pcmFrameCountInThisFLACFrame;
82103 } else {
82104 if (result == DRFLAC_CRC_MISMATCH) {
82105 goto next_iteration;
82106 } else {
82107 return DRFLAC_FALSE;
82108 }
82109 }
82110 } else {
82111 runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;
82113 isMidFrame = DRFLAC_FALSE;
82114 }
82115 if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {
82116 return DRFLAC_TRUE;
82117 }
82118 }
82119 next_iteration:
82120 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
82121 return DRFLAC_FALSE;
82122 }
82123 }
82124}
82125#if !defined(DR_FLAC_NO_CRC)
82126#define DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f
82127static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFlac, drflac_uint64 targetByte, drflac_uint64 rangeLo, drflac_uint64 rangeHi, drflac_uint64* pLastSuccessfulSeekOffset)
82128{
82129 DRFLAC_ASSERT(pFlac != NULL);
82130 DRFLAC_ASSERT(pLastSuccessfulSeekOffset != NULL);
82131 DRFLAC_ASSERT(targetByte >= rangeLo);
82132 DRFLAC_ASSERT(targetByte <= rangeHi);
82133 *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes;
82134 for (;;) {
82135 drflac_uint64 lastTargetByte = targetByte;
82136 if (!drflac__seek_to_byte(&pFlac->bs, targetByte)) {
82137 if (targetByte == 0) {
82138 drflac__seek_to_first_frame(pFlac);
82139 return DRFLAC_FALSE;
82140 }
82141 targetByte = rangeLo + ((rangeHi - rangeLo)/2);
82142 rangeHi = targetByte;
82143 } else {
82144 DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));
82145#if 1
82146 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
82147 targetByte = rangeLo + ((rangeHi - rangeLo)/2);
82148 rangeHi = targetByte;
82149 } else {
82150 break;
82151 }
82152#else
82153 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
82154 targetByte = rangeLo + ((rangeHi - rangeLo)/2);
82155 rangeHi = targetByte;
82156 } else {
82157 break;
82158 }
82159#endif
82160 }
82161 if(targetByte == lastTargetByte) {
82162 return DRFLAC_FALSE;
82163 }
82164 }
82165 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);
82166 DRFLAC_ASSERT(targetByte <= rangeHi);
82167 *pLastSuccessfulSeekOffset = targetByte;
82168 return DRFLAC_TRUE;
82169}
82170static drflac_bool32 drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 offset)
82171{
82172#if 0
82173 if (drflac__decode_flac_frame(pFlac) != DRFLAC_SUCCESS) {
82174 if (drflac__read_and_decode_next_flac_frame(pFlac) == DRFLAC_FALSE) {
82175 return DRFLAC_FALSE;
82176 }
82177 }
82178#endif
82179 return drflac__seek_forward_by_pcm_frames(pFlac, offset) == offset;
82180}
82181static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* pFlac, drflac_uint64 pcmFrameIndex, drflac_uint64 byteRangeLo, drflac_uint64 byteRangeHi)
82182{
82183 drflac_uint64 targetByte;
82184 drflac_uint64 pcmRangeLo = pFlac->totalPCMFrameCount;
82185 drflac_uint64 pcmRangeHi = 0;
82186 drflac_uint64 lastSuccessfulSeekOffset = (drflac_uint64)-1;
82187 drflac_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo;
82188 drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;
82189 targetByte = byteRangeLo + (drflac_uint64)(((drflac_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO);
82190 if (targetByte > byteRangeHi) {
82191 targetByte = byteRangeHi;
82192 }
82193 for (;;) {
82194 if (drflac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) {
82195 drflac_uint64 newPCMRangeLo;
82196 drflac_uint64 newPCMRangeHi;
82197 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi);
82198 if (pcmRangeLo == newPCMRangeLo) {
82199 if (!drflac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) {
82200 break;
82201 }
82202 if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {
82203 return DRFLAC_TRUE;
82204 } else {
82205 break;
82206 }
82207 }
82208 pcmRangeLo = newPCMRangeLo;
82209 pcmRangeHi = newPCMRangeHi;
82210 if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) {
82211 if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) {
82212 return DRFLAC_TRUE;
82213 } else {
82214 break;
82215 }
82216 } else {
82217 const float approxCompressionRatio = (drflac_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((drflac_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f);
82218 if (pcmRangeLo > pcmFrameIndex) {
82219 byteRangeHi = lastSuccessfulSeekOffset;
82220 if (byteRangeLo > byteRangeHi) {
82221 byteRangeLo = byteRangeHi;
82222 }
82223 targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2);
82224 if (targetByte < byteRangeLo) {
82225 targetByte = byteRangeLo;
82226 }
82227 } else {
82228 if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) {
82229 if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {
82230 return DRFLAC_TRUE;
82231 } else {
82232 break;
82233 }
82234 } else {
82235 byteRangeLo = lastSuccessfulSeekOffset;
82236 if (byteRangeHi < byteRangeLo) {
82237 byteRangeHi = byteRangeLo;
82238 }
82239 targetByte = lastSuccessfulSeekOffset + (drflac_uint64)(((drflac_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio);
82240 if (targetByte > byteRangeHi) {
82241 targetByte = byteRangeHi;
82242 }
82243 if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) {
82244 closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset;
82245 }
82246 }
82247 }
82248 }
82249 } else {
82250 break;
82251 }
82252 }
82253 drflac__seek_to_first_frame(pFlac);
82254 return DRFLAC_FALSE;
82255}
82256static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drflac_uint64 pcmFrameIndex)
82257{
82258 drflac_uint64 byteRangeLo;
82259 drflac_uint64 byteRangeHi;
82260 drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;
82261 if (drflac__seek_to_first_frame(pFlac) == DRFLAC_FALSE) {
82262 return DRFLAC_FALSE;
82263 }
82264 if (pcmFrameIndex < seekForwardThreshold) {
82265 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex;
82266 }
82267 byteRangeLo = pFlac->firstFLACFramePosInBytes;
82268 byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f);
82269 return drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi);
82270}
82271#endif
82272static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac_uint64 pcmFrameIndex)
82273{
82274 drflac_uint32 iClosestSeekpoint = 0;
82275 drflac_bool32 isMidFrame = DRFLAC_FALSE;
82276 drflac_uint64 runningPCMFrameCount;
82277 drflac_uint32 iSeekpoint;
82278 DRFLAC_ASSERT(pFlac != NULL);
82279 if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) {
82280 return DRFLAC_FALSE;
82281 }
82282 if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) {
82283 return DRFLAC_FALSE;
82284 }
82285 for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
82286 if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) {
82287 break;
82288 }
82289 iClosestSeekpoint = iSeekpoint;
82290 }
82291 if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) {
82292 return DRFLAC_FALSE;
82293 }
82294 if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) {
82295 return DRFLAC_FALSE;
82296 }
82297#if !defined(DR_FLAC_NO_CRC)
82298 if (pFlac->totalPCMFrameCount > 0) {
82299 drflac_uint64 byteRangeLo;
82300 drflac_uint64 byteRangeHi;
82301 byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f);
82302 byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset;
82303 if (iClosestSeekpoint < pFlac->seekpointCount-1) {
82304 drflac_uint32 iNextSeekpoint = iClosestSeekpoint + 1;
82305 if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) {
82306 return DRFLAC_FALSE;
82307 }
82308 if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((drflac_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) {
82309 byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1;
82310 }
82311 }
82312 if (drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {
82313 if (drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
82314 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);
82315 if (drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) {
82316 return DRFLAC_TRUE;
82317 }
82318 }
82319 }
82320 }
82321#endif
82322 if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) {
82323 runningPCMFrameCount = pFlac->currentPCMFrame;
82324 if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
82325 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
82326 return DRFLAC_FALSE;
82327 }
82328 } else {
82329 isMidFrame = DRFLAC_TRUE;
82330 }
82331 } else {
82332 runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame;
82333 if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {
82334 return DRFLAC_FALSE;
82335 }
82336 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
82337 return DRFLAC_FALSE;
82338 }
82339 }
82340 for (;;) {
82341 drflac_uint64 pcmFrameCountInThisFLACFrame;
82342 drflac_uint64 firstPCMFrameInFLACFrame = 0;
82343 drflac_uint64 lastPCMFrameInFLACFrame = 0;
82344 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
82345 pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
82346 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {
82347 drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;
82348 if (!isMidFrame) {
82349 drflac_result result = drflac__decode_flac_frame(pFlac);
82350 if (result == DRFLAC_SUCCESS) {
82351 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
82352 } else {
82353 if (result == DRFLAC_CRC_MISMATCH) {
82354 goto next_iteration;
82355 } else {
82356 return DRFLAC_FALSE;
82357 }
82358 }
82359 } else {
82360 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
82361 }
82362 } else {
82363 if (!isMidFrame) {
82364 drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
82365 if (result == DRFLAC_SUCCESS) {
82366 runningPCMFrameCount += pcmFrameCountInThisFLACFrame;
82367 } else {
82368 if (result == DRFLAC_CRC_MISMATCH) {
82369 goto next_iteration;
82370 } else {
82371 return DRFLAC_FALSE;
82372 }
82373 }
82374 } else {
82375 runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;
82377 isMidFrame = DRFLAC_FALSE;
82378 }
82379 if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {
82380 return DRFLAC_TRUE;
82381 }
82382 }
82383 next_iteration:
82384 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
82385 return DRFLAC_FALSE;
82386 }
82387 }
82388}
82389#ifndef DR_FLAC_NO_OGG
82390typedef struct
82391{
82392 drflac_uint8 capturePattern[4];
82393 drflac_uint8 structureVersion;
82394 drflac_uint8 headerType;
82395 drflac_uint64 granulePosition;
82396 drflac_uint32 serialNumber;
82397 drflac_uint32 sequenceNumber;
82398 drflac_uint32 checksum;
82399 drflac_uint8 segmentCount;
82400 drflac_uint8 segmentTable[255];
82401} drflac_ogg_page_header;
82402#endif
82403typedef struct
82404{
82405 drflac_read_proc onRead;
82406 drflac_seek_proc onSeek;
82407 drflac_meta_proc onMeta;
82408 drflac_container container;
82409 void* pUserData;
82410 void* pUserDataMD;
82411 drflac_uint32 sampleRate;
82412 drflac_uint8 channels;
82413 drflac_uint8 bitsPerSample;
82414 drflac_uint64 totalPCMFrameCount;
82415 drflac_uint16 maxBlockSizeInPCMFrames;
82416 drflac_uint64 runningFilePos;
82417 drflac_bool32 hasStreamInfoBlock;
82418 drflac_bool32 hasMetadataBlocks;
82419 drflac_bs bs;
82420 drflac_frame_header firstFrameHeader;
82421#ifndef DR_FLAC_NO_OGG
82422 drflac_uint32 oggSerial;
82423 drflac_uint64 oggFirstBytePos;
82424 drflac_ogg_page_header oggBosHeader;
82425#endif
82426} drflac_init_info;
82427static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize)
82428{
82429 blockHeader = drflac__be2host_32(blockHeader);
82430 *isLastBlock = (drflac_uint8)((blockHeader & 0x80000000UL) >> 31);
82431 *blockType = (drflac_uint8)((blockHeader & 0x7F000000UL) >> 24);
82432 *blockSize = (blockHeader & 0x00FFFFFFUL);
82433}
82434static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize)
82435{
82436 drflac_uint32 blockHeader;
82437 *blockSize = 0;
82438 if (onRead(pUserData, &blockHeader, 4) != 4) {
82439 return DRFLAC_FALSE;
82440 }
82441 drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize);
82442 return DRFLAC_TRUE;
82443}
82444static drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo)
82445{
82446 drflac_uint32 blockSizes;
82447 drflac_uint64 frameSizes = 0;
82448 drflac_uint64 importantProps;
82449 drflac_uint8 md5[16];
82450 if (onRead(pUserData, &blockSizes, 4) != 4) {
82451 return DRFLAC_FALSE;
82452 }
82453 if (onRead(pUserData, &frameSizes, 6) != 6) {
82454 return DRFLAC_FALSE;
82455 }
82456 if (onRead(pUserData, &importantProps, 8) != 8) {
82457 return DRFLAC_FALSE;
82458 }
82459 if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) {
82460 return DRFLAC_FALSE;
82461 }
82462 blockSizes = drflac__be2host_32(blockSizes);
82463 frameSizes = drflac__be2host_64(frameSizes);
82464 importantProps = drflac__be2host_64(importantProps);
82465 pStreamInfo->minBlockSizeInPCMFrames = (drflac_uint16)((blockSizes & 0xFFFF0000) >> 16);
82466 pStreamInfo->maxBlockSizeInPCMFrames = (drflac_uint16) (blockSizes & 0x0000FFFF);
82467 pStreamInfo->minFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 24)) >> 40);
82468 pStreamInfo->maxFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 0)) >> 16);
82469 pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (((drflac_uint64)0x000FFFFF << 16) << 28)) >> 44);
82470 pStreamInfo->channels = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000000E << 16) << 24)) >> 41) + 1;
82471 pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000001F << 16) << 20)) >> 36) + 1;
82472 pStreamInfo->totalPCMFrameCount = ((importantProps & ((((drflac_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF)));
82473 DRFLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5));
82474 return DRFLAC_TRUE;
82475}
82476static void* drflac__malloc_default(size_t sz, void* pUserData)
82477{
82478 (void)pUserData;
82479 return DRFLAC_MALLOC(sz);
82480}
82481static void* drflac__realloc_default(void* p, size_t sz, void* pUserData)
82482{
82483 (void)pUserData;
82484 return DRFLAC_REALLOC(p, sz);
82485}
82486static void drflac__free_default(void* p, void* pUserData)
82487{
82488 (void)pUserData;
82489 DRFLAC_FREE(p);
82490}
82491static void* drflac__malloc_from_callbacks(size_t sz, const drflac_allocation_callbacks* pAllocationCallbacks)
82492{
82493 if (pAllocationCallbacks == NULL) {
82494 return NULL;
82495 }
82496 if (pAllocationCallbacks->onMalloc != NULL) {
82497 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
82498 }
82499 if (pAllocationCallbacks->onRealloc != NULL) {
82500 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
82501 }
82502 return NULL;
82503}
82504static void* drflac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drflac_allocation_callbacks* pAllocationCallbacks)
82505{
82506 if (pAllocationCallbacks == NULL) {
82507 return NULL;
82508 }
82509 if (pAllocationCallbacks->onRealloc != NULL) {
82510 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
82511 }
82512 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
82513 void* p2;
82514 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
82515 if (p2 == NULL) {
82516 return NULL;
82517 }
82518 if (p != NULL) {
82519 DRFLAC_COPY_MEMORY(p2, p, szOld);
82520 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
82521 }
82522 return p2;
82523 }
82524 return NULL;
82525}
82526static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbacks* pAllocationCallbacks)
82527{
82528 if (p == NULL || pAllocationCallbacks == NULL) {
82529 return;
82530 }
82531 if (pAllocationCallbacks->onFree != NULL) {
82532 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
82533 }
82534}
82535static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeektableSize, drflac_allocation_callbacks* pAllocationCallbacks)
82536{
82537 drflac_uint64 runningFilePos = 42;
82538 drflac_uint64 seektablePos = 0;
82539 drflac_uint32 seektableSize = 0;
82540 for (;;) {
82541 drflac_metadata metadata;
82542 drflac_uint8 isLastBlock = 0;
82543 drflac_uint8 blockType;
82544 drflac_uint32 blockSize;
82545 if (drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == DRFLAC_FALSE) {
82546 return DRFLAC_FALSE;
82547 }
82548 runningFilePos += 4;
82549 metadata.type = blockType;
82550 metadata.pRawData = NULL;
82551 metadata.rawDataSize = 0;
82552 switch (blockType)
82553 {
82555 {
82556 if (blockSize < 4) {
82557 return DRFLAC_FALSE;
82558 }
82559 if (onMeta) {
82560 void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
82561 if (pRawData == NULL) {
82562 return DRFLAC_FALSE;
82563 }
82564 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
82565 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82566 return DRFLAC_FALSE;
82567 }
82568 metadata.pRawData = pRawData;
82569 metadata.rawDataSize = blockSize;
82570 metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData);
82571 metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32));
82572 metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32);
82573 onMeta(pUserDataMD, &metadata);
82574 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82575 }
82576 } break;
82578 {
82579 seektablePos = runningFilePos;
82580 seektableSize = blockSize;
82581 if (onMeta) {
82582 drflac_uint32 iSeekpoint;
82583 void* pRawData;
82584 pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
82585 if (pRawData == NULL) {
82586 return DRFLAC_FALSE;
82587 }
82588 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
82589 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82590 return DRFLAC_FALSE;
82591 }
82592 metadata.pRawData = pRawData;
82593 metadata.rawDataSize = blockSize;
82594 metadata.data.seektable.seekpointCount = blockSize/sizeof(drflac_seekpoint);
82595 metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData;
82596 for (iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) {
82597 drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint;
82598 pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame);
82599 pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset);
82600 pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount);
82601 }
82602 onMeta(pUserDataMD, &metadata);
82603 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82604 }
82605 } break;
82607 {
82608 if (blockSize < 8) {
82609 return DRFLAC_FALSE;
82610 }
82611 if (onMeta) {
82612 void* pRawData;
82613 const char* pRunningData;
82614 const char* pRunningDataEnd;
82615 drflac_uint32 i;
82616 pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
82617 if (pRawData == NULL) {
82618 return DRFLAC_FALSE;
82619 }
82620 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
82621 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82622 return DRFLAC_FALSE;
82623 }
82624 metadata.pRawData = pRawData;
82625 metadata.rawDataSize = blockSize;
82626 pRunningData = (const char*)pRawData;
82627 pRunningDataEnd = (const char*)pRawData + blockSize;
82628 metadata.data.vorbis_comment.vendorLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
82629 if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) {
82630 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82631 return DRFLAC_FALSE;
82632 }
82633 metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength;
82634 metadata.data.vorbis_comment.commentCount = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
82635 if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) {
82636 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82637 return DRFLAC_FALSE;
82638 }
82639 metadata.data.vorbis_comment.pComments = pRunningData;
82640 for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) {
82641 drflac_uint32 commentLength;
82642 if (pRunningDataEnd - pRunningData < 4) {
82643 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82644 return DRFLAC_FALSE;
82645 }
82646 commentLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
82647 if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) {
82648 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82649 return DRFLAC_FALSE;
82650 }
82651 pRunningData += commentLength;
82652 }
82653 onMeta(pUserDataMD, &metadata);
82654 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82655 }
82656 } break;
82658 {
82659 if (blockSize < 396) {
82660 return DRFLAC_FALSE;
82661 }
82662 if (onMeta) {
82663 void* pRawData;
82664 const char* pRunningData;
82665 const char* pRunningDataEnd;
82666 drflac_uint8 iTrack;
82667 drflac_uint8 iIndex;
82668 pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
82669 if (pRawData == NULL) {
82670 return DRFLAC_FALSE;
82671 }
82672 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
82673 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82674 return DRFLAC_FALSE;
82675 }
82676 metadata.pRawData = pRawData;
82677 metadata.rawDataSize = blockSize;
82678 pRunningData = (const char*)pRawData;
82679 pRunningDataEnd = (const char*)pRawData + blockSize;
82680 DRFLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128;
82681 metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8;
82682 metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259;
82683 metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1;
82684 metadata.data.cuesheet.pTrackData = pRunningData;
82685 for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) {
82686 drflac_uint8 indexCount;
82687 drflac_uint32 indexPointSize;
82688 if (pRunningDataEnd - pRunningData < 36) {
82689 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82690 return DRFLAC_FALSE;
82691 }
82692 pRunningData += 35;
82693 indexCount = pRunningData[0]; pRunningData += 1;
82694 indexPointSize = indexCount * sizeof(drflac_cuesheet_track_index);
82695 if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) {
82696 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82697 return DRFLAC_FALSE;
82698 }
82699 for (iIndex = 0; iIndex < indexCount; ++iIndex) {
82701 pRunningData += sizeof(drflac_cuesheet_track_index);
82702 pTrack->offset = drflac__be2host_64(pTrack->offset);
82703 }
82704 }
82705 onMeta(pUserDataMD, &metadata);
82706 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82707 }
82708 } break;
82710 {
82711 if (blockSize < 32) {
82712 return DRFLAC_FALSE;
82713 }
82714 if (onMeta) {
82715 void* pRawData;
82716 const char* pRunningData;
82717 const char* pRunningDataEnd;
82718 pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
82719 if (pRawData == NULL) {
82720 return DRFLAC_FALSE;
82721 }
82722 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
82723 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82724 return DRFLAC_FALSE;
82725 }
82726 metadata.pRawData = pRawData;
82727 metadata.rawDataSize = blockSize;
82728 pRunningData = (const char*)pRawData;
82729 pRunningDataEnd = (const char*)pRawData + blockSize;
82730 metadata.data.picture.type = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
82731 metadata.data.picture.mimeLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
82732 if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) {
82733 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82734 return DRFLAC_FALSE;
82735 }
82736 metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength;
82737 metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
82738 if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) {
82739 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82740 return DRFLAC_FALSE;
82741 }
82742 metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength;
82743 metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
82744 metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
82745 metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
82746 metadata.data.picture.indexColorCount = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
82747 metadata.data.picture.pictureDataSize = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
82748 metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData;
82749 if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) {
82750 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82751 return DRFLAC_FALSE;
82752 }
82753 onMeta(pUserDataMD, &metadata);
82754 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82755 }
82756 } break;
82758 {
82759 if (onMeta) {
82760 metadata.data.padding.unused = 0;
82761 if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
82762 isLastBlock = DRFLAC_TRUE;
82763 } else {
82764 onMeta(pUserDataMD, &metadata);
82765 }
82766 }
82767 } break;
82769 {
82770 if (onMeta) {
82771 if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
82772 isLastBlock = DRFLAC_TRUE;
82773 }
82774 }
82775 } break;
82776 default:
82777 {
82778 if (onMeta) {
82779 void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
82780 if (pRawData == NULL) {
82781 return DRFLAC_FALSE;
82782 }
82783 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
82784 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82785 return DRFLAC_FALSE;
82786 }
82787 metadata.pRawData = pRawData;
82788 metadata.rawDataSize = blockSize;
82789 onMeta(pUserDataMD, &metadata);
82790 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
82791 }
82792 } break;
82793 }
82794 if (onMeta == NULL && blockSize > 0) {
82795 if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
82796 isLastBlock = DRFLAC_TRUE;
82797 }
82798 }
82799 runningFilePos += blockSize;
82800 if (isLastBlock) {
82801 break;
82802 }
82803 }
82804 *pSeektablePos = seektablePos;
82805 *pSeektableSize = seektableSize;
82806 *pFirstFramePos = runningFilePos;
82807 return DRFLAC_TRUE;
82808}
82809static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed)
82810{
82811 drflac_uint8 isLastBlock;
82812 drflac_uint8 blockType;
82813 drflac_uint32 blockSize;
82814 (void)onSeek;
82815 pInit->container = drflac_container_native;
82816 if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
82817 return DRFLAC_FALSE;
82818 }
82819 if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
82820 if (!relaxed) {
82821 return DRFLAC_FALSE;
82822 } else {
82823 pInit->hasStreamInfoBlock = DRFLAC_FALSE;
82824 pInit->hasMetadataBlocks = DRFLAC_FALSE;
82825 if (!drflac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) {
82826 return DRFLAC_FALSE;
82827 }
82828 if (pInit->firstFrameHeader.bitsPerSample == 0) {
82829 return DRFLAC_FALSE;
82830 }
82831 pInit->sampleRate = pInit->firstFrameHeader.sampleRate;
82832 pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment);
82833 pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample;
82834 pInit->maxBlockSizeInPCMFrames = 65535;
82835 return DRFLAC_TRUE;
82836 }
82837 } else {
82838 drflac_streaminfo streaminfo;
82839 if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) {
82840 return DRFLAC_FALSE;
82841 }
82842 pInit->hasStreamInfoBlock = DRFLAC_TRUE;
82843 pInit->sampleRate = streaminfo.sampleRate;
82844 pInit->channels = streaminfo.channels;
82845 pInit->bitsPerSample = streaminfo.bitsPerSample;
82846 pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount;
82847 pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;
82848 pInit->hasMetadataBlocks = !isLastBlock;
82849 if (onMeta) {
82850 drflac_metadata metadata;
82852 metadata.pRawData = NULL;
82853 metadata.rawDataSize = 0;
82854 metadata.data.streaminfo = streaminfo;
82855 onMeta(pUserDataMD, &metadata);
82856 }
82857 return DRFLAC_TRUE;
82858 }
82859}
82860#ifndef DR_FLAC_NO_OGG
82861#define DRFLAC_OGG_MAX_PAGE_SIZE 65307
82862#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199
82863typedef enum
82864{
82865 drflac_ogg_recover_on_crc_mismatch,
82866 drflac_ogg_fail_on_crc_mismatch
82867} drflac_ogg_crc_mismatch_recovery;
82868#ifndef DR_FLAC_NO_CRC
82869static drflac_uint32 drflac__crc32_table[] = {
82870 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L,
82871 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L,
82872 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L,
82873 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL,
82874 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L,
82875 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L,
82876 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L,
82877 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL,
82878 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L,
82879 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L,
82880 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L,
82881 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL,
82882 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L,
82883 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L,
82884 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L,
82885 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL,
82886 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL,
82887 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L,
82888 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L,
82889 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL,
82890 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL,
82891 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L,
82892 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L,
82893 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL,
82894 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL,
82895 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L,
82896 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L,
82897 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL,
82898 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL,
82899 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L,
82900 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L,
82901 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL,
82902 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L,
82903 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL,
82904 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL,
82905 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L,
82906 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L,
82907 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL,
82908 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL,
82909 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L,
82910 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L,
82911 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL,
82912 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL,
82913 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L,
82914 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L,
82915 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL,
82916 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL,
82917 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L,
82918 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L,
82919 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL,
82920 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L,
82921 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L,
82922 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L,
82923 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL,
82924 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L,
82925 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L,
82926 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L,
82927 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL,
82928 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L,
82929 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L,
82930 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L,
82931 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL,
82932 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L,
82933 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L
82934};
82935#endif
82936static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data)
82937{
82938#ifndef DR_FLAC_NO_CRC
82939 return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data];
82940#else
82941 (void)data;
82942 return crc32;
82943#endif
82944}
82945#if 0
82946static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data)
82947{
82948 crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF));
82949 crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF));
82950 crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF));
82951 crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF));
82952 return crc32;
82953}
82954static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data)
82955{
82956 crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF));
82957 crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF));
82958 return crc32;
82959}
82960#endif
82961static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize)
82962{
82963 drflac_uint32 i;
82964 for (i = 0; i < dataSize; ++i) {
82965 crc32 = drflac_crc32_byte(crc32, pData[i]);
82966 }
82967 return crc32;
82968}
82969static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4])
82970{
82971 return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S';
82972}
82973static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader)
82974{
82975 return 27 + pHeader->segmentCount;
82976}
82977static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader)
82978{
82979 drflac_uint32 pageBodySize = 0;
82980 int i;
82981 for (i = 0; i < pHeader->segmentCount; ++i) {
82982 pageBodySize += pHeader->segmentTable[i];
82983 }
82984 return pageBodySize;
82985}
82986static drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32)
82987{
82988 drflac_uint8 data[23];
82989 drflac_uint32 i;
82990 DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32);
82991 if (onRead(pUserData, data, 23) != 23) {
82992 return DRFLAC_AT_END;
82993 }
82994 *pBytesRead += 23;
82995 pHeader->capturePattern[0] = 'O';
82996 pHeader->capturePattern[1] = 'g';
82997 pHeader->capturePattern[2] = 'g';
82998 pHeader->capturePattern[3] = 'S';
82999 pHeader->structureVersion = data[0];
83000 pHeader->headerType = data[1];
83001 DRFLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8);
83002 DRFLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4);
83003 DRFLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4);
83004 DRFLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4);
83005 pHeader->segmentCount = data[22];
83006 data[18] = 0;
83007 data[19] = 0;
83008 data[20] = 0;
83009 data[21] = 0;
83010 for (i = 0; i < 23; ++i) {
83011 *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]);
83012 }
83013 if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) {
83014 return DRFLAC_AT_END;
83015 }
83016 *pBytesRead += pHeader->segmentCount;
83017 for (i = 0; i < pHeader->segmentCount; ++i) {
83018 *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]);
83019 }
83020 return DRFLAC_SUCCESS;
83021}
83022static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32)
83023{
83024 drflac_uint8 id[4];
83025 *pBytesRead = 0;
83026 if (onRead(pUserData, id, 4) != 4) {
83027 return DRFLAC_AT_END;
83028 }
83029 *pBytesRead += 4;
83030 for (;;) {
83031 if (drflac_ogg__is_capture_pattern(id)) {
83032 drflac_result result;
83033 *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
83034 result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32);
83035 if (result == DRFLAC_SUCCESS) {
83036 return DRFLAC_SUCCESS;
83037 } else {
83038 if (result == DRFLAC_CRC_MISMATCH) {
83039 continue;
83040 } else {
83041 return result;
83042 }
83043 }
83044 } else {
83045 id[0] = id[1];
83046 id[1] = id[2];
83047 id[2] = id[3];
83048 if (onRead(pUserData, &id[3], 1) != 1) {
83049 return DRFLAC_AT_END;
83050 }
83051 *pBytesRead += 1;
83052 }
83053 }
83054}
83055typedef struct
83056{
83057 drflac_read_proc onRead;
83058 drflac_seek_proc onSeek;
83059 void* pUserData;
83060 drflac_uint64 currentBytePos;
83061 drflac_uint64 firstBytePos;
83062 drflac_uint32 serialNumber;
83063 drflac_ogg_page_header bosPageHeader;
83064 drflac_ogg_page_header currentPageHeader;
83065 drflac_uint32 bytesRemainingInPage;
83066 drflac_uint32 pageDataSize;
83067 drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE];
83068} drflac_oggbs;
83069static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead)
83070{
83071 size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead);
83072 oggbs->currentBytePos += bytesActuallyRead;
83073 return bytesActuallyRead;
83074}
83075static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin)
83076{
83077 if (origin == drflac_seek_origin_start) {
83078 if (offset <= 0x7FFFFFFF) {
83079 if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) {
83080 return DRFLAC_FALSE;
83081 }
83082 oggbs->currentBytePos = offset;
83083 return DRFLAC_TRUE;
83084 } else {
83085 if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
83086 return DRFLAC_FALSE;
83087 }
83088 oggbs->currentBytePos = offset;
83089 return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current);
83090 }
83091 } else {
83092 while (offset > 0x7FFFFFFF) {
83093 if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
83094 return DRFLAC_FALSE;
83095 }
83096 oggbs->currentBytePos += 0x7FFFFFFF;
83097 offset -= 0x7FFFFFFF;
83098 }
83099 if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) {
83100 return DRFLAC_FALSE;
83101 }
83102 oggbs->currentBytePos += offset;
83103 return DRFLAC_TRUE;
83104 }
83105}
83106static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod)
83107{
83108 drflac_ogg_page_header header;
83109 for (;;) {
83110 drflac_uint32 crc32 = 0;
83111 drflac_uint32 bytesRead;
83112 drflac_uint32 pageBodySize;
83113#ifndef DR_FLAC_NO_CRC
83114 drflac_uint32 actualCRC32;
83115#endif
83116 if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
83117 return DRFLAC_FALSE;
83118 }
83119 oggbs->currentBytePos += bytesRead;
83120 pageBodySize = drflac_ogg__get_page_body_size(&header);
83121 if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) {
83122 continue;
83123 }
83124 if (header.serialNumber != oggbs->serialNumber) {
83125 if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) {
83126 return DRFLAC_FALSE;
83127 }
83128 continue;
83129 }
83130 if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) {
83131 return DRFLAC_FALSE;
83132 }
83133 oggbs->pageDataSize = pageBodySize;
83134#ifndef DR_FLAC_NO_CRC
83135 actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize);
83136 if (actualCRC32 != header.checksum) {
83137 if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) {
83138 continue;
83139 } else {
83140 drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch);
83141 return DRFLAC_FALSE;
83142 }
83143 }
83144#else
83145 (void)recoveryMethod;
83146#endif
83147 oggbs->currentPageHeader = header;
83148 oggbs->bytesRemainingInPage = pageBodySize;
83149 return DRFLAC_TRUE;
83150 }
83151}
83152#if 0
83153static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg)
83154{
83155 drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage;
83156 drflac_uint8 iSeg = 0;
83157 drflac_uint32 iByte = 0;
83158 while (iByte < bytesConsumedInPage) {
83159 drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
83160 if (iByte + segmentSize > bytesConsumedInPage) {
83161 break;
83162 } else {
83163 iSeg += 1;
83164 iByte += segmentSize;
83165 }
83166 }
83167 *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte);
83168 return iSeg;
83169}
83170static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs)
83171{
83172 for (;;) {
83173 drflac_bool32 atEndOfPage = DRFLAC_FALSE;
83174 drflac_uint8 bytesRemainingInSeg;
83175 drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg);
83176 drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg;
83177 for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) {
83178 drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
83179 if (segmentSize < 255) {
83180 if (iSeg == oggbs->currentPageHeader.segmentCount-1) {
83181 atEndOfPage = DRFLAC_TRUE;
83182 }
83183 break;
83184 }
83185 bytesToEndOfPacketOrPage += segmentSize;
83186 }
83187 drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current);
83188 oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage;
83189 if (atEndOfPage) {
83190 if (!drflac_oggbs__goto_next_page(oggbs)) {
83191 return DRFLAC_FALSE;
83192 }
83193 if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
83194 return DRFLAC_TRUE;
83195 }
83196 } else {
83197 return DRFLAC_TRUE;
83198 }
83199 }
83200}
83201static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs)
83202{
83203 return drflac_oggbs__seek_to_next_packet(oggbs);
83204}
83205#endif
83206static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead)
83207{
83208 drflac_oggbs* oggbs = (drflac_oggbs*)pUserData;
83209 drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut;
83210 size_t bytesRead = 0;
83211 DRFLAC_ASSERT(oggbs != NULL);
83212 DRFLAC_ASSERT(pRunningBufferOut != NULL);
83213 while (bytesRead < bytesToRead) {
83214 size_t bytesRemainingToRead = bytesToRead - bytesRead;
83215 if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) {
83216 DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead);
83217 bytesRead += bytesRemainingToRead;
83218 oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead;
83219 break;
83220 }
83221 if (oggbs->bytesRemainingInPage > 0) {
83222 DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage);
83223 bytesRead += oggbs->bytesRemainingInPage;
83224 pRunningBufferOut += oggbs->bytesRemainingInPage;
83225 oggbs->bytesRemainingInPage = 0;
83226 }
83227 DRFLAC_ASSERT(bytesRemainingToRead > 0);
83228 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
83229 break;
83230 }
83231 }
83232 return bytesRead;
83233}
83234static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin)
83235{
83236 drflac_oggbs* oggbs = (drflac_oggbs*)pUserData;
83237 int bytesSeeked = 0;
83238 DRFLAC_ASSERT(oggbs != NULL);
83239 DRFLAC_ASSERT(offset >= 0);
83240 if (origin == drflac_seek_origin_start) {
83241 if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) {
83242 return DRFLAC_FALSE;
83243 }
83244 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
83245 return DRFLAC_FALSE;
83246 }
83247 return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current);
83248 }
83249 DRFLAC_ASSERT(origin == drflac_seek_origin_current);
83250 while (bytesSeeked < offset) {
83251 int bytesRemainingToSeek = offset - bytesSeeked;
83252 DRFLAC_ASSERT(bytesRemainingToSeek >= 0);
83253 if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) {
83254 bytesSeeked += bytesRemainingToSeek;
83255 (void)bytesSeeked;
83256 oggbs->bytesRemainingInPage -= bytesRemainingToSeek;
83257 break;
83258 }
83259 if (oggbs->bytesRemainingInPage > 0) {
83260 bytesSeeked += (int)oggbs->bytesRemainingInPage;
83261 oggbs->bytesRemainingInPage = 0;
83262 }
83263 DRFLAC_ASSERT(bytesRemainingToSeek > 0);
83264 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
83265 return DRFLAC_FALSE;
83266 }
83267 }
83268 return DRFLAC_TRUE;
83269}
83270static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex)
83271{
83272 drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
83273 drflac_uint64 originalBytePos;
83274 drflac_uint64 runningGranulePosition;
83275 drflac_uint64 runningFrameBytePos;
83276 drflac_uint64 runningPCMFrameCount;
83277 DRFLAC_ASSERT(oggbs != NULL);
83278 originalBytePos = oggbs->currentBytePos;
83279 if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) {
83280 return DRFLAC_FALSE;
83281 }
83282 oggbs->bytesRemainingInPage = 0;
83283 runningGranulePosition = 0;
83284 for (;;) {
83285 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
83286 drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start);
83287 return DRFLAC_FALSE;
83288 }
83289 runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize;
83290 if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) {
83291 break;
83292 }
83293 if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
83294 if (oggbs->currentPageHeader.segmentTable[0] >= 2) {
83295 drflac_uint8 firstBytesInPage[2];
83296 firstBytesInPage[0] = oggbs->pageData[0];
83297 firstBytesInPage[1] = oggbs->pageData[1];
83298 if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) {
83299 runningGranulePosition = oggbs->currentPageHeader.granulePosition;
83300 }
83301 continue;
83302 }
83303 }
83304 }
83305 if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) {
83306 return DRFLAC_FALSE;
83307 }
83308 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
83309 return DRFLAC_FALSE;
83310 }
83311 runningPCMFrameCount = runningGranulePosition;
83312 for (;;) {
83313 drflac_uint64 firstPCMFrameInFLACFrame = 0;
83314 drflac_uint64 lastPCMFrameInFLACFrame = 0;
83315 drflac_uint64 pcmFrameCountInThisFrame;
83316 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
83317 return DRFLAC_FALSE;
83318 }
83319 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
83320 pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
83321 if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) {
83322 drflac_result result = drflac__decode_flac_frame(pFlac);
83323 if (result == DRFLAC_SUCCESS) {
83324 pFlac->currentPCMFrame = pcmFrameIndex;
83326 return DRFLAC_TRUE;
83327 } else {
83328 return DRFLAC_FALSE;
83329 }
83330 }
83331 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) {
83332 drflac_result result = drflac__decode_flac_frame(pFlac);
83333 if (result == DRFLAC_SUCCESS) {
83334 drflac_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount);
83335 if (pcmFramesToDecode == 0) {
83336 return DRFLAC_TRUE;
83337 }
83338 pFlac->currentPCMFrame = runningPCMFrameCount;
83339 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
83340 } else {
83341 if (result == DRFLAC_CRC_MISMATCH) {
83342 continue;
83343 } else {
83344 return DRFLAC_FALSE;
83345 }
83346 }
83347 } else {
83348 drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
83349 if (result == DRFLAC_SUCCESS) {
83350 runningPCMFrameCount += pcmFrameCountInThisFrame;
83351 } else {
83352 if (result == DRFLAC_CRC_MISMATCH) {
83353 continue;
83354 } else {
83355 return DRFLAC_FALSE;
83356 }
83357 }
83358 }
83359 }
83360}
83361static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed)
83362{
83363 drflac_ogg_page_header header;
83364 drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
83365 drflac_uint32 bytesRead = 0;
83366 (void)relaxed;
83367 pInit->container = drflac_container_ogg;
83368 pInit->oggFirstBytePos = 0;
83369 if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
83370 return DRFLAC_FALSE;
83371 }
83372 pInit->runningFilePos += bytesRead;
83373 for (;;) {
83374 int pageBodySize;
83375 if ((header.headerType & 0x02) == 0) {
83376 return DRFLAC_FALSE;
83377 }
83378 pageBodySize = drflac_ogg__get_page_body_size(&header);
83379 if (pageBodySize == 51) {
83380 drflac_uint32 bytesRemainingInPage = pageBodySize;
83381 drflac_uint8 packetType;
83382 if (onRead(pUserData, &packetType, 1) != 1) {
83383 return DRFLAC_FALSE;
83384 }
83385 bytesRemainingInPage -= 1;
83386 if (packetType == 0x7F) {
83387 drflac_uint8 sig[4];
83388 if (onRead(pUserData, sig, 4) != 4) {
83389 return DRFLAC_FALSE;
83390 }
83391 bytesRemainingInPage -= 4;
83392 if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') {
83393 drflac_uint8 mappingVersion[2];
83394 if (onRead(pUserData, mappingVersion, 2) != 2) {
83395 return DRFLAC_FALSE;
83396 }
83397 if (mappingVersion[0] != 1) {
83398 return DRFLAC_FALSE;
83399 }
83400 if (!onSeek(pUserData, 2, drflac_seek_origin_current)) {
83401 return DRFLAC_FALSE;
83402 }
83403 if (onRead(pUserData, sig, 4) != 4) {
83404 return DRFLAC_FALSE;
83405 }
83406 if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') {
83407 drflac_streaminfo streaminfo;
83408 drflac_uint8 isLastBlock;
83409 drflac_uint8 blockType;
83410 drflac_uint32 blockSize;
83411 if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
83412 return DRFLAC_FALSE;
83413 }
83414 if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
83415 return DRFLAC_FALSE;
83416 }
83417 if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) {
83418 pInit->hasStreamInfoBlock = DRFLAC_TRUE;
83419 pInit->sampleRate = streaminfo.sampleRate;
83420 pInit->channels = streaminfo.channels;
83421 pInit->bitsPerSample = streaminfo.bitsPerSample;
83422 pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount;
83423 pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;
83424 pInit->hasMetadataBlocks = !isLastBlock;
83425 if (onMeta) {
83426 drflac_metadata metadata;
83428 metadata.pRawData = NULL;
83429 metadata.rawDataSize = 0;
83430 metadata.data.streaminfo = streaminfo;
83431 onMeta(pUserDataMD, &metadata);
83432 }
83433 pInit->runningFilePos += pageBodySize;
83434 pInit->oggFirstBytePos = pInit->runningFilePos - 79;
83435 pInit->oggSerial = header.serialNumber;
83436 pInit->oggBosHeader = header;
83437 break;
83438 } else {
83439 return DRFLAC_FALSE;
83440 }
83441 } else {
83442 return DRFLAC_FALSE;
83443 }
83444 } else {
83445 if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
83446 return DRFLAC_FALSE;
83447 }
83448 }
83449 } else {
83450 if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
83451 return DRFLAC_FALSE;
83452 }
83453 }
83454 } else {
83455 if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) {
83456 return DRFLAC_FALSE;
83457 }
83458 }
83459 pInit->runningFilePos += pageBodySize;
83460 if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
83461 return DRFLAC_FALSE;
83462 }
83463 pInit->runningFilePos += bytesRead;
83464 }
83465 pInit->hasMetadataBlocks = DRFLAC_TRUE;
83466 return DRFLAC_TRUE;
83467}
83468#endif
83469static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD)
83470{
83471 drflac_bool32 relaxed;
83472 drflac_uint8 id[4];
83473 if (pInit == NULL || onRead == NULL || onSeek == NULL) {
83474 return DRFLAC_FALSE;
83475 }
83476 DRFLAC_ZERO_MEMORY(pInit, sizeof(*pInit));
83477 pInit->onRead = onRead;
83478 pInit->onSeek = onSeek;
83479 pInit->onMeta = onMeta;
83480 pInit->container = container;
83481 pInit->pUserData = pUserData;
83482 pInit->pUserDataMD = pUserDataMD;
83483 pInit->bs.onRead = onRead;
83484 pInit->bs.onSeek = onSeek;
83485 pInit->bs.pUserData = pUserData;
83486 drflac__reset_cache(&pInit->bs);
83487 relaxed = container != drflac_container_unknown;
83488 for (;;) {
83489 if (onRead(pUserData, id, 4) != 4) {
83490 return DRFLAC_FALSE;
83491 }
83492 pInit->runningFilePos += 4;
83493 if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') {
83494 drflac_uint8 header[6];
83495 drflac_uint8 flags;
83496 drflac_uint32 headerSize;
83497 if (onRead(pUserData, header, 6) != 6) {
83498 return DRFLAC_FALSE;
83499 }
83500 pInit->runningFilePos += 6;
83501 flags = header[1];
83502 DRFLAC_COPY_MEMORY(&headerSize, header+2, 4);
83503 headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize));
83504 if (flags & 0x10) {
83505 headerSize += 10;
83506 }
83507 if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) {
83508 return DRFLAC_FALSE;
83509 }
83510 pInit->runningFilePos += headerSize;
83511 } else {
83512 break;
83513 }
83514 }
83515 if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') {
83516 return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
83517 }
83518#ifndef DR_FLAC_NO_OGG
83519 if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') {
83520 return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
83521 }
83522#endif
83523 if (relaxed) {
83524 if (container == drflac_container_native) {
83525 return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
83526 }
83527#ifndef DR_FLAC_NO_OGG
83528 if (container == drflac_container_ogg) {
83529 return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
83530 }
83531#endif
83532 }
83533 return DRFLAC_FALSE;
83534}
83535static void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit)
83536{
83537 DRFLAC_ASSERT(pFlac != NULL);
83538 DRFLAC_ASSERT(pInit != NULL);
83539 DRFLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac));
83540 pFlac->bs = pInit->bs;
83541 pFlac->onMeta = pInit->onMeta;
83542 pFlac->pUserDataMD = pInit->pUserDataMD;
83543 pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames;
83544 pFlac->sampleRate = pInit->sampleRate;
83545 pFlac->channels = (drflac_uint8)pInit->channels;
83546 pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample;
83547 pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount;
83548 pFlac->container = pInit->container;
83549}
83550static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks)
83551{
83552 drflac_init_info init;
83553 drflac_uint32 allocationSize;
83554 drflac_uint32 wholeSIMDVectorCountPerChannel;
83555 drflac_uint32 decodedSamplesAllocationSize;
83556#ifndef DR_FLAC_NO_OGG
83557 drflac_oggbs oggbs;
83558#endif
83559 drflac_uint64 firstFramePos;
83560 drflac_uint64 seektablePos;
83561 drflac_uint32 seektableSize;
83562 drflac_allocation_callbacks allocationCallbacks;
83563 drflac* pFlac;
83564 drflac__init_cpu_caps();
83565 if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) {
83566 return NULL;
83567 }
83568 if (pAllocationCallbacks != NULL) {
83569 allocationCallbacks = *pAllocationCallbacks;
83570 if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) {
83571 return NULL;
83572 }
83573 } else {
83574 allocationCallbacks.pUserData = NULL;
83575 allocationCallbacks.onMalloc = drflac__malloc_default;
83576 allocationCallbacks.onRealloc = drflac__realloc_default;
83577 allocationCallbacks.onFree = drflac__free_default;
83578 }
83579 allocationSize = sizeof(drflac);
83580 if ((init.maxBlockSizeInPCMFrames % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) {
83581 wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32)));
83582 } else {
83583 wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1;
83584 }
83585 decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels;
83586 allocationSize += decodedSamplesAllocationSize;
83587 allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE;
83588#ifndef DR_FLAC_NO_OGG
83589 if (init.container == drflac_container_ogg) {
83590 allocationSize += sizeof(drflac_oggbs);
83591 }
83592 DRFLAC_ZERO_MEMORY(&oggbs, sizeof(oggbs));
83593 if (init.container == drflac_container_ogg) {
83594 oggbs.onRead = onRead;
83595 oggbs.onSeek = onSeek;
83596 oggbs.pUserData = pUserData;
83597 oggbs.currentBytePos = init.oggFirstBytePos;
83598 oggbs.firstBytePos = init.oggFirstBytePos;
83599 oggbs.serialNumber = init.oggSerial;
83600 oggbs.bosPageHeader = init.oggBosHeader;
83601 oggbs.bytesRemainingInPage = 0;
83602 }
83603#endif
83604 firstFramePos = 42;
83605 seektablePos = 0;
83606 seektableSize = 0;
83607 if (init.hasMetadataBlocks) {
83608 drflac_read_proc onReadOverride = onRead;
83609 drflac_seek_proc onSeekOverride = onSeek;
83610 void* pUserDataOverride = pUserData;
83611#ifndef DR_FLAC_NO_OGG
83612 if (init.container == drflac_container_ogg) {
83613 onReadOverride = drflac__on_read_ogg;
83614 onSeekOverride = drflac__on_seek_ogg;
83615 pUserDataOverride = (void*)&oggbs;
83616 }
83617#endif
83618 if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seektableSize, &allocationCallbacks)) {
83619 return NULL;
83620 }
83621 allocationSize += seektableSize;
83622 }
83623 pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks);
83624 if (pFlac == NULL) {
83625 return NULL;
83626 }
83627 drflac__init_from_info(pFlac, &init);
83628 pFlac->allocationCallbacks = allocationCallbacks;
83629 pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE);
83630#ifndef DR_FLAC_NO_OGG
83631 if (init.container == drflac_container_ogg) {
83632 drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + seektableSize);
83633 *pInternalOggbs = oggbs;
83634 pFlac->bs.onRead = drflac__on_read_ogg;
83635 pFlac->bs.onSeek = drflac__on_seek_ogg;
83636 pFlac->bs.pUserData = (void*)pInternalOggbs;
83637 pFlac->_oggbs = (void*)pInternalOggbs;
83638 }
83639#endif
83640 pFlac->firstFLACFramePosInBytes = firstFramePos;
83641#ifndef DR_FLAC_NO_OGG
83642 if (init.container == drflac_container_ogg)
83643 {
83644 pFlac->pSeekpoints = NULL;
83645 pFlac->seekpointCount = 0;
83646 }
83647 else
83648#endif
83649 {
83650 if (seektablePos != 0) {
83651 pFlac->seekpointCount = seektableSize / sizeof(*pFlac->pSeekpoints);
83652 pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize);
83653 DRFLAC_ASSERT(pFlac->bs.onSeek != NULL);
83654 DRFLAC_ASSERT(pFlac->bs.onRead != NULL);
83655 if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) {
83656 if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints, seektableSize) == seektableSize) {
83657 drflac_uint32 iSeekpoint;
83658 for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
83659 pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame);
83660 pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset);
83661 pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount);
83662 }
83663 } else {
83664 pFlac->pSeekpoints = NULL;
83665 pFlac->seekpointCount = 0;
83666 }
83667 if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) {
83668 drflac__free_from_callbacks(pFlac, &allocationCallbacks);
83669 return NULL;
83670 }
83671 } else {
83672 pFlac->pSeekpoints = NULL;
83673 pFlac->seekpointCount = 0;
83674 }
83675 }
83676 }
83677 if (!init.hasStreamInfoBlock) {
83678 pFlac->currentFLACFrame.header = init.firstFrameHeader;
83679 for (;;) {
83680 drflac_result result = drflac__decode_flac_frame(pFlac);
83681 if (result == DRFLAC_SUCCESS) {
83682 break;
83683 } else {
83684 if (result == DRFLAC_CRC_MISMATCH) {
83685 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
83686 drflac__free_from_callbacks(pFlac, &allocationCallbacks);
83687 return NULL;
83688 }
83689 continue;
83690 } else {
83691 drflac__free_from_callbacks(pFlac, &allocationCallbacks);
83692 return NULL;
83693 }
83694 }
83695 }
83696 }
83697 return pFlac;
83698}
83699#ifndef DR_FLAC_NO_STDIO
83700#include <stdio.h>
83701#include <wchar.h>
83702#include <errno.h>
83703static drflac_result drflac_result_from_errno(int e)
83704{
83705 switch (e)
83706 {
83707 case 0: return DRFLAC_SUCCESS;
83708 #ifdef EPERM
83709 case EPERM: return DRFLAC_INVALID_OPERATION;
83710 #endif
83711 #ifdef ENOENT
83712 case ENOENT: return DRFLAC_DOES_NOT_EXIST;
83713 #endif
83714 #ifdef ESRCH
83715 case ESRCH: return DRFLAC_DOES_NOT_EXIST;
83716 #endif
83717 #ifdef EINTR
83718 case EINTR: return DRFLAC_INTERRUPT;
83719 #endif
83720 #ifdef EIO
83721 case EIO: return DRFLAC_IO_ERROR;
83722 #endif
83723 #ifdef ENXIO
83724 case ENXIO: return DRFLAC_DOES_NOT_EXIST;
83725 #endif
83726 #ifdef E2BIG
83727 case E2BIG: return DRFLAC_INVALID_ARGS;
83728 #endif
83729 #ifdef ENOEXEC
83730 case ENOEXEC: return DRFLAC_INVALID_FILE;
83731 #endif
83732 #ifdef EBADF
83733 case EBADF: return DRFLAC_INVALID_FILE;
83734 #endif
83735 #ifdef ECHILD
83736 case ECHILD: return DRFLAC_ERROR;
83737 #endif
83738 #ifdef EAGAIN
83739 case EAGAIN: return DRFLAC_UNAVAILABLE;
83740 #endif
83741 #ifdef ENOMEM
83742 case ENOMEM: return DRFLAC_OUT_OF_MEMORY;
83743 #endif
83744 #ifdef EACCES
83745 case EACCES: return DRFLAC_ACCESS_DENIED;
83746 #endif
83747 #ifdef EFAULT
83748 case EFAULT: return DRFLAC_BAD_ADDRESS;
83749 #endif
83750 #ifdef ENOTBLK
83751 case ENOTBLK: return DRFLAC_ERROR;
83752 #endif
83753 #ifdef EBUSY
83754 case EBUSY: return DRFLAC_BUSY;
83755 #endif
83756 #ifdef EEXIST
83757 case EEXIST: return DRFLAC_ALREADY_EXISTS;
83758 #endif
83759 #ifdef EXDEV
83760 case EXDEV: return DRFLAC_ERROR;
83761 #endif
83762 #ifdef ENODEV
83763 case ENODEV: return DRFLAC_DOES_NOT_EXIST;
83764 #endif
83765 #ifdef ENOTDIR
83766 case ENOTDIR: return DRFLAC_NOT_DIRECTORY;
83767 #endif
83768 #ifdef EISDIR
83769 case EISDIR: return DRFLAC_IS_DIRECTORY;
83770 #endif
83771 #ifdef EINVAL
83772 case EINVAL: return DRFLAC_INVALID_ARGS;
83773 #endif
83774 #ifdef ENFILE
83775 case ENFILE: return DRFLAC_TOO_MANY_OPEN_FILES;
83776 #endif
83777 #ifdef EMFILE
83778 case EMFILE: return DRFLAC_TOO_MANY_OPEN_FILES;
83779 #endif
83780 #ifdef ENOTTY
83781 case ENOTTY: return DRFLAC_INVALID_OPERATION;
83782 #endif
83783 #ifdef ETXTBSY
83784 case ETXTBSY: return DRFLAC_BUSY;
83785 #endif
83786 #ifdef EFBIG
83787 case EFBIG: return DRFLAC_TOO_BIG;
83788 #endif
83789 #ifdef ENOSPC
83790 case ENOSPC: return DRFLAC_NO_SPACE;
83791 #endif
83792 #ifdef ESPIPE
83793 case ESPIPE: return DRFLAC_BAD_SEEK;
83794 #endif
83795 #ifdef EROFS
83796 case EROFS: return DRFLAC_ACCESS_DENIED;
83797 #endif
83798 #ifdef EMLINK
83799 case EMLINK: return DRFLAC_TOO_MANY_LINKS;
83800 #endif
83801 #ifdef EPIPE
83802 case EPIPE: return DRFLAC_BAD_PIPE;
83803 #endif
83804 #ifdef EDOM
83805 case EDOM: return DRFLAC_OUT_OF_RANGE;
83806 #endif
83807 #ifdef ERANGE
83808 case ERANGE: return DRFLAC_OUT_OF_RANGE;
83809 #endif
83810 #ifdef EDEADLK
83811 case EDEADLK: return DRFLAC_DEADLOCK;
83812 #endif
83813 #ifdef ENAMETOOLONG
83814 case ENAMETOOLONG: return DRFLAC_PATH_TOO_LONG;
83815 #endif
83816 #ifdef ENOLCK
83817 case ENOLCK: return DRFLAC_ERROR;
83818 #endif
83819 #ifdef ENOSYS
83820 case ENOSYS: return DRFLAC_NOT_IMPLEMENTED;
83821 #endif
83822 #ifdef ENOTEMPTY
83823 case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY;
83824 #endif
83825 #ifdef ELOOP
83826 case ELOOP: return DRFLAC_TOO_MANY_LINKS;
83827 #endif
83828 #ifdef ENOMSG
83829 case ENOMSG: return DRFLAC_NO_MESSAGE;
83830 #endif
83831 #ifdef EIDRM
83832 case EIDRM: return DRFLAC_ERROR;
83833 #endif
83834 #ifdef ECHRNG
83835 case ECHRNG: return DRFLAC_ERROR;
83836 #endif
83837 #ifdef EL2NSYNC
83838 case EL2NSYNC: return DRFLAC_ERROR;
83839 #endif
83840 #ifdef EL3HLT
83841 case EL3HLT: return DRFLAC_ERROR;
83842 #endif
83843 #ifdef EL3RST
83844 case EL3RST: return DRFLAC_ERROR;
83845 #endif
83846 #ifdef ELNRNG
83847 case ELNRNG: return DRFLAC_OUT_OF_RANGE;
83848 #endif
83849 #ifdef EUNATCH
83850 case EUNATCH: return DRFLAC_ERROR;
83851 #endif
83852 #ifdef ENOCSI
83853 case ENOCSI: return DRFLAC_ERROR;
83854 #endif
83855 #ifdef EL2HLT
83856 case EL2HLT: return DRFLAC_ERROR;
83857 #endif
83858 #ifdef EBADE
83859 case EBADE: return DRFLAC_ERROR;
83860 #endif
83861 #ifdef EBADR
83862 case EBADR: return DRFLAC_ERROR;
83863 #endif
83864 #ifdef EXFULL
83865 case EXFULL: return DRFLAC_ERROR;
83866 #endif
83867 #ifdef ENOANO
83868 case ENOANO: return DRFLAC_ERROR;
83869 #endif
83870 #ifdef EBADRQC
83871 case EBADRQC: return DRFLAC_ERROR;
83872 #endif
83873 #ifdef EBADSLT
83874 case EBADSLT: return DRFLAC_ERROR;
83875 #endif
83876 #ifdef EBFONT
83877 case EBFONT: return DRFLAC_INVALID_FILE;
83878 #endif
83879 #ifdef ENOSTR
83880 case ENOSTR: return DRFLAC_ERROR;
83881 #endif
83882 #ifdef ENODATA
83883 case ENODATA: return DRFLAC_NO_DATA_AVAILABLE;
83884 #endif
83885 #ifdef ETIME
83886 case ETIME: return DRFLAC_TIMEOUT;
83887 #endif
83888 #ifdef ENOSR
83889 case ENOSR: return DRFLAC_NO_DATA_AVAILABLE;
83890 #endif
83891 #ifdef ENONET
83892 case ENONET: return DRFLAC_NO_NETWORK;
83893 #endif
83894 #ifdef ENOPKG
83895 case ENOPKG: return DRFLAC_ERROR;
83896 #endif
83897 #ifdef EREMOTE
83898 case EREMOTE: return DRFLAC_ERROR;
83899 #endif
83900 #ifdef ENOLINK
83901 case ENOLINK: return DRFLAC_ERROR;
83902 #endif
83903 #ifdef EADV
83904 case EADV: return DRFLAC_ERROR;
83905 #endif
83906 #ifdef ESRMNT
83907 case ESRMNT: return DRFLAC_ERROR;
83908 #endif
83909 #ifdef ECOMM
83910 case ECOMM: return DRFLAC_ERROR;
83911 #endif
83912 #ifdef EPROTO
83913 case EPROTO: return DRFLAC_ERROR;
83914 #endif
83915 #ifdef EMULTIHOP
83916 case EMULTIHOP: return DRFLAC_ERROR;
83917 #endif
83918 #ifdef EDOTDOT
83919 case EDOTDOT: return DRFLAC_ERROR;
83920 #endif
83921 #ifdef EBADMSG
83922 case EBADMSG: return DRFLAC_BAD_MESSAGE;
83923 #endif
83924 #ifdef EOVERFLOW
83925 case EOVERFLOW: return DRFLAC_TOO_BIG;
83926 #endif
83927 #ifdef ENOTUNIQ
83928 case ENOTUNIQ: return DRFLAC_NOT_UNIQUE;
83929 #endif
83930 #ifdef EBADFD
83931 case EBADFD: return DRFLAC_ERROR;
83932 #endif
83933 #ifdef EREMCHG
83934 case EREMCHG: return DRFLAC_ERROR;
83935 #endif
83936 #ifdef ELIBACC
83937 case ELIBACC: return DRFLAC_ACCESS_DENIED;
83938 #endif
83939 #ifdef ELIBBAD
83940 case ELIBBAD: return DRFLAC_INVALID_FILE;
83941 #endif
83942 #ifdef ELIBSCN
83943 case ELIBSCN: return DRFLAC_INVALID_FILE;
83944 #endif
83945 #ifdef ELIBMAX
83946 case ELIBMAX: return DRFLAC_ERROR;
83947 #endif
83948 #ifdef ELIBEXEC
83949 case ELIBEXEC: return DRFLAC_ERROR;
83950 #endif
83951 #ifdef EILSEQ
83952 case EILSEQ: return DRFLAC_INVALID_DATA;
83953 #endif
83954 #ifdef ERESTART
83955 case ERESTART: return DRFLAC_ERROR;
83956 #endif
83957 #ifdef ESTRPIPE
83958 case ESTRPIPE: return DRFLAC_ERROR;
83959 #endif
83960 #ifdef EUSERS
83961 case EUSERS: return DRFLAC_ERROR;
83962 #endif
83963 #ifdef ENOTSOCK
83964 case ENOTSOCK: return DRFLAC_NOT_SOCKET;
83965 #endif
83966 #ifdef EDESTADDRREQ
83967 case EDESTADDRREQ: return DRFLAC_NO_ADDRESS;
83968 #endif
83969 #ifdef EMSGSIZE
83970 case EMSGSIZE: return DRFLAC_TOO_BIG;
83971 #endif
83972 #ifdef EPROTOTYPE
83973 case EPROTOTYPE: return DRFLAC_BAD_PROTOCOL;
83974 #endif
83975 #ifdef ENOPROTOOPT
83976 case ENOPROTOOPT: return DRFLAC_PROTOCOL_UNAVAILABLE;
83977 #endif
83978 #ifdef EPROTONOSUPPORT
83979 case EPROTONOSUPPORT: return DRFLAC_PROTOCOL_NOT_SUPPORTED;
83980 #endif
83981 #ifdef ESOCKTNOSUPPORT
83982 case ESOCKTNOSUPPORT: return DRFLAC_SOCKET_NOT_SUPPORTED;
83983 #endif
83984 #ifdef EOPNOTSUPP
83985 case EOPNOTSUPP: return DRFLAC_INVALID_OPERATION;
83986 #endif
83987 #ifdef EPFNOSUPPORT
83988 case EPFNOSUPPORT: return DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED;
83989 #endif
83990 #ifdef EAFNOSUPPORT
83991 case EAFNOSUPPORT: return DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED;
83992 #endif
83993 #ifdef EADDRINUSE
83994 case EADDRINUSE: return DRFLAC_ALREADY_IN_USE;
83995 #endif
83996 #ifdef EADDRNOTAVAIL
83997 case EADDRNOTAVAIL: return DRFLAC_ERROR;
83998 #endif
83999 #ifdef ENETDOWN
84000 case ENETDOWN: return DRFLAC_NO_NETWORK;
84001 #endif
84002 #ifdef ENETUNREACH
84003 case ENETUNREACH: return DRFLAC_NO_NETWORK;
84004 #endif
84005 #ifdef ENETRESET
84006 case ENETRESET: return DRFLAC_NO_NETWORK;
84007 #endif
84008 #ifdef ECONNABORTED
84009 case ECONNABORTED: return DRFLAC_NO_NETWORK;
84010 #endif
84011 #ifdef ECONNRESET
84012 case ECONNRESET: return DRFLAC_CONNECTION_RESET;
84013 #endif
84014 #ifdef ENOBUFS
84015 case ENOBUFS: return DRFLAC_NO_SPACE;
84016 #endif
84017 #ifdef EISCONN
84018 case EISCONN: return DRFLAC_ALREADY_CONNECTED;
84019 #endif
84020 #ifdef ENOTCONN
84021 case ENOTCONN: return DRFLAC_NOT_CONNECTED;
84022 #endif
84023 #ifdef ESHUTDOWN
84024 case ESHUTDOWN: return DRFLAC_ERROR;
84025 #endif
84026 #ifdef ETOOMANYREFS
84027 case ETOOMANYREFS: return DRFLAC_ERROR;
84028 #endif
84029 #ifdef ETIMEDOUT
84030 case ETIMEDOUT: return DRFLAC_TIMEOUT;
84031 #endif
84032 #ifdef ECONNREFUSED
84033 case ECONNREFUSED: return DRFLAC_CONNECTION_REFUSED;
84034 #endif
84035 #ifdef EHOSTDOWN
84036 case EHOSTDOWN: return DRFLAC_NO_HOST;
84037 #endif
84038 #ifdef EHOSTUNREACH
84039 case EHOSTUNREACH: return DRFLAC_NO_HOST;
84040 #endif
84041 #ifdef EALREADY
84042 case EALREADY: return DRFLAC_IN_PROGRESS;
84043 #endif
84044 #ifdef EINPROGRESS
84045 case EINPROGRESS: return DRFLAC_IN_PROGRESS;
84046 #endif
84047 #ifdef ESTALE
84048 case ESTALE: return DRFLAC_INVALID_FILE;
84049 #endif
84050 #ifdef EUCLEAN
84051 case EUCLEAN: return DRFLAC_ERROR;
84052 #endif
84053 #ifdef ENOTNAM
84054 case ENOTNAM: return DRFLAC_ERROR;
84055 #endif
84056 #ifdef ENAVAIL
84057 case ENAVAIL: return DRFLAC_ERROR;
84058 #endif
84059 #ifdef EISNAM
84060 case EISNAM: return DRFLAC_ERROR;
84061 #endif
84062 #ifdef EREMOTEIO
84063 case EREMOTEIO: return DRFLAC_IO_ERROR;
84064 #endif
84065 #ifdef EDQUOT
84066 case EDQUOT: return DRFLAC_NO_SPACE;
84067 #endif
84068 #ifdef ENOMEDIUM
84069 case ENOMEDIUM: return DRFLAC_DOES_NOT_EXIST;
84070 #endif
84071 #ifdef EMEDIUMTYPE
84072 case EMEDIUMTYPE: return DRFLAC_ERROR;
84073 #endif
84074 #ifdef ECANCELED
84075 case ECANCELED: return DRFLAC_CANCELLED;
84076 #endif
84077 #ifdef ENOKEY
84078 case ENOKEY: return DRFLAC_ERROR;
84079 #endif
84080 #ifdef EKEYEXPIRED
84081 case EKEYEXPIRED: return DRFLAC_ERROR;
84082 #endif
84083 #ifdef EKEYREVOKED
84084 case EKEYREVOKED: return DRFLAC_ERROR;
84085 #endif
84086 #ifdef EKEYREJECTED
84087 case EKEYREJECTED: return DRFLAC_ERROR;
84088 #endif
84089 #ifdef EOWNERDEAD
84090 case EOWNERDEAD: return DRFLAC_ERROR;
84091 #endif
84092 #ifdef ENOTRECOVERABLE
84093 case ENOTRECOVERABLE: return DRFLAC_ERROR;
84094 #endif
84095 #ifdef ERFKILL
84096 case ERFKILL: return DRFLAC_ERROR;
84097 #endif
84098 #ifdef EHWPOISON
84099 case EHWPOISON: return DRFLAC_ERROR;
84100 #endif
84101 default: return DRFLAC_ERROR;
84102 }
84103}
84104static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
84105{
84106#if defined(_MSC_VER) && _MSC_VER >= 1400
84107 errno_t err;
84108#endif
84109 if (ppFile != NULL) {
84110 *ppFile = NULL;
84111 }
84112 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
84113 return DRFLAC_INVALID_ARGS;
84114 }
84115#if defined(_MSC_VER) && _MSC_VER >= 1400
84116 err = fopen_s(ppFile, pFilePath, pOpenMode);
84117 if (err != 0) {
84118 return drflac_result_from_errno(err);
84119 }
84120#else
84121#if defined(_WIN32) || defined(__APPLE__)
84122 *ppFile = fopen(pFilePath, pOpenMode);
84123#else
84124 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
84125 *ppFile = fopen64(pFilePath, pOpenMode);
84126 #else
84127 *ppFile = fopen(pFilePath, pOpenMode);
84128 #endif
84129#endif
84130 if (*ppFile == NULL) {
84131 drflac_result result = drflac_result_from_errno(errno);
84132 if (result == DRFLAC_SUCCESS) {
84133 result = DRFLAC_ERROR;
84134 }
84135 return result;
84136 }
84137#endif
84138 return DRFLAC_SUCCESS;
84139}
84140#if defined(_WIN32)
84141 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
84142 #define DRFLAC_HAS_WFOPEN
84143 #endif
84144#endif
84145static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks)
84146{
84147 if (ppFile != NULL) {
84148 *ppFile = NULL;
84149 }
84150 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
84151 return DRFLAC_INVALID_ARGS;
84152 }
84153#if defined(DRFLAC_HAS_WFOPEN)
84154 {
84155 #if defined(_MSC_VER) && _MSC_VER >= 1400
84156 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
84157 if (err != 0) {
84158 return drflac_result_from_errno(err);
84159 }
84160 #else
84161 *ppFile = _wfopen(pFilePath, pOpenMode);
84162 if (*ppFile == NULL) {
84163 return drflac_result_from_errno(errno);
84164 }
84165 #endif
84166 (void)pAllocationCallbacks;
84167 }
84168#else
84169 {
84170 mbstate_t mbs;
84171 size_t lenMB;
84172 const wchar_t* pFilePathTemp = pFilePath;
84173 char* pFilePathMB = NULL;
84174 char pOpenModeMB[32] = {0};
84175 DRFLAC_ZERO_OBJECT(&mbs);
84176 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
84177 if (lenMB == (size_t)-1) {
84178 return drflac_result_from_errno(errno);
84179 }
84180 pFilePathMB = (char*)drflac__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
84181 if (pFilePathMB == NULL) {
84182 return DRFLAC_OUT_OF_MEMORY;
84183 }
84184 pFilePathTemp = pFilePath;
84185 DRFLAC_ZERO_OBJECT(&mbs);
84186 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
84187 {
84188 size_t i = 0;
84189 for (;;) {
84190 if (pOpenMode[i] == 0) {
84191 pOpenModeMB[i] = '\0';
84192 break;
84193 }
84194 pOpenModeMB[i] = (char)pOpenMode[i];
84195 i += 1;
84196 }
84197 }
84198 *ppFile = fopen(pFilePathMB, pOpenModeMB);
84199 drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
84200 }
84201 if (*ppFile == NULL) {
84202 return DRFLAC_ERROR;
84203 }
84204#endif
84205 return DRFLAC_SUCCESS;
84206}
84207static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
84208{
84209 return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData);
84210}
84211static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin)
84212{
84213 DRFLAC_ASSERT(offset >= 0);
84214 return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
84215}
84216DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks)
84217{
84218 drflac* pFlac;
84219 FILE* pFile;
84220 if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) {
84221 return NULL;
84222 }
84223 pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
84224 if (pFlac == NULL) {
84225 fclose(pFile);
84226 return NULL;
84227 }
84228 return pFlac;
84229}
84230DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks)
84231{
84232 drflac* pFlac;
84233 FILE* pFile;
84234 if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) {
84235 return NULL;
84236 }
84237 pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
84238 if (pFlac == NULL) {
84239 fclose(pFile);
84240 return NULL;
84241 }
84242 return pFlac;
84243}
84244DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
84245{
84246 drflac* pFlac;
84247 FILE* pFile;
84248 if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) {
84249 return NULL;
84250 }
84251 pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
84252 if (pFlac == NULL) {
84253 fclose(pFile);
84254 return pFlac;
84255 }
84256 return pFlac;
84257}
84258DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
84259{
84260 drflac* pFlac;
84261 FILE* pFile;
84262 if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) {
84263 return NULL;
84264 }
84265 pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
84266 if (pFlac == NULL) {
84267 fclose(pFile);
84268 return pFlac;
84269 }
84270 return pFlac;
84271}
84272#endif
84273static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)
84274{
84275 drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
84276 size_t bytesRemaining;
84277 DRFLAC_ASSERT(memoryStream != NULL);
84278 DRFLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos);
84279 bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos;
84280 if (bytesToRead > bytesRemaining) {
84281 bytesToRead = bytesRemaining;
84282 }
84283 if (bytesToRead > 0) {
84284 DRFLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead);
84285 memoryStream->currentReadPos += bytesToRead;
84286 }
84287 return bytesToRead;
84288}
84289static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin)
84290{
84291 drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
84292 DRFLAC_ASSERT(memoryStream != NULL);
84293 DRFLAC_ASSERT(offset >= 0);
84294 if (offset > (drflac_int64)memoryStream->dataSize) {
84295 return DRFLAC_FALSE;
84296 }
84297 if (origin == drflac_seek_origin_current) {
84298 if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) {
84299 memoryStream->currentReadPos += offset;
84300 } else {
84301 return DRFLAC_FALSE;
84302 }
84303 } else {
84304 if ((drflac_uint32)offset <= memoryStream->dataSize) {
84305 memoryStream->currentReadPos = offset;
84306 } else {
84307 return DRFLAC_FALSE;
84308 }
84309 }
84310 return DRFLAC_TRUE;
84311}
84312DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks)
84313{
84314 drflac__memory_stream memoryStream;
84315 drflac* pFlac;
84316 memoryStream.data = (const drflac_uint8*)pData;
84317 memoryStream.dataSize = dataSize;
84318 memoryStream.currentReadPos = 0;
84319 pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks);
84320 if (pFlac == NULL) {
84321 return NULL;
84322 }
84323 pFlac->memoryStream = memoryStream;
84324#ifndef DR_FLAC_NO_OGG
84325 if (pFlac->container == drflac_container_ogg)
84326 {
84327 drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
84328 oggbs->pUserData = &pFlac->memoryStream;
84329 }
84330 else
84331#endif
84332 {
84333 pFlac->bs.pUserData = &pFlac->memoryStream;
84334 }
84335 return pFlac;
84336}
84337DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
84338{
84339 drflac__memory_stream memoryStream;
84340 drflac* pFlac;
84341 memoryStream.data = (const drflac_uint8*)pData;
84342 memoryStream.dataSize = dataSize;
84343 memoryStream.currentReadPos = 0;
84344 pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks);
84345 if (pFlac == NULL) {
84346 return NULL;
84347 }
84348 pFlac->memoryStream = memoryStream;
84349#ifndef DR_FLAC_NO_OGG
84350 if (pFlac->container == drflac_container_ogg)
84351 {
84352 drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
84353 oggbs->pUserData = &pFlac->memoryStream;
84354 }
84355 else
84356#endif
84357 {
84358 pFlac->bs.pUserData = &pFlac->memoryStream;
84359 }
84360 return pFlac;
84361}
84362DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
84363{
84364 return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
84365}
84366DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
84367{
84368 return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks);
84369}
84370DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
84371{
84372 return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
84373}
84374DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
84375{
84376 return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks);
84377}
84378DRFLAC_API void drflac_close(drflac* pFlac)
84379{
84380 if (pFlac == NULL) {
84381 return;
84382 }
84383#ifndef DR_FLAC_NO_STDIO
84384 if (pFlac->bs.onRead == drflac__on_read_stdio) {
84385 fclose((FILE*)pFlac->bs.pUserData);
84386 }
84387#ifndef DR_FLAC_NO_OGG
84388 if (pFlac->container == drflac_container_ogg) {
84389 drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
84390 DRFLAC_ASSERT(pFlac->bs.onRead == drflac__on_read_ogg);
84391 if (oggbs->onRead == drflac__on_read_stdio) {
84392 fclose((FILE*)oggbs->pUserData);
84393 }
84394 }
84395#endif
84396#endif
84397 drflac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks);
84398}
84399#if 0
84400static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84401{
84402 drflac_uint64 i;
84403 for (i = 0; i < frameCount; ++i) {
84404 drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
84405 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
84406 drflac_uint32 right = left - side;
84407 pOutputSamples[i*2+0] = (drflac_int32)left;
84408 pOutputSamples[i*2+1] = (drflac_int32)right;
84409 }
84410}
84411#endif
84412static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84413{
84414 drflac_uint64 i;
84415 drflac_uint64 frameCount4 = frameCount >> 2;
84416 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
84417 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
84418 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84419 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84420 for (i = 0; i < frameCount4; ++i) {
84421 drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
84422 drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
84423 drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
84424 drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
84425 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
84426 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
84427 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
84428 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
84429 drflac_uint32 right0 = left0 - side0;
84430 drflac_uint32 right1 = left1 - side1;
84431 drflac_uint32 right2 = left2 - side2;
84432 drflac_uint32 right3 = left3 - side3;
84433 pOutputSamples[i*8+0] = (drflac_int32)left0;
84434 pOutputSamples[i*8+1] = (drflac_int32)right0;
84435 pOutputSamples[i*8+2] = (drflac_int32)left1;
84436 pOutputSamples[i*8+3] = (drflac_int32)right1;
84437 pOutputSamples[i*8+4] = (drflac_int32)left2;
84438 pOutputSamples[i*8+5] = (drflac_int32)right2;
84439 pOutputSamples[i*8+6] = (drflac_int32)left3;
84440 pOutputSamples[i*8+7] = (drflac_int32)right3;
84441 }
84442 for (i = (frameCount4 << 2); i < frameCount; ++i) {
84443 drflac_uint32 left = pInputSamples0U32[i] << shift0;
84444 drflac_uint32 side = pInputSamples1U32[i] << shift1;
84445 drflac_uint32 right = left - side;
84446 pOutputSamples[i*2+0] = (drflac_int32)left;
84447 pOutputSamples[i*2+1] = (drflac_int32)right;
84448 }
84449}
84450#if defined(DRFLAC_SUPPORT_SSE2)
84451static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84452{
84453 drflac_uint64 i;
84454 drflac_uint64 frameCount4 = frameCount >> 2;
84455 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
84456 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
84457 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84458 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84459 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
84460 for (i = 0; i < frameCount4; ++i) {
84461 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
84462 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
84463 __m128i right = _mm_sub_epi32(left, side);
84464 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
84465 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
84466 }
84467 for (i = (frameCount4 << 2); i < frameCount; ++i) {
84468 drflac_uint32 left = pInputSamples0U32[i] << shift0;
84469 drflac_uint32 side = pInputSamples1U32[i] << shift1;
84470 drflac_uint32 right = left - side;
84471 pOutputSamples[i*2+0] = (drflac_int32)left;
84472 pOutputSamples[i*2+1] = (drflac_int32)right;
84473 }
84474}
84475#endif
84476#if defined(DRFLAC_SUPPORT_NEON)
84477static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84478{
84479 drflac_uint64 i;
84480 drflac_uint64 frameCount4 = frameCount >> 2;
84481 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
84482 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
84483 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84484 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84485 int32x4_t shift0_4;
84486 int32x4_t shift1_4;
84487 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
84488 shift0_4 = vdupq_n_s32(shift0);
84489 shift1_4 = vdupq_n_s32(shift1);
84490 for (i = 0; i < frameCount4; ++i) {
84491 uint32x4_t left;
84492 uint32x4_t side;
84493 uint32x4_t right;
84494 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
84495 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
84496 right = vsubq_u32(left, side);
84497 drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right));
84498 }
84499 for (i = (frameCount4 << 2); i < frameCount; ++i) {
84500 drflac_uint32 left = pInputSamples0U32[i] << shift0;
84501 drflac_uint32 side = pInputSamples1U32[i] << shift1;
84502 drflac_uint32 right = left - side;
84503 pOutputSamples[i*2+0] = (drflac_int32)left;
84504 pOutputSamples[i*2+1] = (drflac_int32)right;
84505 }
84506}
84507#endif
84508static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84509{
84510#if defined(DRFLAC_SUPPORT_SSE2)
84511 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
84512 drflac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
84513 } else
84514#elif defined(DRFLAC_SUPPORT_NEON)
84515 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
84516 drflac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
84517 } else
84518#endif
84519 {
84520#if 0
84521 drflac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
84522#else
84523 drflac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
84524#endif
84525 }
84526}
84527#if 0
84528static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84529{
84530 drflac_uint64 i;
84531 for (i = 0; i < frameCount; ++i) {
84532 drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
84533 drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
84534 drflac_uint32 left = right + side;
84535 pOutputSamples[i*2+0] = (drflac_int32)left;
84536 pOutputSamples[i*2+1] = (drflac_int32)right;
84537 }
84538}
84539#endif
84540static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84541{
84542 drflac_uint64 i;
84543 drflac_uint64 frameCount4 = frameCount >> 2;
84544 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
84545 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
84546 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84547 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84548 for (i = 0; i < frameCount4; ++i) {
84549 drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
84550 drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
84551 drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
84552 drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
84553 drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
84554 drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
84555 drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
84556 drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
84557 drflac_uint32 left0 = right0 + side0;
84558 drflac_uint32 left1 = right1 + side1;
84559 drflac_uint32 left2 = right2 + side2;
84560 drflac_uint32 left3 = right3 + side3;
84561 pOutputSamples[i*8+0] = (drflac_int32)left0;
84562 pOutputSamples[i*8+1] = (drflac_int32)right0;
84563 pOutputSamples[i*8+2] = (drflac_int32)left1;
84564 pOutputSamples[i*8+3] = (drflac_int32)right1;
84565 pOutputSamples[i*8+4] = (drflac_int32)left2;
84566 pOutputSamples[i*8+5] = (drflac_int32)right2;
84567 pOutputSamples[i*8+6] = (drflac_int32)left3;
84568 pOutputSamples[i*8+7] = (drflac_int32)right3;
84569 }
84570 for (i = (frameCount4 << 2); i < frameCount; ++i) {
84571 drflac_uint32 side = pInputSamples0U32[i] << shift0;
84572 drflac_uint32 right = pInputSamples1U32[i] << shift1;
84573 drflac_uint32 left = right + side;
84574 pOutputSamples[i*2+0] = (drflac_int32)left;
84575 pOutputSamples[i*2+1] = (drflac_int32)right;
84576 }
84577}
84578#if defined(DRFLAC_SUPPORT_SSE2)
84579static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84580{
84581 drflac_uint64 i;
84582 drflac_uint64 frameCount4 = frameCount >> 2;
84583 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
84584 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
84585 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84586 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84587 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
84588 for (i = 0; i < frameCount4; ++i) {
84589 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
84590 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
84591 __m128i left = _mm_add_epi32(right, side);
84592 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
84593 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
84594 }
84595 for (i = (frameCount4 << 2); i < frameCount; ++i) {
84596 drflac_uint32 side = pInputSamples0U32[i] << shift0;
84597 drflac_uint32 right = pInputSamples1U32[i] << shift1;
84598 drflac_uint32 left = right + side;
84599 pOutputSamples[i*2+0] = (drflac_int32)left;
84600 pOutputSamples[i*2+1] = (drflac_int32)right;
84601 }
84602}
84603#endif
84604#if defined(DRFLAC_SUPPORT_NEON)
84605static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84606{
84607 drflac_uint64 i;
84608 drflac_uint64 frameCount4 = frameCount >> 2;
84609 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
84610 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
84611 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84612 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84613 int32x4_t shift0_4;
84614 int32x4_t shift1_4;
84615 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
84616 shift0_4 = vdupq_n_s32(shift0);
84617 shift1_4 = vdupq_n_s32(shift1);
84618 for (i = 0; i < frameCount4; ++i) {
84619 uint32x4_t side;
84620 uint32x4_t right;
84621 uint32x4_t left;
84622 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
84623 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
84624 left = vaddq_u32(right, side);
84625 drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right));
84626 }
84627 for (i = (frameCount4 << 2); i < frameCount; ++i) {
84628 drflac_uint32 side = pInputSamples0U32[i] << shift0;
84629 drflac_uint32 right = pInputSamples1U32[i] << shift1;
84630 drflac_uint32 left = right + side;
84631 pOutputSamples[i*2+0] = (drflac_int32)left;
84632 pOutputSamples[i*2+1] = (drflac_int32)right;
84633 }
84634}
84635#endif
84636static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84637{
84638#if defined(DRFLAC_SUPPORT_SSE2)
84639 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
84640 drflac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
84641 } else
84642#elif defined(DRFLAC_SUPPORT_NEON)
84643 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
84644 drflac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
84645 } else
84646#endif
84647 {
84648#if 0
84649 drflac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
84650#else
84651 drflac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
84652#endif
84653 }
84654}
84655#if 0
84656static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84657{
84658 for (drflac_uint64 i = 0; i < frameCount; ++i) {
84659 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84660 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84661 mid = (mid << 1) | (side & 0x01);
84662 pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample);
84663 pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample);
84664 }
84665}
84666#endif
84667static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84668{
84669 drflac_uint64 i;
84670 drflac_uint64 frameCount4 = frameCount >> 2;
84671 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
84672 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
84673 drflac_int32 shift = unusedBitsPerSample;
84674 if (shift > 0) {
84675 shift -= 1;
84676 for (i = 0; i < frameCount4; ++i) {
84677 drflac_uint32 temp0L;
84678 drflac_uint32 temp1L;
84679 drflac_uint32 temp2L;
84680 drflac_uint32 temp3L;
84681 drflac_uint32 temp0R;
84682 drflac_uint32 temp1R;
84683 drflac_uint32 temp2R;
84684 drflac_uint32 temp3R;
84685 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84686 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84687 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84688 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84689 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84690 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84691 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84692 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84693 mid0 = (mid0 << 1) | (side0 & 0x01);
84694 mid1 = (mid1 << 1) | (side1 & 0x01);
84695 mid2 = (mid2 << 1) | (side2 & 0x01);
84696 mid3 = (mid3 << 1) | (side3 & 0x01);
84697 temp0L = (mid0 + side0) << shift;
84698 temp1L = (mid1 + side1) << shift;
84699 temp2L = (mid2 + side2) << shift;
84700 temp3L = (mid3 + side3) << shift;
84701 temp0R = (mid0 - side0) << shift;
84702 temp1R = (mid1 - side1) << shift;
84703 temp2R = (mid2 - side2) << shift;
84704 temp3R = (mid3 - side3) << shift;
84705 pOutputSamples[i*8+0] = (drflac_int32)temp0L;
84706 pOutputSamples[i*8+1] = (drflac_int32)temp0R;
84707 pOutputSamples[i*8+2] = (drflac_int32)temp1L;
84708 pOutputSamples[i*8+3] = (drflac_int32)temp1R;
84709 pOutputSamples[i*8+4] = (drflac_int32)temp2L;
84710 pOutputSamples[i*8+5] = (drflac_int32)temp2R;
84711 pOutputSamples[i*8+6] = (drflac_int32)temp3L;
84712 pOutputSamples[i*8+7] = (drflac_int32)temp3R;
84713 }
84714 } else {
84715 for (i = 0; i < frameCount4; ++i) {
84716 drflac_uint32 temp0L;
84717 drflac_uint32 temp1L;
84718 drflac_uint32 temp2L;
84719 drflac_uint32 temp3L;
84720 drflac_uint32 temp0R;
84721 drflac_uint32 temp1R;
84722 drflac_uint32 temp2R;
84723 drflac_uint32 temp3R;
84724 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84725 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84726 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84727 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84728 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84729 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84730 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84731 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84732 mid0 = (mid0 << 1) | (side0 & 0x01);
84733 mid1 = (mid1 << 1) | (side1 & 0x01);
84734 mid2 = (mid2 << 1) | (side2 & 0x01);
84735 mid3 = (mid3 << 1) | (side3 & 0x01);
84736 temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1);
84737 temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1);
84738 temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1);
84739 temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1);
84740 temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1);
84741 temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1);
84742 temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1);
84743 temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1);
84744 pOutputSamples[i*8+0] = (drflac_int32)temp0L;
84745 pOutputSamples[i*8+1] = (drflac_int32)temp0R;
84746 pOutputSamples[i*8+2] = (drflac_int32)temp1L;
84747 pOutputSamples[i*8+3] = (drflac_int32)temp1R;
84748 pOutputSamples[i*8+4] = (drflac_int32)temp2L;
84749 pOutputSamples[i*8+5] = (drflac_int32)temp2R;
84750 pOutputSamples[i*8+6] = (drflac_int32)temp3L;
84751 pOutputSamples[i*8+7] = (drflac_int32)temp3R;
84752 }
84753 }
84754 for (i = (frameCount4 << 2); i < frameCount; ++i) {
84755 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84756 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84757 mid = (mid << 1) | (side & 0x01);
84758 pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample);
84759 pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample);
84760 }
84761}
84762#if defined(DRFLAC_SUPPORT_SSE2)
84763static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84764{
84765 drflac_uint64 i;
84766 drflac_uint64 frameCount4 = frameCount >> 2;
84767 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
84768 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
84769 drflac_int32 shift = unusedBitsPerSample;
84770 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
84771 if (shift == 0) {
84772 for (i = 0; i < frameCount4; ++i) {
84773 __m128i mid;
84774 __m128i side;
84775 __m128i left;
84776 __m128i right;
84777 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
84778 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
84779 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
84780 left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
84781 right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
84782 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
84783 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
84784 }
84785 for (i = (frameCount4 << 2); i < frameCount; ++i) {
84786 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84787 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84788 mid = (mid << 1) | (side & 0x01);
84789 pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1;
84790 pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1;
84791 }
84792 } else {
84793 shift -= 1;
84794 for (i = 0; i < frameCount4; ++i) {
84795 __m128i mid;
84796 __m128i side;
84797 __m128i left;
84798 __m128i right;
84799 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
84800 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
84801 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
84802 left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
84803 right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
84804 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
84805 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
84806 }
84807 for (i = (frameCount4 << 2); i < frameCount; ++i) {
84808 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84809 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84810 mid = (mid << 1) | (side & 0x01);
84811 pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift);
84812 pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift);
84813 }
84814 }
84815}
84816#endif
84817#if defined(DRFLAC_SUPPORT_NEON)
84818static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84819{
84820 drflac_uint64 i;
84821 drflac_uint64 frameCount4 = frameCount >> 2;
84822 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
84823 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
84824 drflac_int32 shift = unusedBitsPerSample;
84825 int32x4_t wbpsShift0_4;
84826 int32x4_t wbpsShift1_4;
84827 uint32x4_t one4;
84828 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
84829 wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
84830 wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
84831 one4 = vdupq_n_u32(1);
84832 if (shift == 0) {
84833 for (i = 0; i < frameCount4; ++i) {
84834 uint32x4_t mid;
84835 uint32x4_t side;
84836 int32x4_t left;
84837 int32x4_t right;
84838 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
84839 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
84840 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4));
84841 left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
84842 right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
84843 drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
84844 }
84845 for (i = (frameCount4 << 2); i < frameCount; ++i) {
84846 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84847 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84848 mid = (mid << 1) | (side & 0x01);
84849 pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1;
84850 pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1;
84851 }
84852 } else {
84853 int32x4_t shift4;
84854 shift -= 1;
84855 shift4 = vdupq_n_s32(shift);
84856 for (i = 0; i < frameCount4; ++i) {
84857 uint32x4_t mid;
84858 uint32x4_t side;
84859 int32x4_t left;
84860 int32x4_t right;
84861 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
84862 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
84863 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4));
84864 left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
84865 right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
84866 drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
84867 }
84868 for (i = (frameCount4 << 2); i < frameCount; ++i) {
84869 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84870 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84871 mid = (mid << 1) | (side & 0x01);
84872 pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift);
84873 pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift);
84874 }
84875 }
84876}
84877#endif
84878static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84879{
84880#if defined(DRFLAC_SUPPORT_SSE2)
84881 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
84882 drflac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
84883 } else
84884#elif defined(DRFLAC_SUPPORT_NEON)
84885 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
84886 drflac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
84887 } else
84888#endif
84889 {
84890#if 0
84891 drflac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
84892#else
84893 drflac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
84894#endif
84895 }
84896}
84897#if 0
84898static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84899{
84900 for (drflac_uint64 i = 0; i < frameCount; ++i) {
84901 pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample));
84902 pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample));
84903 }
84904}
84905#endif
84906static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84907{
84908 drflac_uint64 i;
84909 drflac_uint64 frameCount4 = frameCount >> 2;
84910 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
84911 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
84912 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84913 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84914 for (i = 0; i < frameCount4; ++i) {
84915 drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
84916 drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
84917 drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
84918 drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
84919 drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
84920 drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
84921 drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
84922 drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
84923 pOutputSamples[i*8+0] = (drflac_int32)tempL0;
84924 pOutputSamples[i*8+1] = (drflac_int32)tempR0;
84925 pOutputSamples[i*8+2] = (drflac_int32)tempL1;
84926 pOutputSamples[i*8+3] = (drflac_int32)tempR1;
84927 pOutputSamples[i*8+4] = (drflac_int32)tempL2;
84928 pOutputSamples[i*8+5] = (drflac_int32)tempR2;
84929 pOutputSamples[i*8+6] = (drflac_int32)tempL3;
84930 pOutputSamples[i*8+7] = (drflac_int32)tempR3;
84931 }
84932 for (i = (frameCount4 << 2); i < frameCount; ++i) {
84933 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0);
84934 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1);
84935 }
84936}
84937#if defined(DRFLAC_SUPPORT_SSE2)
84938static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84939{
84940 drflac_uint64 i;
84941 drflac_uint64 frameCount4 = frameCount >> 2;
84942 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
84943 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
84944 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84945 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84946 for (i = 0; i < frameCount4; ++i) {
84947 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
84948 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
84949 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
84950 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
84951 }
84952 for (i = (frameCount4 << 2); i < frameCount; ++i) {
84953 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0);
84954 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1);
84955 }
84956}
84957#endif
84958#if defined(DRFLAC_SUPPORT_NEON)
84959static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84960{
84961 drflac_uint64 i;
84962 drflac_uint64 frameCount4 = frameCount >> 2;
84963 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
84964 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
84965 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
84966 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
84967 int32x4_t shift4_0 = vdupq_n_s32(shift0);
84968 int32x4_t shift4_1 = vdupq_n_s32(shift1);
84969 for (i = 0; i < frameCount4; ++i) {
84970 int32x4_t left;
84971 int32x4_t right;
84972 left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0));
84973 right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1));
84974 drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
84975 }
84976 for (i = (frameCount4 << 2); i < frameCount; ++i) {
84977 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0);
84978 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1);
84979 }
84980}
84981#endif
84982static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
84983{
84984#if defined(DRFLAC_SUPPORT_SSE2)
84985 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
84986 drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
84987 } else
84988#elif defined(DRFLAC_SUPPORT_NEON)
84989 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
84990 drflac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
84991 } else
84992#endif
84993 {
84994#if 0
84995 drflac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
84996#else
84997 drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
84998#endif
84999 }
85000}
85002{
85003 drflac_uint64 framesRead;
85004 drflac_uint32 unusedBitsPerSample;
85005 if (pFlac == NULL || framesToRead == 0) {
85006 return 0;
85007 }
85008 if (pBufferOut == NULL) {
85009 return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
85010 }
85011 DRFLAC_ASSERT(pFlac->bitsPerSample <= 32);
85012 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
85013 framesRead = 0;
85014 while (framesToRead > 0) {
85015 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
85016 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
85017 break;
85018 }
85019 } else {
85020 unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
85021 drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
85022 drflac_uint64 frameCountThisIteration = framesToRead;
85023 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
85024 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
85025 }
85026 if (channelCount == 2) {
85027 const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
85028 const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
85030 {
85031 case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
85032 {
85033 drflac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
85034 } break;
85035 case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
85036 {
85037 drflac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
85038 } break;
85039 case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
85040 {
85041 drflac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
85042 } break;
85043 case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
85044 default:
85045 {
85046 drflac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
85047 } break;
85048 }
85049 } else {
85050 drflac_uint64 i;
85051 for (i = 0; i < frameCountThisIteration; ++i) {
85052 unsigned int j;
85053 for (j = 0; j < channelCount; ++j) {
85054 pBufferOut[(i*channelCount)+j] = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
85055 }
85056 }
85057 }
85058 framesRead += frameCountThisIteration;
85059 pBufferOut += frameCountThisIteration * channelCount;
85060 framesToRead -= frameCountThisIteration;
85061 pFlac->currentPCMFrame += frameCountThisIteration;
85062 pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration;
85063 }
85064 }
85065 return framesRead;
85066}
85067#if 0
85068static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85069{
85070 drflac_uint64 i;
85071 for (i = 0; i < frameCount; ++i) {
85072 drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
85073 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
85074 drflac_uint32 right = left - side;
85075 left >>= 16;
85076 right >>= 16;
85077 pOutputSamples[i*2+0] = (drflac_int16)left;
85078 pOutputSamples[i*2+1] = (drflac_int16)right;
85079 }
85080}
85081#endif
85082static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85083{
85084 drflac_uint64 i;
85085 drflac_uint64 frameCount4 = frameCount >> 2;
85086 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
85087 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
85088 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85089 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85090 for (i = 0; i < frameCount4; ++i) {
85091 drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
85092 drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
85093 drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
85094 drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
85095 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
85096 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
85097 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
85098 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
85099 drflac_uint32 right0 = left0 - side0;
85100 drflac_uint32 right1 = left1 - side1;
85101 drflac_uint32 right2 = left2 - side2;
85102 drflac_uint32 right3 = left3 - side3;
85103 left0 >>= 16;
85104 left1 >>= 16;
85105 left2 >>= 16;
85106 left3 >>= 16;
85107 right0 >>= 16;
85108 right1 >>= 16;
85109 right2 >>= 16;
85110 right3 >>= 16;
85111 pOutputSamples[i*8+0] = (drflac_int16)left0;
85112 pOutputSamples[i*8+1] = (drflac_int16)right0;
85113 pOutputSamples[i*8+2] = (drflac_int16)left1;
85114 pOutputSamples[i*8+3] = (drflac_int16)right1;
85115 pOutputSamples[i*8+4] = (drflac_int16)left2;
85116 pOutputSamples[i*8+5] = (drflac_int16)right2;
85117 pOutputSamples[i*8+6] = (drflac_int16)left3;
85118 pOutputSamples[i*8+7] = (drflac_int16)right3;
85119 }
85120 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85121 drflac_uint32 left = pInputSamples0U32[i] << shift0;
85122 drflac_uint32 side = pInputSamples1U32[i] << shift1;
85123 drflac_uint32 right = left - side;
85124 left >>= 16;
85125 right >>= 16;
85126 pOutputSamples[i*2+0] = (drflac_int16)left;
85127 pOutputSamples[i*2+1] = (drflac_int16)right;
85128 }
85129}
85130#if defined(DRFLAC_SUPPORT_SSE2)
85131static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85132{
85133 drflac_uint64 i;
85134 drflac_uint64 frameCount4 = frameCount >> 2;
85135 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
85136 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
85137 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85138 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85139 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
85140 for (i = 0; i < frameCount4; ++i) {
85141 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
85142 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
85143 __m128i right = _mm_sub_epi32(left, side);
85144 left = _mm_srai_epi32(left, 16);
85145 right = _mm_srai_epi32(right, 16);
85146 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
85147 }
85148 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85149 drflac_uint32 left = pInputSamples0U32[i] << shift0;
85150 drflac_uint32 side = pInputSamples1U32[i] << shift1;
85151 drflac_uint32 right = left - side;
85152 left >>= 16;
85153 right >>= 16;
85154 pOutputSamples[i*2+0] = (drflac_int16)left;
85155 pOutputSamples[i*2+1] = (drflac_int16)right;
85156 }
85157}
85158#endif
85159#if defined(DRFLAC_SUPPORT_NEON)
85160static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85161{
85162 drflac_uint64 i;
85163 drflac_uint64 frameCount4 = frameCount >> 2;
85164 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
85165 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
85166 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85167 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85168 int32x4_t shift0_4;
85169 int32x4_t shift1_4;
85170 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
85171 shift0_4 = vdupq_n_s32(shift0);
85172 shift1_4 = vdupq_n_s32(shift1);
85173 for (i = 0; i < frameCount4; ++i) {
85174 uint32x4_t left;
85175 uint32x4_t side;
85176 uint32x4_t right;
85177 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
85178 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
85179 right = vsubq_u32(left, side);
85180 left = vshrq_n_u32(left, 16);
85181 right = vshrq_n_u32(right, 16);
85182 drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right)));
85183 }
85184 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85185 drflac_uint32 left = pInputSamples0U32[i] << shift0;
85186 drflac_uint32 side = pInputSamples1U32[i] << shift1;
85187 drflac_uint32 right = left - side;
85188 left >>= 16;
85189 right >>= 16;
85190 pOutputSamples[i*2+0] = (drflac_int16)left;
85191 pOutputSamples[i*2+1] = (drflac_int16)right;
85192 }
85193}
85194#endif
85195static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85196{
85197#if defined(DRFLAC_SUPPORT_SSE2)
85198 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
85199 drflac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85200 } else
85201#elif defined(DRFLAC_SUPPORT_NEON)
85202 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
85203 drflac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85204 } else
85205#endif
85206 {
85207#if 0
85208 drflac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85209#else
85210 drflac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85211#endif
85212 }
85213}
85214#if 0
85215static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85216{
85217 drflac_uint64 i;
85218 for (i = 0; i < frameCount; ++i) {
85219 drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
85220 drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
85221 drflac_uint32 left = right + side;
85222 left >>= 16;
85223 right >>= 16;
85224 pOutputSamples[i*2+0] = (drflac_int16)left;
85225 pOutputSamples[i*2+1] = (drflac_int16)right;
85226 }
85227}
85228#endif
85229static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85230{
85231 drflac_uint64 i;
85232 drflac_uint64 frameCount4 = frameCount >> 2;
85233 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
85234 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
85235 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85236 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85237 for (i = 0; i < frameCount4; ++i) {
85238 drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
85239 drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
85240 drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
85241 drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
85242 drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
85243 drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
85244 drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
85245 drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
85246 drflac_uint32 left0 = right0 + side0;
85247 drflac_uint32 left1 = right1 + side1;
85248 drflac_uint32 left2 = right2 + side2;
85249 drflac_uint32 left3 = right3 + side3;
85250 left0 >>= 16;
85251 left1 >>= 16;
85252 left2 >>= 16;
85253 left3 >>= 16;
85254 right0 >>= 16;
85255 right1 >>= 16;
85256 right2 >>= 16;
85257 right3 >>= 16;
85258 pOutputSamples[i*8+0] = (drflac_int16)left0;
85259 pOutputSamples[i*8+1] = (drflac_int16)right0;
85260 pOutputSamples[i*8+2] = (drflac_int16)left1;
85261 pOutputSamples[i*8+3] = (drflac_int16)right1;
85262 pOutputSamples[i*8+4] = (drflac_int16)left2;
85263 pOutputSamples[i*8+5] = (drflac_int16)right2;
85264 pOutputSamples[i*8+6] = (drflac_int16)left3;
85265 pOutputSamples[i*8+7] = (drflac_int16)right3;
85266 }
85267 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85268 drflac_uint32 side = pInputSamples0U32[i] << shift0;
85269 drflac_uint32 right = pInputSamples1U32[i] << shift1;
85270 drflac_uint32 left = right + side;
85271 left >>= 16;
85272 right >>= 16;
85273 pOutputSamples[i*2+0] = (drflac_int16)left;
85274 pOutputSamples[i*2+1] = (drflac_int16)right;
85275 }
85276}
85277#if defined(DRFLAC_SUPPORT_SSE2)
85278static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85279{
85280 drflac_uint64 i;
85281 drflac_uint64 frameCount4 = frameCount >> 2;
85282 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
85283 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
85284 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85285 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85286 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
85287 for (i = 0; i < frameCount4; ++i) {
85288 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
85289 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
85290 __m128i left = _mm_add_epi32(right, side);
85291 left = _mm_srai_epi32(left, 16);
85292 right = _mm_srai_epi32(right, 16);
85293 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
85294 }
85295 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85296 drflac_uint32 side = pInputSamples0U32[i] << shift0;
85297 drflac_uint32 right = pInputSamples1U32[i] << shift1;
85298 drflac_uint32 left = right + side;
85299 left >>= 16;
85300 right >>= 16;
85301 pOutputSamples[i*2+0] = (drflac_int16)left;
85302 pOutputSamples[i*2+1] = (drflac_int16)right;
85303 }
85304}
85305#endif
85306#if defined(DRFLAC_SUPPORT_NEON)
85307static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85308{
85309 drflac_uint64 i;
85310 drflac_uint64 frameCount4 = frameCount >> 2;
85311 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
85312 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
85313 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85314 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85315 int32x4_t shift0_4;
85316 int32x4_t shift1_4;
85317 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
85318 shift0_4 = vdupq_n_s32(shift0);
85319 shift1_4 = vdupq_n_s32(shift1);
85320 for (i = 0; i < frameCount4; ++i) {
85321 uint32x4_t side;
85322 uint32x4_t right;
85323 uint32x4_t left;
85324 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
85325 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
85326 left = vaddq_u32(right, side);
85327 left = vshrq_n_u32(left, 16);
85328 right = vshrq_n_u32(right, 16);
85329 drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right)));
85330 }
85331 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85332 drflac_uint32 side = pInputSamples0U32[i] << shift0;
85333 drflac_uint32 right = pInputSamples1U32[i] << shift1;
85334 drflac_uint32 left = right + side;
85335 left >>= 16;
85336 right >>= 16;
85337 pOutputSamples[i*2+0] = (drflac_int16)left;
85338 pOutputSamples[i*2+1] = (drflac_int16)right;
85339 }
85340}
85341#endif
85342static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85343{
85344#if defined(DRFLAC_SUPPORT_SSE2)
85345 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
85346 drflac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85347 } else
85348#elif defined(DRFLAC_SUPPORT_NEON)
85349 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
85350 drflac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85351 } else
85352#endif
85353 {
85354#if 0
85355 drflac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85356#else
85357 drflac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85358#endif
85359 }
85360}
85361#if 0
85362static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85363{
85364 for (drflac_uint64 i = 0; i < frameCount; ++i) {
85365 drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85366 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85367 mid = (mid << 1) | (side & 0x01);
85368 pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16);
85369 pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16);
85370 }
85371}
85372#endif
85373static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85374{
85375 drflac_uint64 i;
85376 drflac_uint64 frameCount4 = frameCount >> 2;
85377 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
85378 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
85379 drflac_uint32 shift = unusedBitsPerSample;
85380 if (shift > 0) {
85381 shift -= 1;
85382 for (i = 0; i < frameCount4; ++i) {
85383 drflac_uint32 temp0L;
85384 drflac_uint32 temp1L;
85385 drflac_uint32 temp2L;
85386 drflac_uint32 temp3L;
85387 drflac_uint32 temp0R;
85388 drflac_uint32 temp1R;
85389 drflac_uint32 temp2R;
85390 drflac_uint32 temp3R;
85391 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85392 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85393 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85394 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85395 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85396 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85397 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85398 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85399 mid0 = (mid0 << 1) | (side0 & 0x01);
85400 mid1 = (mid1 << 1) | (side1 & 0x01);
85401 mid2 = (mid2 << 1) | (side2 & 0x01);
85402 mid3 = (mid3 << 1) | (side3 & 0x01);
85403 temp0L = (mid0 + side0) << shift;
85404 temp1L = (mid1 + side1) << shift;
85405 temp2L = (mid2 + side2) << shift;
85406 temp3L = (mid3 + side3) << shift;
85407 temp0R = (mid0 - side0) << shift;
85408 temp1R = (mid1 - side1) << shift;
85409 temp2R = (mid2 - side2) << shift;
85410 temp3R = (mid3 - side3) << shift;
85411 temp0L >>= 16;
85412 temp1L >>= 16;
85413 temp2L >>= 16;
85414 temp3L >>= 16;
85415 temp0R >>= 16;
85416 temp1R >>= 16;
85417 temp2R >>= 16;
85418 temp3R >>= 16;
85419 pOutputSamples[i*8+0] = (drflac_int16)temp0L;
85420 pOutputSamples[i*8+1] = (drflac_int16)temp0R;
85421 pOutputSamples[i*8+2] = (drflac_int16)temp1L;
85422 pOutputSamples[i*8+3] = (drflac_int16)temp1R;
85423 pOutputSamples[i*8+4] = (drflac_int16)temp2L;
85424 pOutputSamples[i*8+5] = (drflac_int16)temp2R;
85425 pOutputSamples[i*8+6] = (drflac_int16)temp3L;
85426 pOutputSamples[i*8+7] = (drflac_int16)temp3R;
85427 }
85428 } else {
85429 for (i = 0; i < frameCount4; ++i) {
85430 drflac_uint32 temp0L;
85431 drflac_uint32 temp1L;
85432 drflac_uint32 temp2L;
85433 drflac_uint32 temp3L;
85434 drflac_uint32 temp0R;
85435 drflac_uint32 temp1R;
85436 drflac_uint32 temp2R;
85437 drflac_uint32 temp3R;
85438 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85439 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85440 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85441 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85442 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85443 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85444 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85445 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85446 mid0 = (mid0 << 1) | (side0 & 0x01);
85447 mid1 = (mid1 << 1) | (side1 & 0x01);
85448 mid2 = (mid2 << 1) | (side2 & 0x01);
85449 mid3 = (mid3 << 1) | (side3 & 0x01);
85450 temp0L = ((drflac_int32)(mid0 + side0) >> 1);
85451 temp1L = ((drflac_int32)(mid1 + side1) >> 1);
85452 temp2L = ((drflac_int32)(mid2 + side2) >> 1);
85453 temp3L = ((drflac_int32)(mid3 + side3) >> 1);
85454 temp0R = ((drflac_int32)(mid0 - side0) >> 1);
85455 temp1R = ((drflac_int32)(mid1 - side1) >> 1);
85456 temp2R = ((drflac_int32)(mid2 - side2) >> 1);
85457 temp3R = ((drflac_int32)(mid3 - side3) >> 1);
85458 temp0L >>= 16;
85459 temp1L >>= 16;
85460 temp2L >>= 16;
85461 temp3L >>= 16;
85462 temp0R >>= 16;
85463 temp1R >>= 16;
85464 temp2R >>= 16;
85465 temp3R >>= 16;
85466 pOutputSamples[i*8+0] = (drflac_int16)temp0L;
85467 pOutputSamples[i*8+1] = (drflac_int16)temp0R;
85468 pOutputSamples[i*8+2] = (drflac_int16)temp1L;
85469 pOutputSamples[i*8+3] = (drflac_int16)temp1R;
85470 pOutputSamples[i*8+4] = (drflac_int16)temp2L;
85471 pOutputSamples[i*8+5] = (drflac_int16)temp2R;
85472 pOutputSamples[i*8+6] = (drflac_int16)temp3L;
85473 pOutputSamples[i*8+7] = (drflac_int16)temp3R;
85474 }
85475 }
85476 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85477 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85478 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85479 mid = (mid << 1) | (side & 0x01);
85480 pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16);
85481 pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16);
85482 }
85483}
85484#if defined(DRFLAC_SUPPORT_SSE2)
85485static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85486{
85487 drflac_uint64 i;
85488 drflac_uint64 frameCount4 = frameCount >> 2;
85489 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
85490 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
85491 drflac_uint32 shift = unusedBitsPerSample;
85492 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
85493 if (shift == 0) {
85494 for (i = 0; i < frameCount4; ++i) {
85495 __m128i mid;
85496 __m128i side;
85497 __m128i left;
85498 __m128i right;
85499 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
85500 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
85501 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
85502 left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
85503 right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
85504 left = _mm_srai_epi32(left, 16);
85505 right = _mm_srai_epi32(right, 16);
85506 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
85507 }
85508 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85509 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85510 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85511 mid = (mid << 1) | (side & 0x01);
85512 pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16);
85513 pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16);
85514 }
85515 } else {
85516 shift -= 1;
85517 for (i = 0; i < frameCount4; ++i) {
85518 __m128i mid;
85519 __m128i side;
85520 __m128i left;
85521 __m128i right;
85522 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
85523 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
85524 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
85525 left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
85526 right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
85527 left = _mm_srai_epi32(left, 16);
85528 right = _mm_srai_epi32(right, 16);
85529 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
85530 }
85531 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85532 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85533 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85534 mid = (mid << 1) | (side & 0x01);
85535 pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16);
85536 pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16);
85537 }
85538 }
85539}
85540#endif
85541#if defined(DRFLAC_SUPPORT_NEON)
85542static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85543{
85544 drflac_uint64 i;
85545 drflac_uint64 frameCount4 = frameCount >> 2;
85546 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
85547 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
85548 drflac_uint32 shift = unusedBitsPerSample;
85549 int32x4_t wbpsShift0_4;
85550 int32x4_t wbpsShift1_4;
85551 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
85552 wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
85553 wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
85554 if (shift == 0) {
85555 for (i = 0; i < frameCount4; ++i) {
85556 uint32x4_t mid;
85557 uint32x4_t side;
85558 int32x4_t left;
85559 int32x4_t right;
85560 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
85561 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
85562 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
85563 left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
85564 right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
85565 left = vshrq_n_s32(left, 16);
85566 right = vshrq_n_s32(right, 16);
85567 drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
85568 }
85569 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85570 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85571 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85572 mid = (mid << 1) | (side & 0x01);
85573 pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16);
85574 pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16);
85575 }
85576 } else {
85577 int32x4_t shift4;
85578 shift -= 1;
85579 shift4 = vdupq_n_s32(shift);
85580 for (i = 0; i < frameCount4; ++i) {
85581 uint32x4_t mid;
85582 uint32x4_t side;
85583 int32x4_t left;
85584 int32x4_t right;
85585 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
85586 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
85587 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
85588 left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
85589 right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
85590 left = vshrq_n_s32(left, 16);
85591 right = vshrq_n_s32(right, 16);
85592 drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
85593 }
85594 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85595 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85596 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85597 mid = (mid << 1) | (side & 0x01);
85598 pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16);
85599 pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16);
85600 }
85601 }
85602}
85603#endif
85604static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85605{
85606#if defined(DRFLAC_SUPPORT_SSE2)
85607 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
85608 drflac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85609 } else
85610#elif defined(DRFLAC_SUPPORT_NEON)
85611 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
85612 drflac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85613 } else
85614#endif
85615 {
85616#if 0
85617 drflac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85618#else
85619 drflac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85620#endif
85621 }
85622}
85623#if 0
85624static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85625{
85626 for (drflac_uint64 i = 0; i < frameCount; ++i) {
85627 pOutputSamples[i*2+0] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16);
85628 pOutputSamples[i*2+1] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16);
85629 }
85630}
85631#endif
85632static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85633{
85634 drflac_uint64 i;
85635 drflac_uint64 frameCount4 = frameCount >> 2;
85636 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
85637 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
85638 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85639 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85640 for (i = 0; i < frameCount4; ++i) {
85641 drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
85642 drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
85643 drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
85644 drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
85645 drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
85646 drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
85647 drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
85648 drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
85649 tempL0 >>= 16;
85650 tempL1 >>= 16;
85651 tempL2 >>= 16;
85652 tempL3 >>= 16;
85653 tempR0 >>= 16;
85654 tempR1 >>= 16;
85655 tempR2 >>= 16;
85656 tempR3 >>= 16;
85657 pOutputSamples[i*8+0] = (drflac_int16)tempL0;
85658 pOutputSamples[i*8+1] = (drflac_int16)tempR0;
85659 pOutputSamples[i*8+2] = (drflac_int16)tempL1;
85660 pOutputSamples[i*8+3] = (drflac_int16)tempR1;
85661 pOutputSamples[i*8+4] = (drflac_int16)tempL2;
85662 pOutputSamples[i*8+5] = (drflac_int16)tempR2;
85663 pOutputSamples[i*8+6] = (drflac_int16)tempL3;
85664 pOutputSamples[i*8+7] = (drflac_int16)tempR3;
85665 }
85666 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85667 pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16);
85668 pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16);
85669 }
85670}
85671#if defined(DRFLAC_SUPPORT_SSE2)
85672static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85673{
85674 drflac_uint64 i;
85675 drflac_uint64 frameCount4 = frameCount >> 2;
85676 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
85677 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
85678 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85679 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85680 for (i = 0; i < frameCount4; ++i) {
85681 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
85682 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
85683 left = _mm_srai_epi32(left, 16);
85684 right = _mm_srai_epi32(right, 16);
85685 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
85686 }
85687 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85688 pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16);
85689 pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16);
85690 }
85691}
85692#endif
85693#if defined(DRFLAC_SUPPORT_NEON)
85694static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85695{
85696 drflac_uint64 i;
85697 drflac_uint64 frameCount4 = frameCount >> 2;
85698 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
85699 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
85700 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85701 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85702 int32x4_t shift0_4 = vdupq_n_s32(shift0);
85703 int32x4_t shift1_4 = vdupq_n_s32(shift1);
85704 for (i = 0; i < frameCount4; ++i) {
85705 int32x4_t left;
85706 int32x4_t right;
85707 left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));
85708 right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));
85709 left = vshrq_n_s32(left, 16);
85710 right = vshrq_n_s32(right, 16);
85711 drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
85712 }
85713 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85714 pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16);
85715 pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16);
85716 }
85717}
85718#endif
85719static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
85720{
85721#if defined(DRFLAC_SUPPORT_SSE2)
85722 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
85723 drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85724 } else
85725#elif defined(DRFLAC_SUPPORT_NEON)
85726 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
85727 drflac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85728 } else
85729#endif
85730 {
85731#if 0
85732 drflac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85733#else
85734 drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85735#endif
85736 }
85737}
85739{
85740 drflac_uint64 framesRead;
85741 drflac_uint32 unusedBitsPerSample;
85742 if (pFlac == NULL || framesToRead == 0) {
85743 return 0;
85744 }
85745 if (pBufferOut == NULL) {
85746 return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
85747 }
85748 DRFLAC_ASSERT(pFlac->bitsPerSample <= 32);
85749 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
85750 framesRead = 0;
85751 while (framesToRead > 0) {
85752 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
85753 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
85754 break;
85755 }
85756 } else {
85757 unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
85758 drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
85759 drflac_uint64 frameCountThisIteration = framesToRead;
85760 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
85761 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
85762 }
85763 if (channelCount == 2) {
85764 const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
85765 const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
85767 {
85768 case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
85769 {
85770 drflac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
85771 } break;
85772 case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
85773 {
85774 drflac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
85775 } break;
85776 case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
85777 {
85778 drflac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
85779 } break;
85780 case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
85781 default:
85782 {
85783 drflac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
85784 } break;
85785 }
85786 } else {
85787 drflac_uint64 i;
85788 for (i = 0; i < frameCountThisIteration; ++i) {
85789 unsigned int j;
85790 for (j = 0; j < channelCount; ++j) {
85791 drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
85792 pBufferOut[(i*channelCount)+j] = (drflac_int16)(sampleS32 >> 16);
85793 }
85794 }
85795 }
85796 framesRead += frameCountThisIteration;
85797 pBufferOut += frameCountThisIteration * channelCount;
85798 framesToRead -= frameCountThisIteration;
85799 pFlac->currentPCMFrame += frameCountThisIteration;
85800 pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration;
85801 }
85802 }
85803 return framesRead;
85804}
85805#if 0
85806static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
85807{
85808 drflac_uint64 i;
85809 for (i = 0; i < frameCount; ++i) {
85810 drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
85811 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
85812 drflac_uint32 right = left - side;
85813 pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0);
85814 pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0);
85815 }
85816}
85817#endif
85818static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
85819{
85820 drflac_uint64 i;
85821 drflac_uint64 frameCount4 = frameCount >> 2;
85822 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
85823 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
85824 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85825 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85826 float factor = 1 / 2147483648.0;
85827 for (i = 0; i < frameCount4; ++i) {
85828 drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
85829 drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
85830 drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
85831 drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
85832 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
85833 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
85834 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
85835 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
85836 drflac_uint32 right0 = left0 - side0;
85837 drflac_uint32 right1 = left1 - side1;
85838 drflac_uint32 right2 = left2 - side2;
85839 drflac_uint32 right3 = left3 - side3;
85840 pOutputSamples[i*8+0] = (drflac_int32)left0 * factor;
85841 pOutputSamples[i*8+1] = (drflac_int32)right0 * factor;
85842 pOutputSamples[i*8+2] = (drflac_int32)left1 * factor;
85843 pOutputSamples[i*8+3] = (drflac_int32)right1 * factor;
85844 pOutputSamples[i*8+4] = (drflac_int32)left2 * factor;
85845 pOutputSamples[i*8+5] = (drflac_int32)right2 * factor;
85846 pOutputSamples[i*8+6] = (drflac_int32)left3 * factor;
85847 pOutputSamples[i*8+7] = (drflac_int32)right3 * factor;
85848 }
85849 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85850 drflac_uint32 left = pInputSamples0U32[i] << shift0;
85851 drflac_uint32 side = pInputSamples1U32[i] << shift1;
85852 drflac_uint32 right = left - side;
85853 pOutputSamples[i*2+0] = (drflac_int32)left * factor;
85854 pOutputSamples[i*2+1] = (drflac_int32)right * factor;
85855 }
85856}
85857#if defined(DRFLAC_SUPPORT_SSE2)
85858static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
85859{
85860 drflac_uint64 i;
85861 drflac_uint64 frameCount4 = frameCount >> 2;
85862 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
85863 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
85864 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
85865 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
85866 __m128 factor;
85867 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
85868 factor = _mm_set1_ps(1.0f / 8388608.0f);
85869 for (i = 0; i < frameCount4; ++i) {
85870 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
85871 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
85872 __m128i right = _mm_sub_epi32(left, side);
85873 __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
85874 __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
85875 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
85876 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
85877 }
85878 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85879 drflac_uint32 left = pInputSamples0U32[i] << shift0;
85880 drflac_uint32 side = pInputSamples1U32[i] << shift1;
85881 drflac_uint32 right = left - side;
85882 pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f;
85883 pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f;
85884 }
85885}
85886#endif
85887#if defined(DRFLAC_SUPPORT_NEON)
85888static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
85889{
85890 drflac_uint64 i;
85891 drflac_uint64 frameCount4 = frameCount >> 2;
85892 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
85893 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
85894 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
85895 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
85896 float32x4_t factor4;
85897 int32x4_t shift0_4;
85898 int32x4_t shift1_4;
85899 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
85900 factor4 = vdupq_n_f32(1.0f / 8388608.0f);
85901 shift0_4 = vdupq_n_s32(shift0);
85902 shift1_4 = vdupq_n_s32(shift1);
85903 for (i = 0; i < frameCount4; ++i) {
85904 uint32x4_t left;
85905 uint32x4_t side;
85906 uint32x4_t right;
85907 float32x4_t leftf;
85908 float32x4_t rightf;
85909 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
85910 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
85911 right = vsubq_u32(left, side);
85912 leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4);
85913 rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4);
85914 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
85915 }
85916 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85917 drflac_uint32 left = pInputSamples0U32[i] << shift0;
85918 drflac_uint32 side = pInputSamples1U32[i] << shift1;
85919 drflac_uint32 right = left - side;
85920 pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f;
85921 pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f;
85922 }
85923}
85924#endif
85925static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
85926{
85927#if defined(DRFLAC_SUPPORT_SSE2)
85928 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
85929 drflac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85930 } else
85931#elif defined(DRFLAC_SUPPORT_NEON)
85932 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
85933 drflac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85934 } else
85935#endif
85936 {
85937#if 0
85938 drflac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85939#else
85940 drflac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
85941#endif
85942 }
85943}
85944#if 0
85945static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
85946{
85947 drflac_uint64 i;
85948 for (i = 0; i < frameCount; ++i) {
85949 drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
85950 drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
85951 drflac_uint32 left = right + side;
85952 pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0);
85953 pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0);
85954 }
85955}
85956#endif
85957static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
85958{
85959 drflac_uint64 i;
85960 drflac_uint64 frameCount4 = frameCount >> 2;
85961 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
85962 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
85963 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
85964 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
85965 float factor = 1 / 2147483648.0;
85966 for (i = 0; i < frameCount4; ++i) {
85967 drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
85968 drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
85969 drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
85970 drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
85971 drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
85972 drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
85973 drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
85974 drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
85975 drflac_uint32 left0 = right0 + side0;
85976 drflac_uint32 left1 = right1 + side1;
85977 drflac_uint32 left2 = right2 + side2;
85978 drflac_uint32 left3 = right3 + side3;
85979 pOutputSamples[i*8+0] = (drflac_int32)left0 * factor;
85980 pOutputSamples[i*8+1] = (drflac_int32)right0 * factor;
85981 pOutputSamples[i*8+2] = (drflac_int32)left1 * factor;
85982 pOutputSamples[i*8+3] = (drflac_int32)right1 * factor;
85983 pOutputSamples[i*8+4] = (drflac_int32)left2 * factor;
85984 pOutputSamples[i*8+5] = (drflac_int32)right2 * factor;
85985 pOutputSamples[i*8+6] = (drflac_int32)left3 * factor;
85986 pOutputSamples[i*8+7] = (drflac_int32)right3 * factor;
85987 }
85988 for (i = (frameCount4 << 2); i < frameCount; ++i) {
85989 drflac_uint32 side = pInputSamples0U32[i] << shift0;
85990 drflac_uint32 right = pInputSamples1U32[i] << shift1;
85991 drflac_uint32 left = right + side;
85992 pOutputSamples[i*2+0] = (drflac_int32)left * factor;
85993 pOutputSamples[i*2+1] = (drflac_int32)right * factor;
85994 }
85995}
85996#if defined(DRFLAC_SUPPORT_SSE2)
85997static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
85998{
85999 drflac_uint64 i;
86000 drflac_uint64 frameCount4 = frameCount >> 2;
86001 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
86002 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
86003 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
86004 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
86005 __m128 factor;
86006 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
86007 factor = _mm_set1_ps(1.0f / 8388608.0f);
86008 for (i = 0; i < frameCount4; ++i) {
86009 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
86010 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
86011 __m128i left = _mm_add_epi32(right, side);
86012 __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
86013 __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
86014 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
86015 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
86016 }
86017 for (i = (frameCount4 << 2); i < frameCount; ++i) {
86018 drflac_uint32 side = pInputSamples0U32[i] << shift0;
86019 drflac_uint32 right = pInputSamples1U32[i] << shift1;
86020 drflac_uint32 left = right + side;
86021 pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f;
86022 pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f;
86023 }
86024}
86025#endif
86026#if defined(DRFLAC_SUPPORT_NEON)
86027static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
86028{
86029 drflac_uint64 i;
86030 drflac_uint64 frameCount4 = frameCount >> 2;
86031 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
86032 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
86033 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
86034 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
86035 float32x4_t factor4;
86036 int32x4_t shift0_4;
86037 int32x4_t shift1_4;
86038 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
86039 factor4 = vdupq_n_f32(1.0f / 8388608.0f);
86040 shift0_4 = vdupq_n_s32(shift0);
86041 shift1_4 = vdupq_n_s32(shift1);
86042 for (i = 0; i < frameCount4; ++i) {
86043 uint32x4_t side;
86044 uint32x4_t right;
86045 uint32x4_t left;
86046 float32x4_t leftf;
86047 float32x4_t rightf;
86048 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
86049 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
86050 left = vaddq_u32(right, side);
86051 leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4);
86052 rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4);
86053 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
86054 }
86055 for (i = (frameCount4 << 2); i < frameCount; ++i) {
86056 drflac_uint32 side = pInputSamples0U32[i] << shift0;
86057 drflac_uint32 right = pInputSamples1U32[i] << shift1;
86058 drflac_uint32 left = right + side;
86059 pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f;
86060 pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f;
86061 }
86062}
86063#endif
86064static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
86065{
86066#if defined(DRFLAC_SUPPORT_SSE2)
86067 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
86068 drflac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
86069 } else
86070#elif defined(DRFLAC_SUPPORT_NEON)
86071 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
86072 drflac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
86073 } else
86074#endif
86075 {
86076#if 0
86077 drflac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
86078#else
86079 drflac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
86080#endif
86081 }
86082}
86083#if 0
86084static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
86085{
86086 for (drflac_uint64 i = 0; i < frameCount; ++i) {
86087 drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
86088 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
86089 mid = (mid << 1) | (side & 0x01);
86090 pOutputSamples[i*2+0] = (float)((((drflac_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
86091 pOutputSamples[i*2+1] = (float)((((drflac_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
86092 }
86093}
86094#endif
86095static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
86096{
86097 drflac_uint64 i;
86098 drflac_uint64 frameCount4 = frameCount >> 2;
86099 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
86100 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
86101 drflac_uint32 shift = unusedBitsPerSample;
86102 float factor = 1 / 2147483648.0;
86103 if (shift > 0) {
86104 shift -= 1;
86105 for (i = 0; i < frameCount4; ++i) {
86106 drflac_uint32 temp0L;
86107 drflac_uint32 temp1L;
86108 drflac_uint32 temp2L;
86109 drflac_uint32 temp3L;
86110 drflac_uint32 temp0R;
86111 drflac_uint32 temp1R;
86112 drflac_uint32 temp2R;
86113 drflac_uint32 temp3R;
86114 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
86115 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
86116 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
86117 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
86118 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
86119 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
86120 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
86121 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
86122 mid0 = (mid0 << 1) | (side0 & 0x01);
86123 mid1 = (mid1 << 1) | (side1 & 0x01);
86124 mid2 = (mid2 << 1) | (side2 & 0x01);
86125 mid3 = (mid3 << 1) | (side3 & 0x01);
86126 temp0L = (mid0 + side0) << shift;
86127 temp1L = (mid1 + side1) << shift;
86128 temp2L = (mid2 + side2) << shift;
86129 temp3L = (mid3 + side3) << shift;
86130 temp0R = (mid0 - side0) << shift;
86131 temp1R = (mid1 - side1) << shift;
86132 temp2R = (mid2 - side2) << shift;
86133 temp3R = (mid3 - side3) << shift;
86134 pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor;
86135 pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor;
86136 pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor;
86137 pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor;
86138 pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor;
86139 pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor;
86140 pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor;
86141 pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor;
86142 }
86143 } else {
86144 for (i = 0; i < frameCount4; ++i) {
86145 drflac_uint32 temp0L;
86146 drflac_uint32 temp1L;
86147 drflac_uint32 temp2L;
86148 drflac_uint32 temp3L;
86149 drflac_uint32 temp0R;
86150 drflac_uint32 temp1R;
86151 drflac_uint32 temp2R;
86152 drflac_uint32 temp3R;
86153 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
86154 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
86155 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
86156 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
86157 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
86158 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
86159 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
86160 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
86161 mid0 = (mid0 << 1) | (side0 & 0x01);
86162 mid1 = (mid1 << 1) | (side1 & 0x01);
86163 mid2 = (mid2 << 1) | (side2 & 0x01);
86164 mid3 = (mid3 << 1) | (side3 & 0x01);
86165 temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1);
86166 temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1);
86167 temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1);
86168 temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1);
86169 temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1);
86170 temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1);
86171 temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1);
86172 temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1);
86173 pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor;
86174 pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor;
86175 pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor;
86176 pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor;
86177 pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor;
86178 pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor;
86179 pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor;
86180 pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor;
86181 }
86182 }
86183 for (i = (frameCount4 << 2); i < frameCount; ++i) {
86184 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
86185 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
86186 mid = (mid << 1) | (side & 0x01);
86187 pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor;
86188 pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor;
86189 }
86190}
86191#if defined(DRFLAC_SUPPORT_SSE2)
86192static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
86193{
86194 drflac_uint64 i;
86195 drflac_uint64 frameCount4 = frameCount >> 2;
86196 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
86197 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
86198 drflac_uint32 shift = unusedBitsPerSample - 8;
86199 float factor;
86200 __m128 factor128;
86201 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
86202 factor = 1.0f / 8388608.0f;
86203 factor128 = _mm_set1_ps(factor);
86204 if (shift == 0) {
86205 for (i = 0; i < frameCount4; ++i) {
86206 __m128i mid;
86207 __m128i side;
86208 __m128i tempL;
86209 __m128i tempR;
86210 __m128 leftf;
86211 __m128 rightf;
86212 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
86213 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
86214 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
86215 tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
86216 tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
86217 leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
86218 rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
86219 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
86220 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
86221 }
86222 for (i = (frameCount4 << 2); i < frameCount; ++i) {
86223 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
86224 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
86225 mid = (mid << 1) | (side & 0x01);
86226 pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor;
86227 pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor;
86228 }
86229 } else {
86230 shift -= 1;
86231 for (i = 0; i < frameCount4; ++i) {
86232 __m128i mid;
86233 __m128i side;
86234 __m128i tempL;
86235 __m128i tempR;
86236 __m128 leftf;
86237 __m128 rightf;
86238 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
86239 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
86240 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
86241 tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
86242 tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
86243 leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
86244 rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
86245 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
86246 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
86247 }
86248 for (i = (frameCount4 << 2); i < frameCount; ++i) {
86249 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
86250 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
86251 mid = (mid << 1) | (side & 0x01);
86252 pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor;
86253 pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor;
86254 }
86255 }
86256}
86257#endif
86258#if defined(DRFLAC_SUPPORT_NEON)
86259static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
86260{
86261 drflac_uint64 i;
86262 drflac_uint64 frameCount4 = frameCount >> 2;
86263 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
86264 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
86265 drflac_uint32 shift = unusedBitsPerSample - 8;
86266 float factor;
86267 float32x4_t factor4;
86268 int32x4_t shift4;
86269 int32x4_t wbps0_4;
86270 int32x4_t wbps1_4;
86271 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
86272 factor = 1.0f / 8388608.0f;
86273 factor4 = vdupq_n_f32(factor);
86274 wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
86275 wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
86276 if (shift == 0) {
86277 for (i = 0; i < frameCount4; ++i) {
86278 int32x4_t lefti;
86279 int32x4_t righti;
86280 float32x4_t leftf;
86281 float32x4_t rightf;
86282 uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4);
86283 uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4);
86284 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
86285 lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
86286 righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
86287 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
86288 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
86289 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
86290 }
86291 for (i = (frameCount4 << 2); i < frameCount; ++i) {
86292 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
86293 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
86294 mid = (mid << 1) | (side & 0x01);
86295 pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor;
86296 pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor;
86297 }
86298 } else {
86299 shift -= 1;
86300 shift4 = vdupq_n_s32(shift);
86301 for (i = 0; i < frameCount4; ++i) {
86302 uint32x4_t mid;
86303 uint32x4_t side;
86304 int32x4_t lefti;
86305 int32x4_t righti;
86306 float32x4_t leftf;
86307 float32x4_t rightf;
86308 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4);
86309 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4);
86310 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
86311 lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
86312 righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
86313 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
86314 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
86315 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
86316 }
86317 for (i = (frameCount4 << 2); i < frameCount; ++i) {
86318 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
86319 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
86320 mid = (mid << 1) | (side & 0x01);
86321 pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor;
86322 pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor;
86323 }
86324 }
86325}
86326#endif
86327static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
86328{
86329#if defined(DRFLAC_SUPPORT_SSE2)
86330 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
86331 drflac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
86332 } else
86333#elif defined(DRFLAC_SUPPORT_NEON)
86334 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
86335 drflac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
86336 } else
86337#endif
86338 {
86339#if 0
86340 drflac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
86341#else
86342 drflac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
86343#endif
86344 }
86345}
86346#if 0
86347static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
86348{
86349 for (drflac_uint64 i = 0; i < frameCount; ++i) {
86350 pOutputSamples[i*2+0] = (float)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0);
86351 pOutputSamples[i*2+1] = (float)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0);
86352 }
86353}
86354#endif
86355static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
86356{
86357 drflac_uint64 i;
86358 drflac_uint64 frameCount4 = frameCount >> 2;
86359 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
86360 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
86361 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
86362 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
86363 float factor = 1 / 2147483648.0;
86364 for (i = 0; i < frameCount4; ++i) {
86365 drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
86366 drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
86367 drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
86368 drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
86369 drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
86370 drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
86371 drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
86372 drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
86373 pOutputSamples[i*8+0] = (drflac_int32)tempL0 * factor;
86374 pOutputSamples[i*8+1] = (drflac_int32)tempR0 * factor;
86375 pOutputSamples[i*8+2] = (drflac_int32)tempL1 * factor;
86376 pOutputSamples[i*8+3] = (drflac_int32)tempR1 * factor;
86377 pOutputSamples[i*8+4] = (drflac_int32)tempL2 * factor;
86378 pOutputSamples[i*8+5] = (drflac_int32)tempR2 * factor;
86379 pOutputSamples[i*8+6] = (drflac_int32)tempL3 * factor;
86380 pOutputSamples[i*8+7] = (drflac_int32)tempR3 * factor;
86381 }
86382 for (i = (frameCount4 << 2); i < frameCount; ++i) {
86383 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor;
86384 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor;
86385 }
86386}
86387#if defined(DRFLAC_SUPPORT_SSE2)
86388static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
86389{
86390 drflac_uint64 i;
86391 drflac_uint64 frameCount4 = frameCount >> 2;
86392 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
86393 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
86394 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
86395 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
86396 float factor = 1.0f / 8388608.0f;
86397 __m128 factor128 = _mm_set1_ps(factor);
86398 for (i = 0; i < frameCount4; ++i) {
86399 __m128i lefti;
86400 __m128i righti;
86401 __m128 leftf;
86402 __m128 rightf;
86403 lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
86404 righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
86405 leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128);
86406 rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128);
86407 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
86408 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
86409 }
86410 for (i = (frameCount4 << 2); i < frameCount; ++i) {
86411 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor;
86412 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor;
86413 }
86414}
86415#endif
86416#if defined(DRFLAC_SUPPORT_NEON)
86417static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
86418{
86419 drflac_uint64 i;
86420 drflac_uint64 frameCount4 = frameCount >> 2;
86421 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
86422 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
86423 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
86424 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
86425 float factor = 1.0f / 8388608.0f;
86426 float32x4_t factor4 = vdupq_n_f32(factor);
86427 int32x4_t shift0_4 = vdupq_n_s32(shift0);
86428 int32x4_t shift1_4 = vdupq_n_s32(shift1);
86429 for (i = 0; i < frameCount4; ++i) {
86430 int32x4_t lefti;
86431 int32x4_t righti;
86432 float32x4_t leftf;
86433 float32x4_t rightf;
86434 lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));
86435 righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));
86436 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
86437 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
86438 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
86439 }
86440 for (i = (frameCount4 << 2); i < frameCount; ++i) {
86441 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor;
86442 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor;
86443 }
86444}
86445#endif
86446static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
86447{
86448#if defined(DRFLAC_SUPPORT_SSE2)
86449 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
86450 drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
86451 } else
86452#elif defined(DRFLAC_SUPPORT_NEON)
86453 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
86454 drflac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
86455 } else
86456#endif
86457 {
86458#if 0
86459 drflac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
86460#else
86461 drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
86462#endif
86463 }
86464}
86465DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut)
86466{
86467 drflac_uint64 framesRead;
86468 drflac_uint32 unusedBitsPerSample;
86469 if (pFlac == NULL || framesToRead == 0) {
86470 return 0;
86471 }
86472 if (pBufferOut == NULL) {
86473 return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
86474 }
86475 DRFLAC_ASSERT(pFlac->bitsPerSample <= 32);
86476 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
86477 framesRead = 0;
86478 while (framesToRead > 0) {
86479 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
86480 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
86481 break;
86482 }
86483 } else {
86484 unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
86485 drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
86486 drflac_uint64 frameCountThisIteration = framesToRead;
86487 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
86488 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
86489 }
86490 if (channelCount == 2) {
86491 const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
86492 const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
86494 {
86495 case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
86496 {
86497 drflac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
86498 } break;
86499 case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
86500 {
86501 drflac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
86502 } break;
86503 case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
86504 {
86505 drflac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
86506 } break;
86507 case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
86508 default:
86509 {
86510 drflac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
86511 } break;
86512 }
86513 } else {
86514 drflac_uint64 i;
86515 for (i = 0; i < frameCountThisIteration; ++i) {
86516 unsigned int j;
86517 for (j = 0; j < channelCount; ++j) {
86518 drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
86519 pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0);
86520 }
86521 }
86522 }
86523 framesRead += frameCountThisIteration;
86524 pBufferOut += frameCountThisIteration * channelCount;
86525 framesToRead -= frameCountThisIteration;
86526 pFlac->currentPCMFrame += frameCountThisIteration;
86527 pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration;
86528 }
86529 }
86530 return framesRead;
86531}
86533{
86534 if (pFlac == NULL) {
86535 return DRFLAC_FALSE;
86536 }
86537 if (pFlac->currentPCMFrame == pcmFrameIndex) {
86538 return DRFLAC_TRUE;
86539 }
86540 if (pFlac->firstFLACFramePosInBytes == 0) {
86541 return DRFLAC_FALSE;
86542 }
86543 if (pcmFrameIndex == 0) {
86544 pFlac->currentPCMFrame = 0;
86545 return drflac__seek_to_first_frame(pFlac);
86546 } else {
86547 drflac_bool32 wasSuccessful = DRFLAC_FALSE;
86548 drflac_uint64 originalPCMFrame = pFlac->currentPCMFrame;
86549 if (pcmFrameIndex > pFlac->totalPCMFrameCount) {
86550 pcmFrameIndex = pFlac->totalPCMFrameCount;
86551 }
86552 if (pcmFrameIndex > pFlac->currentPCMFrame) {
86553 drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex - pFlac->currentPCMFrame);
86554 if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) {
86555 pFlac->currentFLACFrame.pcmFramesRemaining -= offset;
86556 pFlac->currentPCMFrame = pcmFrameIndex;
86557 return DRFLAC_TRUE;
86558 }
86559 } else {
86560 drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentPCMFrame - pcmFrameIndex);
86561 drflac_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
86562 drflac_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining;
86563 if (currentFLACFramePCMFramesConsumed > offsetAbs) {
86564 pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs;
86565 pFlac->currentPCMFrame = pcmFrameIndex;
86566 return DRFLAC_TRUE;
86567 }
86568 }
86569#ifndef DR_FLAC_NO_OGG
86570 if (pFlac->container == drflac_container_ogg)
86571 {
86572 wasSuccessful = drflac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex);
86573 }
86574 else
86575#endif
86576 {
86577 if (!pFlac->_noSeekTableSeek) {
86578 wasSuccessful = drflac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex);
86579 }
86580#if !defined(DR_FLAC_NO_CRC)
86581 if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) {
86582 wasSuccessful = drflac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex);
86583 }
86584#endif
86585 if (!wasSuccessful && !pFlac->_noBruteForceSeek) {
86586 wasSuccessful = drflac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex);
86587 }
86588 }
86589 if (wasSuccessful) {
86590 pFlac->currentPCMFrame = pcmFrameIndex;
86591 } else {
86592 if (drflac_seek_to_pcm_frame(pFlac, originalPCMFrame) == DRFLAC_FALSE) {
86593 drflac_seek_to_pcm_frame(pFlac, 0);
86594 }
86595 }
86596 return wasSuccessful;
86597 }
86598}
86599#if defined(SIZE_MAX)
86600 #define DRFLAC_SIZE_MAX SIZE_MAX
86601#else
86602 #if defined(DRFLAC_64BIT)
86603 #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF)
86604 #else
86605 #define DRFLAC_SIZE_MAX 0xFFFFFFFF
86606 #endif
86607#endif
86608#define DRFLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \
86609static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)\
86610{ \
86611 type* pSampleData = NULL; \
86612 drflac_uint64 totalPCMFrameCount; \
86613 \
86614 DRFLAC_ASSERT(pFlac != NULL); \
86615 \
86616 totalPCMFrameCount = pFlac->totalPCMFrameCount; \
86617 \
86618 if (totalPCMFrameCount == 0) { \
86619 type buffer[4096]; \
86620 drflac_uint64 pcmFramesRead; \
86621 size_t sampleDataBufferSize = sizeof(buffer); \
86622 \
86623 pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \
86624 if (pSampleData == NULL) { \
86625 goto on_error; \
86626 } \
86627 \
86628 while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \
86629 if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \
86630 type* pNewSampleData; \
86631 size_t newSampleDataBufferSize; \
86632 \
86633 newSampleDataBufferSize = sampleDataBufferSize * 2; \
86634 pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \
86635 if (pNewSampleData == NULL) { \
86636 drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \
86637 goto on_error; \
86638 } \
86639 \
86640 sampleDataBufferSize = newSampleDataBufferSize; \
86641 pSampleData = pNewSampleData; \
86642 } \
86643 \
86644 DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \
86645 totalPCMFrameCount += pcmFramesRead; \
86646 } \
86647 \
86648 \
86649 DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \
86650 } else { \
86651 drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \
86652 if (dataSize > (drflac_uint64)DRFLAC_SIZE_MAX) { \
86653 goto on_error; \
86654 } \
86655 \
86656 pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \
86657 if (pSampleData == NULL) { \
86658 goto on_error; \
86659 } \
86660 \
86661 totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \
86662 } \
86663 \
86664 if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \
86665 if (channelsOut) *channelsOut = pFlac->channels; \
86666 if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \
86667 \
86668 drflac_close(pFlac); \
86669 return pSampleData; \
86670 \
86671on_error: \
86672 drflac_close(pFlac); \
86673 return NULL; \
86674}
86675DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32)
86676DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16)
86677DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float)
86678DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
86679{
86680 drflac* pFlac;
86681 if (channelsOut) {
86682 *channelsOut = 0;
86683 }
86684 if (sampleRateOut) {
86685 *sampleRateOut = 0;
86686 }
86687 if (totalPCMFrameCountOut) {
86688 *totalPCMFrameCountOut = 0;
86689 }
86690 pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
86691 if (pFlac == NULL) {
86692 return NULL;
86693 }
86694 return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
86695}
86696DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
86697{
86698 drflac* pFlac;
86699 if (channelsOut) {
86700 *channelsOut = 0;
86701 }
86702 if (sampleRateOut) {
86703 *sampleRateOut = 0;
86704 }
86705 if (totalPCMFrameCountOut) {
86706 *totalPCMFrameCountOut = 0;
86707 }
86708 pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
86709 if (pFlac == NULL) {
86710 return NULL;
86711 }
86712 return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
86713}
86714DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
86715{
86716 drflac* pFlac;
86717 if (channelsOut) {
86718 *channelsOut = 0;
86719 }
86720 if (sampleRateOut) {
86721 *sampleRateOut = 0;
86722 }
86723 if (totalPCMFrameCountOut) {
86724 *totalPCMFrameCountOut = 0;
86725 }
86726 pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
86727 if (pFlac == NULL) {
86728 return NULL;
86729 }
86730 return drflac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
86731}
86732#ifndef DR_FLAC_NO_STDIO
86733DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
86734{
86735 drflac* pFlac;
86736 if (sampleRate) {
86737 *sampleRate = 0;
86738 }
86739 if (channels) {
86740 *channels = 0;
86741 }
86742 if (totalPCMFrameCount) {
86743 *totalPCMFrameCount = 0;
86744 }
86745 pFlac = drflac_open_file(filename, pAllocationCallbacks);
86746 if (pFlac == NULL) {
86747 return NULL;
86748 }
86749 return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
86750}
86751DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
86752{
86753 drflac* pFlac;
86754 if (sampleRate) {
86755 *sampleRate = 0;
86756 }
86757 if (channels) {
86758 *channels = 0;
86759 }
86760 if (totalPCMFrameCount) {
86761 *totalPCMFrameCount = 0;
86762 }
86763 pFlac = drflac_open_file(filename, pAllocationCallbacks);
86764 if (pFlac == NULL) {
86765 return NULL;
86766 }
86767 return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
86768}
86769DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
86770{
86771 drflac* pFlac;
86772 if (sampleRate) {
86773 *sampleRate = 0;
86774 }
86775 if (channels) {
86776 *channels = 0;
86777 }
86778 if (totalPCMFrameCount) {
86779 *totalPCMFrameCount = 0;
86780 }
86781 pFlac = drflac_open_file(filename, pAllocationCallbacks);
86782 if (pFlac == NULL) {
86783 return NULL;
86784 }
86785 return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
86786}
86787#endif
86788DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
86789{
86790 drflac* pFlac;
86791 if (sampleRate) {
86792 *sampleRate = 0;
86793 }
86794 if (channels) {
86795 *channels = 0;
86796 }
86797 if (totalPCMFrameCount) {
86798 *totalPCMFrameCount = 0;
86799 }
86800 pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks);
86801 if (pFlac == NULL) {
86802 return NULL;
86803 }
86804 return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
86805}
86806DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
86807{
86808 drflac* pFlac;
86809 if (sampleRate) {
86810 *sampleRate = 0;
86811 }
86812 if (channels) {
86813 *channels = 0;
86814 }
86815 if (totalPCMFrameCount) {
86816 *totalPCMFrameCount = 0;
86817 }
86818 pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks);
86819 if (pFlac == NULL) {
86820 return NULL;
86821 }
86822 return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
86823}
86824DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
86825{
86826 drflac* pFlac;
86827 if (sampleRate) {
86828 *sampleRate = 0;
86829 }
86830 if (channels) {
86831 *channels = 0;
86832 }
86833 if (totalPCMFrameCount) {
86834 *totalPCMFrameCount = 0;
86835 }
86836 pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks);
86837 if (pFlac == NULL) {
86838 return NULL;
86839 }
86840 return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
86841}
86842DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks)
86843{
86844 if (pAllocationCallbacks != NULL) {
86845 drflac__free_from_callbacks(p, pAllocationCallbacks);
86846 } else {
86847 drflac__free_default(p, NULL);
86848 }
86849}
86851{
86852 if (pIter == NULL) {
86853 return;
86854 }
86855 pIter->countRemaining = commentCount;
86856 pIter->pRunningData = (const char*)pComments;
86857}
86859{
86860 drflac_int32 length;
86861 const char* pComment;
86862 if (pCommentLengthOut) {
86863 *pCommentLengthOut = 0;
86864 }
86865 if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
86866 return NULL;
86867 }
86868 length = drflac__le2host_32_ptr_unaligned(pIter->pRunningData);
86869 pIter->pRunningData += 4;
86870 pComment = pIter->pRunningData;
86871 pIter->pRunningData += length;
86872 pIter->countRemaining -= 1;
86873 if (pCommentLengthOut) {
86874 *pCommentLengthOut = length;
86875 }
86876 return pComment;
86877}
86879{
86880 if (pIter == NULL) {
86881 return;
86882 }
86883 pIter->countRemaining = trackCount;
86884 pIter->pRunningData = (const char*)pTrackData;
86885}
86887{
86888 drflac_cuesheet_track cuesheetTrack;
86889 const char* pRunningData;
86890 drflac_uint64 offsetHi;
86891 drflac_uint64 offsetLo;
86892 if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
86893 return DRFLAC_FALSE;
86894 }
86895 pRunningData = pIter->pRunningData;
86896 offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
86897 offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
86898 cuesheetTrack.offset = offsetLo | (offsetHi << 32);
86899 cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1;
86900 DRFLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12;
86901 cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0;
86902 cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14;
86903 cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1;
86904 cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index);
86905 pIter->pRunningData = pRunningData;
86906 pIter->countRemaining -= 1;
86907 if (pCuesheetTrack) {
86908 *pCuesheetTrack = cuesheetTrack;
86909 }
86910 return DRFLAC_TRUE;
86911}
86912#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
86913 #pragma GCC diagnostic pop
86914#endif
86915#endif
86916/* dr_flac_c end */
86917#endif /* DRFLAC_IMPLEMENTATION */
86918#endif /* MA_NO_FLAC */
86919
86920#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
86921#if !defined(DR_MP3_IMPLEMENTATION) && !defined(DRMP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
86922/* dr_mp3_c begin */
86923#ifndef dr_mp3_c
86924#define dr_mp3_c
86925#include <stdlib.h>
86926#include <string.h>
86927#include <limits.h>
86928DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision)
86929{
86930 if (pMajor) {
86931 *pMajor = DRMP3_VERSION_MAJOR;
86932 }
86933 if (pMinor) {
86934 *pMinor = DRMP3_VERSION_MINOR;
86935 }
86936 if (pRevision) {
86937 *pRevision = DRMP3_VERSION_REVISION;
86938 }
86939}
86940DRMP3_API const char* drmp3_version_string(void)
86941{
86942 return DRMP3_VERSION_STRING;
86943}
86944#if defined(__TINYC__)
86945#define DR_MP3_NO_SIMD
86946#endif
86947#define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset)))
86948#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304
86949#ifndef DRMP3_MAX_FRAME_SYNC_MATCHES
86950#define DRMP3_MAX_FRAME_SYNC_MATCHES 10
86951#endif
86952#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE
86953#define DRMP3_MAX_BITRESERVOIR_BYTES 511
86954#define DRMP3_SHORT_BLOCK_TYPE 2
86955#define DRMP3_STOP_BLOCK_TYPE 3
86956#define DRMP3_MODE_MONO 3
86957#define DRMP3_MODE_JOINT_STEREO 1
86958#define DRMP3_HDR_SIZE 4
86959#define DRMP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0)
86960#define DRMP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60)
86961#define DRMP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0)
86962#define DRMP3_HDR_IS_CRC(h) (!((h[1]) & 1))
86963#define DRMP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2)
86964#define DRMP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8)
86965#define DRMP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10)
86966#define DRMP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10)
86967#define DRMP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20)
86968#define DRMP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3)
86969#define DRMP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3)
86970#define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3)
86971#define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4)
86972#define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3)
86973#define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3)
86974#define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2)
86975#define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6)
86976#define DRMP3_BITS_DEQUANTIZER_OUT -1
86977#define DRMP3_MAX_SCF (255 + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210)
86978#define DRMP3_MAX_SCFI ((DRMP3_MAX_SCF + 3) & ~3)
86979#define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a))
86980#define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a))
86981#if !defined(DR_MP3_NO_SIMD)
86982#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64))
86983#define DR_MP3_ONLY_SIMD
86984#endif
86985#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__))
86986#if defined(_MSC_VER)
86987#include <intrin.h>
86988#endif
86989#include <emmintrin.h>
86990#define DRMP3_HAVE_SSE 1
86991#define DRMP3_HAVE_SIMD 1
86992#define DRMP3_VSTORE _mm_storeu_ps
86993#define DRMP3_VLD _mm_loadu_ps
86994#define DRMP3_VSET _mm_set1_ps
86995#define DRMP3_VADD _mm_add_ps
86996#define DRMP3_VSUB _mm_sub_ps
86997#define DRMP3_VMUL _mm_mul_ps
86998#define DRMP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y))
86999#define DRMP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y))
87000#define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s))
87001#define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3))
87002typedef __m128 drmp3_f4;
87003#if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD)
87004#define drmp3_cpuid __cpuid
87005#else
87006static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType)
87007{
87008#if defined(__PIC__)
87009 __asm__ __volatile__(
87010#if defined(__x86_64__)
87011 "push %%rbx\n"
87012 "cpuid\n"
87013 "xchgl %%ebx, %1\n"
87014 "pop %%rbx\n"
87015#else
87016 "xchgl %%ebx, %1\n"
87017 "cpuid\n"
87018 "xchgl %%ebx, %1\n"
87019#endif
87020 : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3])
87021 : "a" (InfoType));
87022#else
87023 __asm__ __volatile__(
87024 "cpuid"
87025 : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3])
87026 : "a" (InfoType));
87027#endif
87028}
87029#endif
87030static int drmp3_have_simd(void)
87031{
87032#ifdef DR_MP3_ONLY_SIMD
87033 return 1;
87034#else
87035 static int g_have_simd;
87036 int CPUInfo[4];
87037#ifdef MINIMP3_TEST
87038 static int g_counter;
87039 if (g_counter++ > 100)
87040 return 0;
87041#endif
87042 if (g_have_simd)
87043 goto end;
87044 drmp3_cpuid(CPUInfo, 0);
87045 if (CPUInfo[0] > 0)
87046 {
87047 drmp3_cpuid(CPUInfo, 1);
87048 g_have_simd = (CPUInfo[3] & (1 << 26)) + 1;
87049 return g_have_simd - 1;
87050 }
87051end:
87052 return g_have_simd - 1;
87053#endif
87054}
87055#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)
87056#include <arm_neon.h>
87057#define DRMP3_HAVE_SSE 0
87058#define DRMP3_HAVE_SIMD 1
87059#define DRMP3_VSTORE vst1q_f32
87060#define DRMP3_VLD vld1q_f32
87061#define DRMP3_VSET vmovq_n_f32
87062#define DRMP3_VADD vaddq_f32
87063#define DRMP3_VSUB vsubq_f32
87064#define DRMP3_VMUL vmulq_f32
87065#define DRMP3_VMAC(a, x, y) vmlaq_f32(a, x, y)
87066#define DRMP3_VMSB(a, x, y) vmlsq_f32(a, x, y)
87067#define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s))
87068#define DRMP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x)))
87069typedef float32x4_t drmp3_f4;
87070static int drmp3_have_simd(void)
87071{
87072 return 1;
87073}
87074#else
87075#define DRMP3_HAVE_SSE 0
87076#define DRMP3_HAVE_SIMD 0
87077#ifdef DR_MP3_ONLY_SIMD
87078#error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled
87079#endif
87080#endif
87081#else
87082#define DRMP3_HAVE_SIMD 0
87083#endif
87084#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64)
87085#define DRMP3_HAVE_ARMV6 1
87086static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(drmp3_int32 a)
87087{
87088 drmp3_int32 x = 0;
87089 __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a));
87090 return x;
87091}
87092#else
87093#define DRMP3_HAVE_ARMV6 0
87094#endif
87095#ifndef DRMP3_ASSERT
87096#include <assert.h>
87097#define DRMP3_ASSERT(expression) assert(expression)
87098#endif
87099#ifndef DRMP3_COPY_MEMORY
87100#define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
87101#endif
87102#ifndef DRMP3_MOVE_MEMORY
87103#define DRMP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz))
87104#endif
87105#ifndef DRMP3_ZERO_MEMORY
87106#define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
87107#endif
87108#define DRMP3_ZERO_OBJECT(p) DRMP3_ZERO_MEMORY((p), sizeof(*(p)))
87109#ifndef DRMP3_MALLOC
87110#define DRMP3_MALLOC(sz) malloc((sz))
87111#endif
87112#ifndef DRMP3_REALLOC
87113#define DRMP3_REALLOC(p, sz) realloc((p), (sz))
87114#endif
87115#ifndef DRMP3_FREE
87116#define DRMP3_FREE(p) free((p))
87117#endif
87118typedef struct
87119{
87120 const drmp3_uint8 *buf;
87121 int pos, limit;
87122} drmp3_bs;
87123typedef struct
87124{
87125 float scf[3*64];
87126 drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64];
87127} drmp3_L12_scale_info;
87128typedef struct
87129{
87130 drmp3_uint8 tab_offset, code_tab_width, band_count;
87131} drmp3_L12_subband_alloc;
87132typedef struct
87133{
87134 const drmp3_uint8 *sfbtab;
87135 drmp3_uint16 part_23_length, big_values, scalefac_compress;
87136 drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb;
87137 drmp3_uint8 table_select[3], region_count[3], subblock_gain[3];
87138 drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi;
87139} drmp3_L3_gr_info;
87140typedef struct
87141{
87142 drmp3_bs bs;
87143 drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES];
87144 drmp3_L3_gr_info gr_info[4];
87145 float grbuf[2][576], scf[40], syn[18 + 15][2*32];
87146 drmp3_uint8 ist_pos[2][39];
87147} drmp3dec_scratch;
87148static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes)
87149{
87150 bs->buf = data;
87151 bs->pos = 0;
87152 bs->limit = bytes*8;
87153}
87154static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n)
87155{
87156 drmp3_uint32 next, cache = 0, s = bs->pos & 7;
87157 int shl = n + s;
87158 const drmp3_uint8 *p = bs->buf + (bs->pos >> 3);
87159 if ((bs->pos += n) > bs->limit)
87160 return 0;
87161 next = *p++ & (255 >> s);
87162 while ((shl -= 8) > 0)
87163 {
87164 cache |= next << shl;
87165 next = *p++;
87166 }
87167 return cache | (next >> -shl);
87168}
87169static int drmp3_hdr_valid(const drmp3_uint8 *h)
87170{
87171 return h[0] == 0xff &&
87172 ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) &&
87173 (DRMP3_HDR_GET_LAYER(h) != 0) &&
87174 (DRMP3_HDR_GET_BITRATE(h) != 15) &&
87175 (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3);
87176}
87177static int drmp3_hdr_compare(const drmp3_uint8 *h1, const drmp3_uint8 *h2)
87178{
87179 return drmp3_hdr_valid(h2) &&
87180 ((h1[1] ^ h2[1]) & 0xFE) == 0 &&
87181 ((h1[2] ^ h2[2]) & 0x0C) == 0 &&
87182 !(DRMP3_HDR_IS_FREE_FORMAT(h1) ^ DRMP3_HDR_IS_FREE_FORMAT(h2));
87183}
87184static unsigned drmp3_hdr_bitrate_kbps(const drmp3_uint8 *h)
87185{
87186 static const drmp3_uint8 halfrate[2][3][15] = {
87187 { { 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 } },
87188 { { 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 } },
87189 };
87190 return 2*halfrate[!!DRMP3_HDR_TEST_MPEG1(h)][DRMP3_HDR_GET_LAYER(h) - 1][DRMP3_HDR_GET_BITRATE(h)];
87191}
87192static unsigned drmp3_hdr_sample_rate_hz(const drmp3_uint8 *h)
87193{
87194 static const unsigned g_hz[3] = { 44100, 48000, 32000 };
87195 return g_hz[DRMP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!DRMP3_HDR_TEST_MPEG1(h) >> (int)!DRMP3_HDR_TEST_NOT_MPEG25(h);
87196}
87197static unsigned drmp3_hdr_frame_samples(const drmp3_uint8 *h)
87198{
87199 return DRMP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)DRMP3_HDR_IS_FRAME_576(h));
87200}
87201static int drmp3_hdr_frame_bytes(const drmp3_uint8 *h, int free_format_size)
87202{
87203 int frame_bytes = drmp3_hdr_frame_samples(h)*drmp3_hdr_bitrate_kbps(h)*125/drmp3_hdr_sample_rate_hz(h);
87204 if (DRMP3_HDR_IS_LAYER_1(h))
87205 {
87206 frame_bytes &= ~3;
87207 }
87208 return frame_bytes ? frame_bytes : free_format_size;
87209}
87210static int drmp3_hdr_padding(const drmp3_uint8 *h)
87211{
87212 return DRMP3_HDR_TEST_PADDING(h) ? (DRMP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0;
87213}
87214#ifndef DR_MP3_ONLY_MP3
87215static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_uint8 *hdr, drmp3_L12_scale_info *sci)
87216{
87217 const drmp3_L12_subband_alloc *alloc;
87218 int mode = DRMP3_HDR_GET_STEREO_MODE(hdr);
87219 int nbands, stereo_bands = (mode == DRMP3_MODE_MONO) ? 0 : (mode == DRMP3_MODE_JOINT_STEREO) ? (DRMP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32;
87220 if (DRMP3_HDR_IS_LAYER_1(hdr))
87221 {
87222 static const drmp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } };
87223 alloc = g_alloc_L1;
87224 nbands = 32;
87225 } else if (!DRMP3_HDR_TEST_MPEG1(hdr))
87226 {
87227 static const drmp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } };
87228 alloc = g_alloc_L2M2;
87229 nbands = 30;
87230 } else
87231 {
87232 static const drmp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } };
87233 int sample_rate_idx = DRMP3_HDR_GET_SAMPLE_RATE(hdr);
87234 unsigned kbps = drmp3_hdr_bitrate_kbps(hdr) >> (int)(mode != DRMP3_MODE_MONO);
87235 if (!kbps)
87236 {
87237 kbps = 192;
87238 }
87239 alloc = g_alloc_L2M1;
87240 nbands = 27;
87241 if (kbps < 56)
87242 {
87243 static const drmp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } };
87244 alloc = g_alloc_L2M1_lowrate;
87245 nbands = sample_rate_idx == 2 ? 12 : 8;
87246 } else if (kbps >= 96 && sample_rate_idx != 1)
87247 {
87248 nbands = 30;
87249 }
87250 }
87251 sci->total_bands = (drmp3_uint8)nbands;
87252 sci->stereo_bands = (drmp3_uint8)DRMP3_MIN(stereo_bands, nbands);
87253 return alloc;
87254}
87255static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_uint8 *scfcod, int bands, float *scf)
87256{
87257 static const float g_deq_L12[18*3] = {
87258#define DRMP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x
87259 DRMP3_DQ(3),DRMP3_DQ(7),DRMP3_DQ(15),DRMP3_DQ(31),DRMP3_DQ(63),DRMP3_DQ(127),DRMP3_DQ(255),DRMP3_DQ(511),DRMP3_DQ(1023),DRMP3_DQ(2047),DRMP3_DQ(4095),DRMP3_DQ(8191),DRMP3_DQ(16383),DRMP3_DQ(32767),DRMP3_DQ(65535),DRMP3_DQ(3),DRMP3_DQ(5),DRMP3_DQ(9)
87260 };
87261 int i, m;
87262 for (i = 0; i < bands; i++)
87263 {
87264 float s = 0;
87265 int ba = *pba++;
87266 int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0;
87267 for (m = 4; m; m >>= 1)
87268 {
87269 if (mask & m)
87270 {
87271 int b = drmp3_bs_get_bits(bs, 6);
87272 s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3);
87273 }
87274 *scf++ = s;
87275 }
87276 }
87277}
87278static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp3_L12_scale_info *sci)
87279{
87280 static const drmp3_uint8 g_bitalloc_code_tab[] = {
87281 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16,
87282 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16,
87283 0,17,18, 3,19,4,5,16,
87284 0,17,18,16,
87285 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15,
87286 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14,
87287 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16
87288 };
87289 const drmp3_L12_subband_alloc *subband_alloc = drmp3_L12_subband_alloc_table(hdr, sci);
87290 int i, k = 0, ba_bits = 0;
87291 const drmp3_uint8 *ba_code_tab = g_bitalloc_code_tab;
87292 for (i = 0; i < sci->total_bands; i++)
87293 {
87294 drmp3_uint8 ba;
87295 if (i == k)
87296 {
87297 k += subband_alloc->band_count;
87298 ba_bits = subband_alloc->code_tab_width;
87299 ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset;
87300 subband_alloc++;
87301 }
87302 ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)];
87303 sci->bitalloc[2*i] = ba;
87304 if (i < sci->stereo_bands)
87305 {
87306 ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)];
87307 }
87308 sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0;
87309 }
87310 for (i = 0; i < 2*sci->total_bands; i++)
87311 {
87312 sci->scfcod[i] = (drmp3_uint8)(sci->bitalloc[i] ? DRMP3_HDR_IS_LAYER_1(hdr) ? 2 : drmp3_bs_get_bits(bs, 2) : 6);
87313 }
87314 drmp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf);
87315 for (i = sci->stereo_bands; i < sci->total_bands; i++)
87316 {
87317 sci->bitalloc[2*i + 1] = 0;
87318 }
87319}
87320static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_scale_info *sci, int group_size)
87321{
87322 int i, j, k, choff = 576;
87323 for (j = 0; j < 4; j++)
87324 {
87325 float *dst = grbuf + group_size*j;
87326 for (i = 0; i < 2*sci->total_bands; i++)
87327 {
87328 int ba = sci->bitalloc[i];
87329 if (ba != 0)
87330 {
87331 if (ba < 17)
87332 {
87333 int half = (1 << (ba - 1)) - 1;
87334 for (k = 0; k < group_size; k++)
87335 {
87336 dst[k] = (float)((int)drmp3_bs_get_bits(bs, ba) - half);
87337 }
87338 } else
87339 {
87340 unsigned mod = (2 << (ba - 17)) + 1;
87341 unsigned code = drmp3_bs_get_bits(bs, mod + 2 - (mod >> 3));
87342 for (k = 0; k < group_size; k++, code /= mod)
87343 {
87344 dst[k] = (float)((int)(code % mod - mod/2));
87345 }
87346 }
87347 }
87348 dst += choff;
87349 choff = 18 - choff;
87350 }
87351 }
87352 return group_size*4;
87353}
87354static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, float *dst)
87355{
87356 int i, k;
87357 DRMP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float));
87358 for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6)
87359 {
87360 for (k = 0; k < 12; k++)
87361 {
87362 dst[k + 0] *= scf[0];
87363 dst[k + 576] *= scf[3];
87364 }
87365 }
87366}
87367#endif
87368static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr)
87369{
87370 static const drmp3_uint8 g_scf_long[8][23] = {
87371 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
87372 { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 },
87373 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
87374 { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 },
87375 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
87376 { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 },
87377 { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 },
87378 { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 }
87379 };
87380 static const drmp3_uint8 g_scf_short[8][40] = {
87381 { 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 },
87382 { 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 },
87383 { 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 },
87384 { 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 },
87385 { 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 },
87386 { 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 },
87387 { 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 },
87388 { 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 }
87389 };
87390 static const drmp3_uint8 g_scf_mixed[8][40] = {
87391 { 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 },
87392 { 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 },
87393 { 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 },
87394 { 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 },
87395 { 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 },
87396 { 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 },
87397 { 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 },
87398 { 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 }
87399 };
87400 unsigned tables, scfsi = 0;
87401 int main_data_begin, part_23_sum = 0;
87402 int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2;
87403 int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0);
87404 if (DRMP3_HDR_TEST_MPEG1(hdr))
87405 {
87406 gr_count *= 2;
87407 main_data_begin = drmp3_bs_get_bits(bs, 9);
87408 scfsi = drmp3_bs_get_bits(bs, 7 + gr_count);
87409 } else
87410 {
87411 main_data_begin = drmp3_bs_get_bits(bs, 8 + gr_count) >> gr_count;
87412 }
87413 do
87414 {
87415 if (DRMP3_HDR_IS_MONO(hdr))
87416 {
87417 scfsi <<= 4;
87418 }
87419 gr->part_23_length = (drmp3_uint16)drmp3_bs_get_bits(bs, 12);
87420 part_23_sum += gr->part_23_length;
87421 gr->big_values = (drmp3_uint16)drmp3_bs_get_bits(bs, 9);
87422 if (gr->big_values > 288)
87423 {
87424 return -1;
87425 }
87426 gr->global_gain = (drmp3_uint8)drmp3_bs_get_bits(bs, 8);
87427 gr->scalefac_compress = (drmp3_uint16)drmp3_bs_get_bits(bs, DRMP3_HDR_TEST_MPEG1(hdr) ? 4 : 9);
87428 gr->sfbtab = g_scf_long[sr_idx];
87429 gr->n_long_sfb = 22;
87430 gr->n_short_sfb = 0;
87431 if (drmp3_bs_get_bits(bs, 1))
87432 {
87433 gr->block_type = (drmp3_uint8)drmp3_bs_get_bits(bs, 2);
87434 if (!gr->block_type)
87435 {
87436 return -1;
87437 }
87438 gr->mixed_block_flag = (drmp3_uint8)drmp3_bs_get_bits(bs, 1);
87439 gr->region_count[0] = 7;
87440 gr->region_count[1] = 255;
87441 if (gr->block_type == DRMP3_SHORT_BLOCK_TYPE)
87442 {
87443 scfsi &= 0x0F0F;
87444 if (!gr->mixed_block_flag)
87445 {
87446 gr->region_count[0] = 8;
87447 gr->sfbtab = g_scf_short[sr_idx];
87448 gr->n_long_sfb = 0;
87449 gr->n_short_sfb = 39;
87450 } else
87451 {
87452 gr->sfbtab = g_scf_mixed[sr_idx];
87453 gr->n_long_sfb = DRMP3_HDR_TEST_MPEG1(hdr) ? 8 : 6;
87454 gr->n_short_sfb = 30;
87455 }
87456 }
87457 tables = drmp3_bs_get_bits(bs, 10);
87458 tables <<= 5;
87459 gr->subblock_gain[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
87460 gr->subblock_gain[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
87461 gr->subblock_gain[2] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
87462 } else
87463 {
87464 gr->block_type = 0;
87465 gr->mixed_block_flag = 0;
87466 tables = drmp3_bs_get_bits(bs, 15);
87467 gr->region_count[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 4);
87468 gr->region_count[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
87469 gr->region_count[2] = 255;
87470 }
87471 gr->table_select[0] = (drmp3_uint8)(tables >> 10);
87472 gr->table_select[1] = (drmp3_uint8)((tables >> 5) & 31);
87473 gr->table_select[2] = (drmp3_uint8)((tables) & 31);
87474 gr->preflag = (drmp3_uint8)(DRMP3_HDR_TEST_MPEG1(hdr) ? drmp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500));
87475 gr->scalefac_scale = (drmp3_uint8)drmp3_bs_get_bits(bs, 1);
87476 gr->count1_table = (drmp3_uint8)drmp3_bs_get_bits(bs, 1);
87477 gr->scfsi = (drmp3_uint8)((scfsi >> 12) & 15);
87478 scfsi <<= 4;
87479 gr++;
87480 } while(--gr_count);
87481 if (part_23_sum + bs->pos > bs->limit + main_data_begin*8)
87482 {
87483 return -1;
87484 }
87485 return main_data_begin;
87486}
87487static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, const drmp3_uint8 *scf_size, const drmp3_uint8 *scf_count, drmp3_bs *bitbuf, int scfsi)
87488{
87489 int i, k;
87490 for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2)
87491 {
87492 int cnt = scf_count[i];
87493 if (scfsi & 8)
87494 {
87495 DRMP3_COPY_MEMORY(scf, ist_pos, cnt);
87496 } else
87497 {
87498 int bits = scf_size[i];
87499 if (!bits)
87500 {
87501 DRMP3_ZERO_MEMORY(scf, cnt);
87502 DRMP3_ZERO_MEMORY(ist_pos, cnt);
87503 } else
87504 {
87505 int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1;
87506 for (k = 0; k < cnt; k++)
87507 {
87508 int s = drmp3_bs_get_bits(bitbuf, bits);
87509 ist_pos[k] = (drmp3_uint8)(s == max_scf ? -1 : s);
87510 scf[k] = (drmp3_uint8)s;
87511 }
87512 }
87513 }
87514 ist_pos += cnt;
87515 scf += cnt;
87516 }
87517 scf[0] = scf[1] = scf[2] = 0;
87518}
87519static float drmp3_L3_ldexp_q2(float y, int exp_q2)
87520{
87521 static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f };
87522 int e;
87523 do
87524 {
87525 e = DRMP3_MIN(30*4, exp_q2);
87526 y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2));
87527 } while ((exp_q2 -= e) > 0);
87528 return y;
87529}
87530static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch)
87531{
87532 static const drmp3_uint8 g_scf_partitions[3][28] = {
87533 { 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 },
87534 { 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 },
87535 { 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 }
87536 };
87537 const drmp3_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb];
87538 drmp3_uint8 scf_size[4], iscf[40];
87539 int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi;
87540 float gain;
87541 if (DRMP3_HDR_TEST_MPEG1(hdr))
87542 {
87543 static const drmp3_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 };
87544 int part = g_scfc_decode[gr->scalefac_compress];
87545 scf_size[1] = scf_size[0] = (drmp3_uint8)(part >> 2);
87546 scf_size[3] = scf_size[2] = (drmp3_uint8)(part & 3);
87547 } else
87548 {
87549 static const drmp3_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 };
87550 int k, modprod, sfc, ist = DRMP3_HDR_TEST_I_STEREO(hdr) && ch;
87551 sfc = gr->scalefac_compress >> ist;
87552 for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4)
87553 {
87554 for (modprod = 1, i = 3; i >= 0; i--)
87555 {
87556 scf_size[i] = (drmp3_uint8)(sfc / modprod % g_mod[k + i]);
87557 modprod *= g_mod[k + i];
87558 }
87559 }
87560 scf_partition += k;
87561 scfsi = -16;
87562 }
87563 drmp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi);
87564 if (gr->n_short_sfb)
87565 {
87566 int sh = 3 - scf_shift;
87567 for (i = 0; i < gr->n_short_sfb; i += 3)
87568 {
87569 iscf[gr->n_long_sfb + i + 0] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh));
87570 iscf[gr->n_long_sfb + i + 1] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh));
87571 iscf[gr->n_long_sfb + i + 2] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh));
87572 }
87573 } else if (gr->preflag)
87574 {
87575 static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 };
87576 for (i = 0; i < 10; i++)
87577 {
87578 iscf[11 + i] = (drmp3_uint8)(iscf[11 + i] + g_preamp[i]);
87579 }
87580 }
87581 gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210 - (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0);
87582 gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI/4), DRMP3_MAX_SCFI - gain_exp);
87583 for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++)
87584 {
87585 scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift);
87586 }
87587}
87588static const float g_drmp3_pow43[129 + 16] = {
87589 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,
87590 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
87591};
87592static float drmp3_L3_pow_43(int x)
87593{
87594 float frac;
87595 int sign, mult = 256;
87596 if (x < 129)
87597 {
87598 return g_drmp3_pow43[16 + x];
87599 }
87600 if (x < 1024)
87601 {
87602 mult = 16;
87603 x <<= 3;
87604 }
87605 sign = 2*x & 64;
87606 frac = (float)((x & 63) - sign) / ((x & ~63) + sign);
87607 return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult;
87608}
87609static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit)
87610{
87611 static const drmp3_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
87612 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,
87613 -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,
87614 -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,
87615 -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,
87616 -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,
87617 -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,
87618 -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,
87619 -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,
87620 -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,
87621 -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,
87622 -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,
87623 -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,
87624 -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,
87625 -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,
87626 -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 };
87627 static const drmp3_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205};
87628 static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 };
87629 static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 };
87630 static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 };
87631#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - n))
87632#define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); }
87633#define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; }
87634#define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh)
87635 float one = 0.0f;
87636 int ireg = 0, big_val_cnt = gr_info->big_values;
87637 const drmp3_uint8 *sfb = gr_info->sfbtab;
87638 const drmp3_uint8 *bs_next_ptr = bs->buf + bs->pos/8;
87639 drmp3_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7);
87640 int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8;
87641 bs_next_ptr += 4;
87642 while (big_val_cnt > 0)
87643 {
87644 int tab_num = gr_info->table_select[ireg];
87645 int sfb_cnt = gr_info->region_count[ireg++];
87646 const drmp3_int16 *codebook = tabs + tabindex[tab_num];
87647 int linbits = g_linbits[tab_num];
87648 if (linbits)
87649 {
87650 do
87651 {
87652 np = *sfb++ / 2;
87653 pairs_to_decode = DRMP3_MIN(big_val_cnt, np);
87654 one = *scf++;
87655 do
87656 {
87657 int j, w = 5;
87658 int leaf = codebook[DRMP3_PEEK_BITS(w)];
87659 while (leaf < 0)
87660 {
87661 DRMP3_FLUSH_BITS(w);
87662 w = leaf & 7;
87663 leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)];
87664 }
87665 DRMP3_FLUSH_BITS(leaf >> 8);
87666 for (j = 0; j < 2; j++, dst++, leaf >>= 4)
87667 {
87668 int lsb = leaf & 0x0F;
87669 if (lsb == 15)
87670 {
87671 lsb += DRMP3_PEEK_BITS(linbits);
87672 DRMP3_FLUSH_BITS(linbits);
87673 DRMP3_CHECK_BITS;
87674 *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1);
87675 } else
87676 {
87677 *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
87678 }
87679 DRMP3_FLUSH_BITS(lsb ? 1 : 0);
87680 }
87681 DRMP3_CHECK_BITS;
87682 } while (--pairs_to_decode);
87683 } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
87684 } else
87685 {
87686 do
87687 {
87688 np = *sfb++ / 2;
87689 pairs_to_decode = DRMP3_MIN(big_val_cnt, np);
87690 one = *scf++;
87691 do
87692 {
87693 int j, w = 5;
87694 int leaf = codebook[DRMP3_PEEK_BITS(w)];
87695 while (leaf < 0)
87696 {
87697 DRMP3_FLUSH_BITS(w);
87698 w = leaf & 7;
87699 leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)];
87700 }
87701 DRMP3_FLUSH_BITS(leaf >> 8);
87702 for (j = 0; j < 2; j++, dst++, leaf >>= 4)
87703 {
87704 int lsb = leaf & 0x0F;
87705 *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
87706 DRMP3_FLUSH_BITS(lsb ? 1 : 0);
87707 }
87708 DRMP3_CHECK_BITS;
87709 } while (--pairs_to_decode);
87710 } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
87711 }
87712 }
87713 for (np = 1 - big_val_cnt;; dst += 4)
87714 {
87715 const drmp3_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32;
87716 int leaf = codebook_count1[DRMP3_PEEK_BITS(4)];
87717 if (!(leaf & 8))
87718 {
87719 leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))];
87720 }
87721 DRMP3_FLUSH_BITS(leaf & 7);
87722 if (DRMP3_BSPOS > layer3gr_limit)
87723 {
87724 break;
87725 }
87726#define DRMP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; }
87727#define DRMP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((drmp3_int32)bs_cache < 0) ? -one : one; DRMP3_FLUSH_BITS(1) }
87728 DRMP3_RELOAD_SCALEFACTOR;
87729 DRMP3_DEQ_COUNT1(0);
87730 DRMP3_DEQ_COUNT1(1);
87731 DRMP3_RELOAD_SCALEFACTOR;
87732 DRMP3_DEQ_COUNT1(2);
87733 DRMP3_DEQ_COUNT1(3);
87734 DRMP3_CHECK_BITS;
87735 }
87736 bs->pos = layer3gr_limit;
87737}
87738static void drmp3_L3_midside_stereo(float *left, int n)
87739{
87740 int i = 0;
87741 float *right = left + 576;
87742#if DRMP3_HAVE_SIMD
87743 if (drmp3_have_simd())
87744 {
87745 for (; i < n - 3; i += 4)
87746 {
87747 drmp3_f4 vl = DRMP3_VLD(left + i);
87748 drmp3_f4 vr = DRMP3_VLD(right + i);
87749 DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr));
87750 DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr));
87751 }
87752#ifdef __GNUC__
87753 if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0)
87754 return;
87755#endif
87756 }
87757#endif
87758 for (; i < n; i++)
87759 {
87760 float a = left[i];
87761 float b = right[i];
87762 left[i] = a + b;
87763 right[i] = a - b;
87764 }
87765}
87766static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr)
87767{
87768 int i;
87769 for (i = 0; i < n; i++)
87770 {
87771 left[i + 576] = left[i]*kr;
87772 left[i] = left[i]*kl;
87773 }
87774}
87775static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, int nbands, int max_band[3])
87776{
87777 int i, k;
87778 max_band[0] = max_band[1] = max_band[2] = -1;
87779 for (i = 0; i < nbands; i++)
87780 {
87781 for (k = 0; k < sfb[i]; k += 2)
87782 {
87783 if (right[k] != 0 || right[k + 1] != 0)
87784 {
87785 max_band[i % 3] = i;
87786 break;
87787 }
87788 }
87789 right += sfb[i];
87790 }
87791}
87792static void drmp3_L3_stereo_process(float *left, const drmp3_uint8 *ist_pos, const drmp3_uint8 *sfb, const drmp3_uint8 *hdr, int max_band[3], int mpeg2_sh)
87793{
87794 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 };
87795 unsigned i, max_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 7 : 64;
87796 for (i = 0; sfb[i]; i++)
87797 {
87798 unsigned ipos = ist_pos[i];
87799 if ((int)i > max_band[i % 3] && ipos < max_pos)
87800 {
87801 float kl, kr, s = DRMP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1;
87802 if (DRMP3_HDR_TEST_MPEG1(hdr))
87803 {
87804 kl = g_pan[2*ipos];
87805 kr = g_pan[2*ipos + 1];
87806 } else
87807 {
87808 kl = 1;
87809 kr = drmp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh);
87810 if (ipos & 1)
87811 {
87812 kl = kr;
87813 kr = 1;
87814 }
87815 }
87816 drmp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s);
87817 } else if (DRMP3_HDR_TEST_MS_STEREO(hdr))
87818 {
87819 drmp3_L3_midside_stereo(left, sfb[i]);
87820 }
87821 left += sfb[i];
87822 }
87823}
87824static void drmp3_L3_intensity_stereo(float *left, drmp3_uint8 *ist_pos, const drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr)
87825{
87826 int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb;
87827 int i, max_blocks = gr->n_short_sfb ? 3 : 1;
87828 drmp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band);
87829 if (gr->n_long_sfb)
87830 {
87831 max_band[0] = max_band[1] = max_band[2] = DRMP3_MAX(DRMP3_MAX(max_band[0], max_band[1]), max_band[2]);
87832 }
87833 for (i = 0; i < max_blocks; i++)
87834 {
87835 int default_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 3 : 0;
87836 int itop = n_sfb - max_blocks + i;
87837 int prev = itop - max_blocks;
87838 ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]);
87839 }
87840 drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1);
87841}
87842static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sfb)
87843{
87844 int i, len;
87845 float *src = grbuf, *dst = scratch;
87846 for (;0 != (len = *sfb); sfb += 3, src += 2*len)
87847 {
87848 for (i = 0; i < len; i++, src++)
87849 {
87850 *dst++ = src[0*len];
87851 *dst++ = src[1*len];
87852 *dst++ = src[2*len];
87853 }
87854 }
87855 DRMP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float));
87856}
87857static void drmp3_L3_antialias(float *grbuf, int nbands)
87858{
87859 static const float g_aa[2][8] = {
87860 {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f},
87861 {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f}
87862 };
87863 for (; nbands > 0; nbands--, grbuf += 18)
87864 {
87865 int i = 0;
87866#if DRMP3_HAVE_SIMD
87867 if (drmp3_have_simd()) for (; i < 8; i += 4)
87868 {
87869 drmp3_f4 vu = DRMP3_VLD(grbuf + 18 + i);
87870 drmp3_f4 vd = DRMP3_VLD(grbuf + 14 - i);
87871 drmp3_f4 vc0 = DRMP3_VLD(g_aa[0] + i);
87872 drmp3_f4 vc1 = DRMP3_VLD(g_aa[1] + i);
87873 vd = DRMP3_VREV(vd);
87874 DRMP3_VSTORE(grbuf + 18 + i, DRMP3_VSUB(DRMP3_VMUL(vu, vc0), DRMP3_VMUL(vd, vc1)));
87875 vd = DRMP3_VADD(DRMP3_VMUL(vu, vc1), DRMP3_VMUL(vd, vc0));
87876 DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vd));
87877 }
87878#endif
87879#ifndef DR_MP3_ONLY_SIMD
87880 for(; i < 8; i++)
87881 {
87882 float u = grbuf[18 + i];
87883 float d = grbuf[17 - i];
87884 grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i];
87885 grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i];
87886 }
87887#endif
87888 }
87889}
87890static void drmp3_L3_dct3_9(float *y)
87891{
87892 float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4;
87893 s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8];
87894 t0 = s0 + s6*0.5f;
87895 s0 -= s6;
87896 t4 = (s4 + s2)*0.93969262f;
87897 t2 = (s8 + s2)*0.76604444f;
87898 s6 = (s4 - s8)*0.17364818f;
87899 s4 += s8 - s2;
87900 s2 = s0 - s4*0.5f;
87901 y[4] = s4 + s0;
87902 s8 = t0 - t2 + s6;
87903 s0 = t0 - t4 + t2;
87904 s4 = t0 + t4 - s6;
87905 s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7];
87906 s3 *= 0.86602540f;
87907 t0 = (s5 + s1)*0.98480775f;
87908 t4 = (s5 - s7)*0.34202014f;
87909 t2 = (s1 + s7)*0.64278761f;
87910 s1 = (s1 - s5 - s7)*0.86602540f;
87911 s5 = t0 - s3 - t2;
87912 s7 = t4 - s3 - t0;
87913 s3 = t4 + s3 - t2;
87914 y[0] = s4 - s7;
87915 y[1] = s2 + s1;
87916 y[2] = s0 - s3;
87917 y[3] = s8 + s5;
87918 y[5] = s8 - s5;
87919 y[6] = s0 + s3;
87920 y[7] = s2 - s1;
87921 y[8] = s4 + s7;
87922}
87923static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands)
87924{
87925 int i, j;
87926 static const float g_twid9[18] = {
87927 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
87928 };
87929 for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9)
87930 {
87931 float co[9], si[9];
87932 co[0] = -grbuf[0];
87933 si[0] = grbuf[17];
87934 for (i = 0; i < 4; i++)
87935 {
87936 si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2];
87937 co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2];
87938 si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3];
87939 co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]);
87940 }
87941 drmp3_L3_dct3_9(co);
87942 drmp3_L3_dct3_9(si);
87943 si[1] = -si[1];
87944 si[3] = -si[3];
87945 si[5] = -si[5];
87946 si[7] = -si[7];
87947 i = 0;
87948#if DRMP3_HAVE_SIMD
87949 if (drmp3_have_simd()) for (; i < 8; i += 4)
87950 {
87951 drmp3_f4 vovl = DRMP3_VLD(overlap + i);
87952 drmp3_f4 vc = DRMP3_VLD(co + i);
87953 drmp3_f4 vs = DRMP3_VLD(si + i);
87954 drmp3_f4 vr0 = DRMP3_VLD(g_twid9 + i);
87955 drmp3_f4 vr1 = DRMP3_VLD(g_twid9 + 9 + i);
87956 drmp3_f4 vw0 = DRMP3_VLD(window + i);
87957 drmp3_f4 vw1 = DRMP3_VLD(window + 9 + i);
87958 drmp3_f4 vsum = DRMP3_VADD(DRMP3_VMUL(vc, vr1), DRMP3_VMUL(vs, vr0));
87959 DRMP3_VSTORE(overlap + i, DRMP3_VSUB(DRMP3_VMUL(vc, vr0), DRMP3_VMUL(vs, vr1)));
87960 DRMP3_VSTORE(grbuf + i, DRMP3_VSUB(DRMP3_VMUL(vovl, vw0), DRMP3_VMUL(vsum, vw1)));
87961 vsum = DRMP3_VADD(DRMP3_VMUL(vovl, vw1), DRMP3_VMUL(vsum, vw0));
87962 DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vsum));
87963 }
87964#endif
87965 for (; i < 9; i++)
87966 {
87967 float ovl = overlap[i];
87968 float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i];
87969 overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i];
87970 grbuf[i] = ovl*window[0 + i] - sum*window[9 + i];
87971 grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i];
87972 }
87973 }
87974}
87975static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst)
87976{
87977 float m1 = x1*0.86602540f;
87978 float a1 = x0 - x2*0.5f;
87979 dst[1] = x0 + x2;
87980 dst[0] = a1 + m1;
87981 dst[2] = a1 - m1;
87982}
87983static void drmp3_L3_imdct12(float *x, float *dst, float *overlap)
87984{
87985 static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f };
87986 float co[3], si[3];
87987 int i;
87988 drmp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co);
87989 drmp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si);
87990 si[1] = -si[1];
87991 for (i = 0; i < 3; i++)
87992 {
87993 float ovl = overlap[i];
87994 float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i];
87995 overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i];
87996 dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i];
87997 dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i];
87998 }
87999}
88000static void drmp3_L3_imdct_short(float *grbuf, float *overlap, int nbands)
88001{
88002 for (;nbands > 0; nbands--, overlap += 9, grbuf += 18)
88003 {
88004 float tmp[18];
88005 DRMP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp));
88006 DRMP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float));
88007 drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6);
88008 drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6);
88009 drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6);
88010 }
88011}
88012static void drmp3_L3_change_sign(float *grbuf)
88013{
88014 int b, i;
88015 for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36)
88016 for (i = 1; i < 18; i += 2)
88017 grbuf[i] = -grbuf[i];
88018}
88019static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands)
88020{
88021 static const float g_mdct_window[2][18] = {
88022 { 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 },
88023 { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f }
88024 };
88025 if (n_long_bands)
88026 {
88027 drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands);
88028 grbuf += 18*n_long_bands;
88029 overlap += 9*n_long_bands;
88030 }
88031 if (block_type == DRMP3_SHORT_BLOCK_TYPE)
88032 drmp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands);
88033 else
88034 drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == DRMP3_STOP_BLOCK_TYPE], 32 - n_long_bands);
88035}
88036static void drmp3_L3_save_reservoir(drmp3dec *h, drmp3dec_scratch *s)
88037{
88038 int pos = (s->bs.pos + 7)/8u;
88039 int remains = s->bs.limit/8u - pos;
88040 if (remains > DRMP3_MAX_BITRESERVOIR_BYTES)
88041 {
88042 pos += remains - DRMP3_MAX_BITRESERVOIR_BYTES;
88043 remains = DRMP3_MAX_BITRESERVOIR_BYTES;
88044 }
88045 if (remains > 0)
88046 {
88047 DRMP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains);
88048 }
88049 h->reserv = remains;
88050}
88051static int drmp3_L3_restore_reservoir(drmp3dec *h, drmp3_bs *bs, drmp3dec_scratch *s, int main_data_begin)
88052{
88053 int frame_bytes = (bs->limit - bs->pos)/8;
88054 int bytes_have = DRMP3_MIN(h->reserv, main_data_begin);
88055 DRMP3_COPY_MEMORY(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), DRMP3_MIN(h->reserv, main_data_begin));
88056 DRMP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes);
88057 drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes);
88058 return h->reserv >= main_data_begin;
88059}
88060static void drmp3_L3_decode(drmp3dec *h, drmp3dec_scratch *s, drmp3_L3_gr_info *gr_info, int nch)
88061{
88062 int ch;
88063 for (ch = 0; ch < nch; ch++)
88064 {
88065 int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length;
88066 drmp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch);
88067 drmp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit);
88068 }
88069 if (DRMP3_HDR_TEST_I_STEREO(h->header))
88070 {
88071 drmp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header);
88072 } else if (DRMP3_HDR_IS_MS_STEREO(h->header))
88073 {
88074 drmp3_L3_midside_stereo(s->grbuf[0], 576);
88075 }
88076 for (ch = 0; ch < nch; ch++, gr_info++)
88077 {
88078 int aa_bands = 31;
88079 int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(DRMP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2);
88080 if (gr_info->n_short_sfb)
88081 {
88082 aa_bands = n_long_bands - 1;
88083 drmp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb);
88084 }
88085 drmp3_L3_antialias(s->grbuf[ch], aa_bands);
88086 drmp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands);
88087 drmp3_L3_change_sign(s->grbuf[ch]);
88088 }
88089}
88090static void drmp3d_DCT_II(float *grbuf, int n)
88091{
88092 static const float g_sec[24] = {
88093 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
88094 };
88095 int i, k = 0;
88096#if DRMP3_HAVE_SIMD
88097 if (drmp3_have_simd()) for (; k < n; k += 4)
88098 {
88099 drmp3_f4 t[4][8], *x;
88100 float *y = grbuf + k;
88101 for (x = t[0], i = 0; i < 8; i++, x++)
88102 {
88103 drmp3_f4 x0 = DRMP3_VLD(&y[i*18]);
88104 drmp3_f4 x1 = DRMP3_VLD(&y[(15 - i)*18]);
88105 drmp3_f4 x2 = DRMP3_VLD(&y[(16 + i)*18]);
88106 drmp3_f4 x3 = DRMP3_VLD(&y[(31 - i)*18]);
88107 drmp3_f4 t0 = DRMP3_VADD(x0, x3);
88108 drmp3_f4 t1 = DRMP3_VADD(x1, x2);
88109 drmp3_f4 t2 = DRMP3_VMUL_S(DRMP3_VSUB(x1, x2), g_sec[3*i + 0]);
88110 drmp3_f4 t3 = DRMP3_VMUL_S(DRMP3_VSUB(x0, x3), g_sec[3*i + 1]);
88111 x[0] = DRMP3_VADD(t0, t1);
88112 x[8] = DRMP3_VMUL_S(DRMP3_VSUB(t0, t1), g_sec[3*i + 2]);
88113 x[16] = DRMP3_VADD(t3, t2);
88114 x[24] = DRMP3_VMUL_S(DRMP3_VSUB(t3, t2), g_sec[3*i + 2]);
88115 }
88116 for (x = t[0], i = 0; i < 4; i++, x += 8)
88117 {
88118 drmp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt;
88119 xt = DRMP3_VSUB(x0, x7); x0 = DRMP3_VADD(x0, x7);
88120 x7 = DRMP3_VSUB(x1, x6); x1 = DRMP3_VADD(x1, x6);
88121 x6 = DRMP3_VSUB(x2, x5); x2 = DRMP3_VADD(x2, x5);
88122 x5 = DRMP3_VSUB(x3, x4); x3 = DRMP3_VADD(x3, x4);
88123 x4 = DRMP3_VSUB(x0, x3); x0 = DRMP3_VADD(x0, x3);
88124 x3 = DRMP3_VSUB(x1, x2); x1 = DRMP3_VADD(x1, x2);
88125 x[0] = DRMP3_VADD(x0, x1);
88126 x[4] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x1), 0.70710677f);
88127 x5 = DRMP3_VADD(x5, x6);
88128 x6 = DRMP3_VMUL_S(DRMP3_VADD(x6, x7), 0.70710677f);
88129 x7 = DRMP3_VADD(x7, xt);
88130 x3 = DRMP3_VMUL_S(DRMP3_VADD(x3, x4), 0.70710677f);
88131 x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f));
88132 x7 = DRMP3_VADD(x7, DRMP3_VMUL_S(x5, 0.382683432f));
88133 x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f));
88134 x0 = DRMP3_VSUB(xt, x6); xt = DRMP3_VADD(xt, x6);
88135 x[1] = DRMP3_VMUL_S(DRMP3_VADD(xt, x7), 0.50979561f);
88136 x[2] = DRMP3_VMUL_S(DRMP3_VADD(x4, x3), 0.54119611f);
88137 x[3] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x5), 0.60134488f);
88138 x[5] = DRMP3_VMUL_S(DRMP3_VADD(x0, x5), 0.89997619f);
88139 x[6] = DRMP3_VMUL_S(DRMP3_VSUB(x4, x3), 1.30656302f);
88140 x[7] = DRMP3_VMUL_S(DRMP3_VSUB(xt, x7), 2.56291556f);
88141 }
88142 if (k > n - 3)
88143 {
88144#if DRMP3_HAVE_SSE
88145#define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v)
88146#else
88147#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[i*18], vget_low_f32(v))
88148#endif
88149 for (i = 0; i < 7; i++, y += 4*18)
88150 {
88151 drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]);
88152 DRMP3_VSAVE2(0, t[0][i]);
88153 DRMP3_VSAVE2(1, DRMP3_VADD(t[2][i], s));
88154 DRMP3_VSAVE2(2, DRMP3_VADD(t[1][i], t[1][i + 1]));
88155 DRMP3_VSAVE2(3, DRMP3_VADD(t[2][1 + i], s));
88156 }
88157 DRMP3_VSAVE2(0, t[0][7]);
88158 DRMP3_VSAVE2(1, DRMP3_VADD(t[2][7], t[3][7]));
88159 DRMP3_VSAVE2(2, t[1][7]);
88160 DRMP3_VSAVE2(3, t[3][7]);
88161 } else
88162 {
88163#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[i*18], v)
88164 for (i = 0; i < 7; i++, y += 4*18)
88165 {
88166 drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]);
88167 DRMP3_VSAVE4(0, t[0][i]);
88168 DRMP3_VSAVE4(1, DRMP3_VADD(t[2][i], s));
88169 DRMP3_VSAVE4(2, DRMP3_VADD(t[1][i], t[1][i + 1]));
88170 DRMP3_VSAVE4(3, DRMP3_VADD(t[2][1 + i], s));
88171 }
88172 DRMP3_VSAVE4(0, t[0][7]);
88173 DRMP3_VSAVE4(1, DRMP3_VADD(t[2][7], t[3][7]));
88174 DRMP3_VSAVE4(2, t[1][7]);
88175 DRMP3_VSAVE4(3, t[3][7]);
88176 }
88177 } else
88178#endif
88179#ifdef DR_MP3_ONLY_SIMD
88180 {}
88181#else
88182 for (; k < n; k++)
88183 {
88184 float t[4][8], *x, *y = grbuf + k;
88185 for (x = t[0], i = 0; i < 8; i++, x++)
88186 {
88187 float x0 = y[i*18];
88188 float x1 = y[(15 - i)*18];
88189 float x2 = y[(16 + i)*18];
88190 float x3 = y[(31 - i)*18];
88191 float t0 = x0 + x3;
88192 float t1 = x1 + x2;
88193 float t2 = (x1 - x2)*g_sec[3*i + 0];
88194 float t3 = (x0 - x3)*g_sec[3*i + 1];
88195 x[0] = t0 + t1;
88196 x[8] = (t0 - t1)*g_sec[3*i + 2];
88197 x[16] = t3 + t2;
88198 x[24] = (t3 - t2)*g_sec[3*i + 2];
88199 }
88200 for (x = t[0], i = 0; i < 4; i++, x += 8)
88201 {
88202 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;
88203 xt = x0 - x7; x0 += x7;
88204 x7 = x1 - x6; x1 += x6;
88205 x6 = x2 - x5; x2 += x5;
88206 x5 = x3 - x4; x3 += x4;
88207 x4 = x0 - x3; x0 += x3;
88208 x3 = x1 - x2; x1 += x2;
88209 x[0] = x0 + x1;
88210 x[4] = (x0 - x1)*0.70710677f;
88211 x5 = x5 + x6;
88212 x6 = (x6 + x7)*0.70710677f;
88213 x7 = x7 + xt;
88214 x3 = (x3 + x4)*0.70710677f;
88215 x5 -= x7*0.198912367f;
88216 x7 += x5*0.382683432f;
88217 x5 -= x7*0.198912367f;
88218 x0 = xt - x6; xt += x6;
88219 x[1] = (xt + x7)*0.50979561f;
88220 x[2] = (x4 + x3)*0.54119611f;
88221 x[3] = (x0 - x5)*0.60134488f;
88222 x[5] = (x0 + x5)*0.89997619f;
88223 x[6] = (x4 - x3)*1.30656302f;
88224 x[7] = (xt - x7)*2.56291556f;
88225 }
88226 for (i = 0; i < 7; i++, y += 4*18)
88227 {
88228 y[0*18] = t[0][i];
88229 y[1*18] = t[2][i] + t[3][i] + t[3][i + 1];
88230 y[2*18] = t[1][i] + t[1][i + 1];
88231 y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1];
88232 }
88233 y[0*18] = t[0][7];
88234 y[1*18] = t[2][7] + t[3][7];
88235 y[2*18] = t[1][7];
88236 y[3*18] = t[3][7];
88237 }
88238#endif
88239}
88240#ifndef DR_MP3_FLOAT_OUTPUT
88241typedef drmp3_int16 drmp3d_sample_t;
88242static drmp3_int16 drmp3d_scale_pcm(float sample)
88243{
88244 drmp3_int16 s;
88245#if DRMP3_HAVE_ARMV6
88246 drmp3_int32 s32 = (drmp3_int32)(sample + .5f);
88247 s32 -= (s32 < 0);
88248 s = (drmp3_int16)drmp3_clip_int16_arm(s32);
88249#else
88250 if (sample >= 32766.5) return (drmp3_int16) 32767;
88251 if (sample <= -32767.5) return (drmp3_int16)-32768;
88252 s = (drmp3_int16)(sample + .5f);
88253 s -= (s < 0);
88254#endif
88255 return s;
88256}
88257#else
88258typedef float drmp3d_sample_t;
88259static float drmp3d_scale_pcm(float sample)
88260{
88261 return sample*(1.f/32768.f);
88262}
88263#endif
88264static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z)
88265{
88266 float a;
88267 a = (z[14*64] - z[ 0]) * 29;
88268 a += (z[ 1*64] + z[13*64]) * 213;
88269 a += (z[12*64] - z[ 2*64]) * 459;
88270 a += (z[ 3*64] + z[11*64]) * 2037;
88271 a += (z[10*64] - z[ 4*64]) * 5153;
88272 a += (z[ 5*64] + z[ 9*64]) * 6574;
88273 a += (z[ 8*64] - z[ 6*64]) * 37489;
88274 a += z[ 7*64] * 75038;
88275 pcm[0] = drmp3d_scale_pcm(a);
88276 z += 2;
88277 a = z[14*64] * 104;
88278 a += z[12*64] * 1567;
88279 a += z[10*64] * 9727;
88280 a += z[ 8*64] * 64019;
88281 a += z[ 6*64] * -9975;
88282 a += z[ 4*64] * -45;
88283 a += z[ 2*64] * 146;
88284 a += z[ 0*64] * -5;
88285 pcm[16*nch] = drmp3d_scale_pcm(a);
88286}
88287static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins)
88288{
88289 int i;
88290 float *xr = xl + 576*(nch - 1);
88291 drmp3d_sample_t *dstr = dstl + (nch - 1);
88292 static const float g_win[] = {
88293 -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992,
88294 -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856,
88295 -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630,
88296 -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313,
88297 -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908,
88298 -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415,
88299 -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835,
88300 -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169,
88301 -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420,
88302 -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590,
88303 -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679,
88304 -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692,
88305 -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629,
88306 -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494,
88307 -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290
88308 };
88309 float *zlin = lins + 15*64;
88310 const float *w = g_win;
88311 zlin[4*15] = xl[18*16];
88312 zlin[4*15 + 1] = xr[18*16];
88313 zlin[4*15 + 2] = xl[0];
88314 zlin[4*15 + 3] = xr[0];
88315 zlin[4*31] = xl[1 + 18*16];
88316 zlin[4*31 + 1] = xr[1 + 18*16];
88317 zlin[4*31 + 2] = xl[1];
88318 zlin[4*31 + 3] = xr[1];
88319 drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1);
88320 drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1);
88321 drmp3d_synth_pair(dstl, nch, lins + 4*15);
88322 drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64);
88323#if DRMP3_HAVE_SIMD
88324 if (drmp3_have_simd()) for (i = 14; i >= 0; i--)
88325 {
88326#define DRMP3_VLOAD(k) drmp3_f4 w0 = DRMP3_VSET(*w++); drmp3_f4 w1 = DRMP3_VSET(*w++); drmp3_f4 vz = DRMP3_VLD(&zlin[4*i - 64*k]); drmp3_f4 vy = DRMP3_VLD(&zlin[4*i - 64*(15 - k)]);
88327#define DRMP3_V0(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0)) ; a = DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1)); }
88328#define DRMP3_V1(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1))); }
88329#define DRMP3_V2(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vy, w1), DRMP3_VMUL(vz, w0))); }
88330 drmp3_f4 a, b;
88331 zlin[4*i] = xl[18*(31 - i)];
88332 zlin[4*i + 1] = xr[18*(31 - i)];
88333 zlin[4*i + 2] = xl[1 + 18*(31 - i)];
88334 zlin[4*i + 3] = xr[1 + 18*(31 - i)];
88335 zlin[4*i + 64] = xl[1 + 18*(1 + i)];
88336 zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)];
88337 zlin[4*i - 64 + 2] = xl[18*(1 + i)];
88338 zlin[4*i - 64 + 3] = xr[18*(1 + i)];
88339 DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7)
88340 {
88341#ifndef DR_MP3_FLOAT_OUTPUT
88342#if DRMP3_HAVE_SSE
88343 static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
88344 static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
88345 __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
88346 _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
88347 dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
88348 dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
88349 dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
88350 dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
88351 dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
88352 dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
88353 dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
88354 dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6);
88355#else
88356 int16x4_t pcma, pcmb;
88357 a = DRMP3_VADD(a, DRMP3_VSET(0.5f));
88358 b = DRMP3_VADD(b, DRMP3_VSET(0.5f));
88359 pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0)))));
88360 pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0)))));
88361 vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1);
88362 vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1);
88363 vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0);
88364 vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0);
88365 vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3);
88366 vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3);
88367 vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2);
88368 vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2);
88369#endif
88370#else
88371 static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f };
88372 a = DRMP3_VMUL(a, g_scale);
88373 b = DRMP3_VMUL(b, g_scale);
88374#if DRMP3_HAVE_SSE
88375 _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)));
88376 _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1)));
88377 _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)));
88378 _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0)));
88379 _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)));
88380 _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3)));
88381 _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)));
88382 _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2)));
88383#else
88384 vst1q_lane_f32(dstr + (15 - i)*nch, a, 1);
88385 vst1q_lane_f32(dstr + (17 + i)*nch, b, 1);
88386 vst1q_lane_f32(dstl + (15 - i)*nch, a, 0);
88387 vst1q_lane_f32(dstl + (17 + i)*nch, b, 0);
88388 vst1q_lane_f32(dstr + (47 - i)*nch, a, 3);
88389 vst1q_lane_f32(dstr + (49 + i)*nch, b, 3);
88390 vst1q_lane_f32(dstl + (47 - i)*nch, a, 2);
88391 vst1q_lane_f32(dstl + (49 + i)*nch, b, 2);
88392#endif
88393#endif
88394 }
88395 } else
88396#endif
88397#ifdef DR_MP3_ONLY_SIMD
88398 {}
88399#else
88400 for (i = 14; i >= 0; i--)
88401 {
88402#define DRMP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64];
88403#define DRMP3_S0(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; }
88404#define DRMP3_S1(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; }
88405#define DRMP3_S2(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; }
88406 float a[4], b[4];
88407 zlin[4*i] = xl[18*(31 - i)];
88408 zlin[4*i + 1] = xr[18*(31 - i)];
88409 zlin[4*i + 2] = xl[1 + 18*(31 - i)];
88410 zlin[4*i + 3] = xr[1 + 18*(31 - i)];
88411 zlin[4*(i + 16)] = xl[1 + 18*(1 + i)];
88412 zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)];
88413 zlin[4*(i - 16) + 2] = xl[18*(1 + i)];
88414 zlin[4*(i - 16) + 3] = xr[18*(1 + i)];
88415 DRMP3_S0(0) DRMP3_S2(1) DRMP3_S1(2) DRMP3_S2(3) DRMP3_S1(4) DRMP3_S2(5) DRMP3_S1(6) DRMP3_S2(7)
88416 dstr[(15 - i)*nch] = drmp3d_scale_pcm(a[1]);
88417 dstr[(17 + i)*nch] = drmp3d_scale_pcm(b[1]);
88418 dstl[(15 - i)*nch] = drmp3d_scale_pcm(a[0]);
88419 dstl[(17 + i)*nch] = drmp3d_scale_pcm(b[0]);
88420 dstr[(47 - i)*nch] = drmp3d_scale_pcm(a[3]);
88421 dstr[(49 + i)*nch] = drmp3d_scale_pcm(b[3]);
88422 dstl[(47 - i)*nch] = drmp3d_scale_pcm(a[2]);
88423 dstl[(49 + i)*nch] = drmp3d_scale_pcm(b[2]);
88424 }
88425#endif
88426}
88427static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, drmp3d_sample_t *pcm, float *lins)
88428{
88429 int i;
88430 for (i = 0; i < nch; i++)
88431 {
88432 drmp3d_DCT_II(grbuf + 576*i, nbands);
88433 }
88434 DRMP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64);
88435 for (i = 0; i < nbands; i += 2)
88436 {
88437 drmp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64);
88438 }
88439#ifndef DR_MP3_NONSTANDARD_BUT_LOGICAL
88440 if (nch == 1)
88441 {
88442 for (i = 0; i < 15*64; i += 2)
88443 {
88444 qmf_state[i] = lins[nbands*64 + i];
88445 }
88446 } else
88447#endif
88448 {
88449 DRMP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64);
88450 }
88451}
88452static int drmp3d_match_frame(const drmp3_uint8 *hdr, int mp3_bytes, int frame_bytes)
88453{
88454 int i, nmatch;
88455 for (i = 0, nmatch = 0; nmatch < DRMP3_MAX_FRAME_SYNC_MATCHES; nmatch++)
88456 {
88457 i += drmp3_hdr_frame_bytes(hdr + i, frame_bytes) + drmp3_hdr_padding(hdr + i);
88458 if (i + DRMP3_HDR_SIZE > mp3_bytes)
88459 return nmatch > 0;
88460 if (!drmp3_hdr_compare(hdr, hdr + i))
88461 return 0;
88462 }
88463 return 1;
88464}
88465static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes)
88466{
88467 int i, k;
88468 for (i = 0; i < mp3_bytes - DRMP3_HDR_SIZE; i++, mp3++)
88469 {
88470 if (drmp3_hdr_valid(mp3))
88471 {
88472 int frame_bytes = drmp3_hdr_frame_bytes(mp3, *free_format_bytes);
88473 int frame_and_padding = frame_bytes + drmp3_hdr_padding(mp3);
88474 for (k = DRMP3_HDR_SIZE; !frame_bytes && k < DRMP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - DRMP3_HDR_SIZE; k++)
88475 {
88476 if (drmp3_hdr_compare(mp3, mp3 + k))
88477 {
88478 int fb = k - drmp3_hdr_padding(mp3);
88479 int nextfb = fb + drmp3_hdr_padding(mp3 + k);
88480 if (i + k + nextfb + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + k + nextfb))
88481 continue;
88482 frame_and_padding = k;
88483 frame_bytes = fb;
88484 *free_format_bytes = fb;
88485 }
88486 }
88487 if ((frame_bytes && i + frame_and_padding <= mp3_bytes &&
88488 drmp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) ||
88489 (!i && frame_and_padding == mp3_bytes))
88490 {
88491 *ptr_frame_bytes = frame_and_padding;
88492 return i;
88493 }
88494 *free_format_bytes = 0;
88495 }
88496 }
88497 *ptr_frame_bytes = 0;
88498 return mp3_bytes;
88499}
88501{
88502 dec->header[0] = 0;
88503}
88504DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info)
88505{
88506 int i = 0, igr, frame_size = 0, success = 1;
88507 const drmp3_uint8 *hdr;
88508 drmp3_bs bs_frame[1];
88509 drmp3dec_scratch scratch;
88510 if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3))
88511 {
88512 frame_size = drmp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + drmp3_hdr_padding(mp3);
88513 if (frame_size != mp3_bytes && (frame_size + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + frame_size)))
88514 {
88515 frame_size = 0;
88516 }
88517 }
88518 if (!frame_size)
88519 {
88520 DRMP3_ZERO_MEMORY(dec, sizeof(drmp3dec));
88521 i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size);
88522 if (!frame_size || i + frame_size > mp3_bytes)
88523 {
88524 info->frame_bytes = i;
88525 return 0;
88526 }
88527 }
88528 hdr = mp3 + i;
88529 DRMP3_COPY_MEMORY(dec->header, hdr, DRMP3_HDR_SIZE);
88530 info->frame_bytes = i + frame_size;
88531 info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2;
88532 info->hz = drmp3_hdr_sample_rate_hz(hdr);
88533 info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr);
88534 info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr);
88535 drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE);
88536 if (DRMP3_HDR_IS_CRC(hdr))
88537 {
88538 drmp3_bs_get_bits(bs_frame, 16);
88539 }
88540 if (info->layer == 3)
88541 {
88542 int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr);
88543 if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit)
88544 {
88545 drmp3dec_init(dec);
88546 return 0;
88547 }
88548 success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin);
88549 if (success && pcm != NULL)
88550 {
88551 for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels))
88552 {
88553 DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
88554 drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels);
88555 drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]);
88556 }
88557 }
88558 drmp3_L3_save_reservoir(dec, &scratch);
88559 } else
88560 {
88561#ifdef DR_MP3_ONLY_MP3
88562 return 0;
88563#else
88564 drmp3_L12_scale_info sci[1];
88565 if (pcm == NULL) {
88566 return drmp3_hdr_frame_samples(hdr);
88567 }
88568 drmp3_L12_read_scale_info(hdr, bs_frame, sci);
88569 DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
88570 for (i = 0, igr = 0; igr < 3; igr++)
88571 {
88572 if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1)))
88573 {
88574 i = 0;
88575 drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]);
88576 drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]);
88577 DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
88578 pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels);
88579 }
88580 if (bs_frame->pos > bs_frame->limit)
88581 {
88582 drmp3dec_init(dec);
88583 return 0;
88584 }
88585 }
88586#endif
88587 }
88588 return success*drmp3_hdr_frame_samples(dec->header);
88589}
88590DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples)
88591{
88592 size_t i = 0;
88593#if DRMP3_HAVE_SIMD
88594 size_t aligned_count = num_samples & ~7;
88595 for(; i < aligned_count; i+=8)
88596 {
88597 drmp3_f4 scale = DRMP3_VSET(32768.0f);
88598 drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), scale);
88599 drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), scale);
88600#if DRMP3_HAVE_SSE
88601 drmp3_f4 s16max = DRMP3_VSET( 32767.0f);
88602 drmp3_f4 s16min = DRMP3_VSET(-32768.0f);
88603 __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)),
88604 _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min)));
88605 out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
88606 out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
88607 out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
88608 out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
88609 out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
88610 out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
88611 out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6);
88612 out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
88613#else
88614 int16x4_t pcma, pcmb;
88615 a = DRMP3_VADD(a, DRMP3_VSET(0.5f));
88616 b = DRMP3_VADD(b, DRMP3_VSET(0.5f));
88617 pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0)))));
88618 pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0)))));
88619 vst1_lane_s16(out+i , pcma, 0);
88620 vst1_lane_s16(out+i+1, pcma, 1);
88621 vst1_lane_s16(out+i+2, pcma, 2);
88622 vst1_lane_s16(out+i+3, pcma, 3);
88623 vst1_lane_s16(out+i+4, pcmb, 0);
88624 vst1_lane_s16(out+i+5, pcmb, 1);
88625 vst1_lane_s16(out+i+6, pcmb, 2);
88626 vst1_lane_s16(out+i+7, pcmb, 3);
88627#endif
88628 }
88629#endif
88630 for(; i < num_samples; i++)
88631 {
88632 float sample = in[i] * 32768.0f;
88633 if (sample >= 32766.5)
88634 out[i] = (drmp3_int16) 32767;
88635 else if (sample <= -32767.5)
88636 out[i] = (drmp3_int16)-32768;
88637 else
88638 {
88639 short s = (drmp3_int16)(sample + .5f);
88640 s -= (s < 0);
88641 out[i] = s;
88642 }
88643 }
88644}
88645#include <math.h>
88646#if defined(SIZE_MAX)
88647 #define DRMP3_SIZE_MAX SIZE_MAX
88648#else
88649 #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
88650 #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF)
88651 #else
88652 #define DRMP3_SIZE_MAX 0xFFFFFFFF
88653 #endif
88654#endif
88655#ifndef DRMP3_SEEK_LEADING_MP3_FRAMES
88656#define DRMP3_SEEK_LEADING_MP3_FRAMES 2
88657#endif
88658#define DRMP3_MIN_DATA_CHUNK_SIZE 16384
88659#ifndef DRMP3_DATA_CHUNK_SIZE
88660#define DRMP3_DATA_CHUNK_SIZE DRMP3_MIN_DATA_CHUNK_SIZE*4
88661#endif
88662#define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0]))
88663#define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi)))
88664#ifndef DRMP3_PI_D
88665#define DRMP3_PI_D 3.14159265358979323846264
88666#endif
88667#define DRMP3_DEFAULT_RESAMPLER_LPF_ORDER 2
88668static DRMP3_INLINE float drmp3_mix_f32(float x, float y, float a)
88669{
88670 return x*(1-a) + y*a;
88671}
88672static DRMP3_INLINE float drmp3_mix_f32_fast(float x, float y, float a)
88673{
88674 float r0 = (y - x);
88675 float r1 = r0*a;
88676 return x + r1;
88677}
88678static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b)
88679{
88680 for (;;) {
88681 if (b == 0) {
88682 break;
88683 } else {
88684 drmp3_uint32 t = a;
88685 a = b;
88686 b = t % a;
88687 }
88688 }
88689 return a;
88690}
88691static DRMP3_INLINE double drmp3_sin(double x)
88692{
88693 return sin(x);
88694}
88695static DRMP3_INLINE double drmp3_exp(double x)
88696{
88697 return exp(x);
88698}
88699static DRMP3_INLINE double drmp3_cos(double x)
88700{
88701 return drmp3_sin((DRMP3_PI_D*0.5) - x);
88702}
88703static void* drmp3__malloc_default(size_t sz, void* pUserData)
88704{
88705 (void)pUserData;
88706 return DRMP3_MALLOC(sz);
88707}
88708static void* drmp3__realloc_default(void* p, size_t sz, void* pUserData)
88709{
88710 (void)pUserData;
88711 return DRMP3_REALLOC(p, sz);
88712}
88713static void drmp3__free_default(void* p, void* pUserData)
88714{
88715 (void)pUserData;
88716 DRMP3_FREE(p);
88717}
88718static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks)
88719{
88720 if (pAllocationCallbacks == NULL) {
88721 return NULL;
88722 }
88723 if (pAllocationCallbacks->onMalloc != NULL) {
88724 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
88725 }
88726 if (pAllocationCallbacks->onRealloc != NULL) {
88727 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
88728 }
88729 return NULL;
88730}
88731static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drmp3_allocation_callbacks* pAllocationCallbacks)
88732{
88733 if (pAllocationCallbacks == NULL) {
88734 return NULL;
88735 }
88736 if (pAllocationCallbacks->onRealloc != NULL) {
88737 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
88738 }
88739 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
88740 void* p2;
88741 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
88742 if (p2 == NULL) {
88743 return NULL;
88744 }
88745 if (p != NULL) {
88746 DRMP3_COPY_MEMORY(p2, p, szOld);
88747 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
88748 }
88749 return p2;
88750 }
88751 return NULL;
88752}
88753static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks)
88754{
88755 if (p == NULL || pAllocationCallbacks == NULL) {
88756 return;
88757 }
88758 if (pAllocationCallbacks->onFree != NULL) {
88759 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
88760 }
88761}
88762static drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drmp3_allocation_callbacks* pAllocationCallbacks)
88763{
88764 if (pAllocationCallbacks != NULL) {
88765 return *pAllocationCallbacks;
88766 } else {
88767 drmp3_allocation_callbacks allocationCallbacks;
88768 allocationCallbacks.pUserData = NULL;
88769 allocationCallbacks.onMalloc = drmp3__malloc_default;
88770 allocationCallbacks.onRealloc = drmp3__realloc_default;
88771 allocationCallbacks.onFree = drmp3__free_default;
88772 return allocationCallbacks;
88773 }
88774}
88775static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead)
88776{
88777 size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead);
88778 pMP3->streamCursor += bytesRead;
88779 return bytesRead;
88780}
88781static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin)
88782{
88783 DRMP3_ASSERT(offset >= 0);
88784 if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) {
88785 return DRMP3_FALSE;
88786 }
88787 if (origin == drmp3_seek_origin_start) {
88788 pMP3->streamCursor = (drmp3_uint64)offset;
88789 } else {
88790 pMP3->streamCursor += offset;
88791 }
88792 return DRMP3_TRUE;
88793}
88794static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin)
88795{
88796 if (offset <= 0x7FFFFFFF) {
88797 return drmp3__on_seek(pMP3, (int)offset, origin);
88798 }
88799 if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) {
88800 return DRMP3_FALSE;
88801 }
88802 offset -= 0x7FFFFFFF;
88803 while (offset > 0) {
88804 if (offset <= 0x7FFFFFFF) {
88805 if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) {
88806 return DRMP3_FALSE;
88807 }
88808 offset = 0;
88809 } else {
88810 if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) {
88811 return DRMP3_FALSE;
88812 }
88813 offset -= 0x7FFFFFFF;
88814 }
88815 }
88816 return DRMP3_TRUE;
88817}
88818static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
88819{
88820 drmp3_uint32 pcmFramesRead = 0;
88821 DRMP3_ASSERT(pMP3 != NULL);
88822 DRMP3_ASSERT(pMP3->onRead != NULL);
88823 if (pMP3->atEnd) {
88824 return 0;
88825 }
88826 for (;;) {
88828 if (pMP3->dataSize < DRMP3_MIN_DATA_CHUNK_SIZE) {
88829 size_t bytesRead;
88830 if (pMP3->pData != NULL) {
88831 DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
88832 }
88833 pMP3->dataConsumed = 0;
88834 if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) {
88835 drmp3_uint8* pNewData;
88836 size_t newDataCap;
88837 newDataCap = DRMP3_DATA_CHUNK_SIZE;
88838 pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
88839 if (pNewData == NULL) {
88840 return 0;
88841 }
88842 pMP3->pData = pNewData;
88843 pMP3->dataCapacity = newDataCap;
88844 }
88845 bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
88846 if (bytesRead == 0) {
88847 if (pMP3->dataSize == 0) {
88848 pMP3->atEnd = DRMP3_TRUE;
88849 return 0;
88850 }
88851 }
88852 pMP3->dataSize += bytesRead;
88853 }
88854 if (pMP3->dataSize > INT_MAX) {
88855 pMP3->atEnd = DRMP3_TRUE;
88856 return 0;
88857 }
88858 DRMP3_ASSERT(pMP3->pData != NULL);
88859 DRMP3_ASSERT(pMP3->dataCapacity > 0);
88860 pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info);
88861 if (info.frame_bytes > 0) {
88862 pMP3->dataConsumed += (size_t)info.frame_bytes;
88863 pMP3->dataSize -= (size_t)info.frame_bytes;
88864 }
88865 if (pcmFramesRead > 0) {
88866 pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header);
88868 pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
88869 pMP3->mp3FrameChannels = info.channels;
88870 pMP3->mp3FrameSampleRate = info.hz;
88871 break;
88872 } else if (info.frame_bytes == 0) {
88873 size_t bytesRead;
88874 DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
88875 pMP3->dataConsumed = 0;
88876 if (pMP3->dataCapacity == pMP3->dataSize) {
88877 drmp3_uint8* pNewData;
88878 size_t newDataCap;
88879 newDataCap = pMP3->dataCapacity + DRMP3_DATA_CHUNK_SIZE;
88880 pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
88881 if (pNewData == NULL) {
88882 return 0;
88883 }
88884 pMP3->pData = pNewData;
88885 pMP3->dataCapacity = newDataCap;
88886 }
88887 bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
88888 if (bytesRead == 0) {
88889 pMP3->atEnd = DRMP3_TRUE;
88890 return 0;
88891 }
88892 pMP3->dataSize += bytesRead;
88893 }
88894 };
88895 return pcmFramesRead;
88896}
88897static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
88898{
88899 drmp3_uint32 pcmFramesRead = 0;
88901 DRMP3_ASSERT(pMP3 != NULL);
88902 DRMP3_ASSERT(pMP3->memory.pData != NULL);
88903 if (pMP3->atEnd) {
88904 return 0;
88905 }
88906 for (;;) {
88907 pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info);
88908 if (pcmFramesRead > 0) {
88909 pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header);
88911 pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
88912 pMP3->mp3FrameChannels = info.channels;
88913 pMP3->mp3FrameSampleRate = info.hz;
88914 break;
88915 } else if (info.frame_bytes > 0) {
88916 pMP3->memory.currentReadPos += (size_t)info.frame_bytes;
88917 } else {
88918 break;
88919 }
88920 }
88921 pMP3->memory.currentReadPos += (size_t)info.frame_bytes;
88922 return pcmFramesRead;
88923}
88924static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
88925{
88926 if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) {
88927 return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames);
88928 } else {
88929 return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames);
88930 }
88931}
88932static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3)
88933{
88934 DRMP3_ASSERT(pMP3 != NULL);
88935 return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames);
88936}
88937#if 0
88938static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3)
88939{
88940 drmp3_uint32 pcmFrameCount;
88941 DRMP3_ASSERT(pMP3 != NULL);
88942 pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL);
88943 if (pcmFrameCount == 0) {
88944 return 0;
88945 }
88946 pMP3->currentPCMFrame += pcmFrameCount;
88947 pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount;
88949 return pcmFrameCount;
88950}
88951#endif
88952static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks)
88953{
88954 DRMP3_ASSERT(pMP3 != NULL);
88955 DRMP3_ASSERT(onRead != NULL);
88956 drmp3dec_init(&pMP3->decoder);
88957 pMP3->onRead = onRead;
88958 pMP3->onSeek = onSeek;
88959 pMP3->pUserData = pUserData;
88960 pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
88962 return DRMP3_FALSE;
88963 }
88964 if (drmp3_decode_next_frame(pMP3) == 0) {
88965 drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
88966 return DRMP3_FALSE;
88967 }
88968 pMP3->channels = pMP3->mp3FrameChannels;
88969 pMP3->sampleRate = pMP3->mp3FrameSampleRate;
88970 return DRMP3_TRUE;
88971}
88972DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks)
88973{
88974 if (pMP3 == NULL || onRead == NULL) {
88975 return DRMP3_FALSE;
88976 }
88977 DRMP3_ZERO_OBJECT(pMP3);
88978 return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks);
88979}
88980static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
88981{
88982 drmp3* pMP3 = (drmp3*)pUserData;
88983 size_t bytesRemaining;
88984 DRMP3_ASSERT(pMP3 != NULL);
88985 DRMP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos);
88986 bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos;
88987 if (bytesToRead > bytesRemaining) {
88988 bytesToRead = bytesRemaining;
88989 }
88990 if (bytesToRead > 0) {
88991 DRMP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead);
88992 pMP3->memory.currentReadPos += bytesToRead;
88993 }
88994 return bytesToRead;
88995}
88996static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin)
88997{
88998 drmp3* pMP3 = (drmp3*)pUserData;
88999 DRMP3_ASSERT(pMP3 != NULL);
89000 if (origin == drmp3_seek_origin_current) {
89001 if (byteOffset > 0) {
89002 if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) {
89003 byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos);
89004 }
89005 } else {
89006 if (pMP3->memory.currentReadPos < (size_t)-byteOffset) {
89007 byteOffset = -(int)pMP3->memory.currentReadPos;
89008 }
89009 }
89010 pMP3->memory.currentReadPos += byteOffset;
89011 } else {
89012 if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) {
89013 pMP3->memory.currentReadPos = byteOffset;
89014 } else {
89015 pMP3->memory.currentReadPos = pMP3->memory.dataSize;
89016 }
89017 }
89018 return DRMP3_TRUE;
89019}
89020DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks)
89021{
89022 if (pMP3 == NULL) {
89023 return DRMP3_FALSE;
89024 }
89025 DRMP3_ZERO_OBJECT(pMP3);
89026 if (pData == NULL || dataSize == 0) {
89027 return DRMP3_FALSE;
89028 }
89029 pMP3->memory.pData = (const drmp3_uint8*)pData;
89030 pMP3->memory.dataSize = dataSize;
89031 pMP3->memory.currentReadPos = 0;
89032 return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pAllocationCallbacks);
89033}
89034#ifndef DR_MP3_NO_STDIO
89035#include <stdio.h>
89036#include <wchar.h>
89037#include <errno.h>
89038static drmp3_result drmp3_result_from_errno(int e)
89039{
89040 switch (e)
89041 {
89042 case 0: return DRMP3_SUCCESS;
89043 #ifdef EPERM
89044 case EPERM: return DRMP3_INVALID_OPERATION;
89045 #endif
89046 #ifdef ENOENT
89047 case ENOENT: return DRMP3_DOES_NOT_EXIST;
89048 #endif
89049 #ifdef ESRCH
89050 case ESRCH: return DRMP3_DOES_NOT_EXIST;
89051 #endif
89052 #ifdef EINTR
89053 case EINTR: return DRMP3_INTERRUPT;
89054 #endif
89055 #ifdef EIO
89056 case EIO: return DRMP3_IO_ERROR;
89057 #endif
89058 #ifdef ENXIO
89059 case ENXIO: return DRMP3_DOES_NOT_EXIST;
89060 #endif
89061 #ifdef E2BIG
89062 case E2BIG: return DRMP3_INVALID_ARGS;
89063 #endif
89064 #ifdef ENOEXEC
89065 case ENOEXEC: return DRMP3_INVALID_FILE;
89066 #endif
89067 #ifdef EBADF
89068 case EBADF: return DRMP3_INVALID_FILE;
89069 #endif
89070 #ifdef ECHILD
89071 case ECHILD: return DRMP3_ERROR;
89072 #endif
89073 #ifdef EAGAIN
89074 case EAGAIN: return DRMP3_UNAVAILABLE;
89075 #endif
89076 #ifdef ENOMEM
89077 case ENOMEM: return DRMP3_OUT_OF_MEMORY;
89078 #endif
89079 #ifdef EACCES
89080 case EACCES: return DRMP3_ACCESS_DENIED;
89081 #endif
89082 #ifdef EFAULT
89083 case EFAULT: return DRMP3_BAD_ADDRESS;
89084 #endif
89085 #ifdef ENOTBLK
89086 case ENOTBLK: return DRMP3_ERROR;
89087 #endif
89088 #ifdef EBUSY
89089 case EBUSY: return DRMP3_BUSY;
89090 #endif
89091 #ifdef EEXIST
89092 case EEXIST: return DRMP3_ALREADY_EXISTS;
89093 #endif
89094 #ifdef EXDEV
89095 case EXDEV: return DRMP3_ERROR;
89096 #endif
89097 #ifdef ENODEV
89098 case ENODEV: return DRMP3_DOES_NOT_EXIST;
89099 #endif
89100 #ifdef ENOTDIR
89101 case ENOTDIR: return DRMP3_NOT_DIRECTORY;
89102 #endif
89103 #ifdef EISDIR
89104 case EISDIR: return DRMP3_IS_DIRECTORY;
89105 #endif
89106 #ifdef EINVAL
89107 case EINVAL: return DRMP3_INVALID_ARGS;
89108 #endif
89109 #ifdef ENFILE
89110 case ENFILE: return DRMP3_TOO_MANY_OPEN_FILES;
89111 #endif
89112 #ifdef EMFILE
89113 case EMFILE: return DRMP3_TOO_MANY_OPEN_FILES;
89114 #endif
89115 #ifdef ENOTTY
89116 case ENOTTY: return DRMP3_INVALID_OPERATION;
89117 #endif
89118 #ifdef ETXTBSY
89119 case ETXTBSY: return DRMP3_BUSY;
89120 #endif
89121 #ifdef EFBIG
89122 case EFBIG: return DRMP3_TOO_BIG;
89123 #endif
89124 #ifdef ENOSPC
89125 case ENOSPC: return DRMP3_NO_SPACE;
89126 #endif
89127 #ifdef ESPIPE
89128 case ESPIPE: return DRMP3_BAD_SEEK;
89129 #endif
89130 #ifdef EROFS
89131 case EROFS: return DRMP3_ACCESS_DENIED;
89132 #endif
89133 #ifdef EMLINK
89134 case EMLINK: return DRMP3_TOO_MANY_LINKS;
89135 #endif
89136 #ifdef EPIPE
89137 case EPIPE: return DRMP3_BAD_PIPE;
89138 #endif
89139 #ifdef EDOM
89140 case EDOM: return DRMP3_OUT_OF_RANGE;
89141 #endif
89142 #ifdef ERANGE
89143 case ERANGE: return DRMP3_OUT_OF_RANGE;
89144 #endif
89145 #ifdef EDEADLK
89146 case EDEADLK: return DRMP3_DEADLOCK;
89147 #endif
89148 #ifdef ENAMETOOLONG
89149 case ENAMETOOLONG: return DRMP3_PATH_TOO_LONG;
89150 #endif
89151 #ifdef ENOLCK
89152 case ENOLCK: return DRMP3_ERROR;
89153 #endif
89154 #ifdef ENOSYS
89155 case ENOSYS: return DRMP3_NOT_IMPLEMENTED;
89156 #endif
89157 #ifdef ENOTEMPTY
89158 case ENOTEMPTY: return DRMP3_DIRECTORY_NOT_EMPTY;
89159 #endif
89160 #ifdef ELOOP
89161 case ELOOP: return DRMP3_TOO_MANY_LINKS;
89162 #endif
89163 #ifdef ENOMSG
89164 case ENOMSG: return DRMP3_NO_MESSAGE;
89165 #endif
89166 #ifdef EIDRM
89167 case EIDRM: return DRMP3_ERROR;
89168 #endif
89169 #ifdef ECHRNG
89170 case ECHRNG: return DRMP3_ERROR;
89171 #endif
89172 #ifdef EL2NSYNC
89173 case EL2NSYNC: return DRMP3_ERROR;
89174 #endif
89175 #ifdef EL3HLT
89176 case EL3HLT: return DRMP3_ERROR;
89177 #endif
89178 #ifdef EL3RST
89179 case EL3RST: return DRMP3_ERROR;
89180 #endif
89181 #ifdef ELNRNG
89182 case ELNRNG: return DRMP3_OUT_OF_RANGE;
89183 #endif
89184 #ifdef EUNATCH
89185 case EUNATCH: return DRMP3_ERROR;
89186 #endif
89187 #ifdef ENOCSI
89188 case ENOCSI: return DRMP3_ERROR;
89189 #endif
89190 #ifdef EL2HLT
89191 case EL2HLT: return DRMP3_ERROR;
89192 #endif
89193 #ifdef EBADE
89194 case EBADE: return DRMP3_ERROR;
89195 #endif
89196 #ifdef EBADR
89197 case EBADR: return DRMP3_ERROR;
89198 #endif
89199 #ifdef EXFULL
89200 case EXFULL: return DRMP3_ERROR;
89201 #endif
89202 #ifdef ENOANO
89203 case ENOANO: return DRMP3_ERROR;
89204 #endif
89205 #ifdef EBADRQC
89206 case EBADRQC: return DRMP3_ERROR;
89207 #endif
89208 #ifdef EBADSLT
89209 case EBADSLT: return DRMP3_ERROR;
89210 #endif
89211 #ifdef EBFONT
89212 case EBFONT: return DRMP3_INVALID_FILE;
89213 #endif
89214 #ifdef ENOSTR
89215 case ENOSTR: return DRMP3_ERROR;
89216 #endif
89217 #ifdef ENODATA
89218 case ENODATA: return DRMP3_NO_DATA_AVAILABLE;
89219 #endif
89220 #ifdef ETIME
89221 case ETIME: return DRMP3_TIMEOUT;
89222 #endif
89223 #ifdef ENOSR
89224 case ENOSR: return DRMP3_NO_DATA_AVAILABLE;
89225 #endif
89226 #ifdef ENONET
89227 case ENONET: return DRMP3_NO_NETWORK;
89228 #endif
89229 #ifdef ENOPKG
89230 case ENOPKG: return DRMP3_ERROR;
89231 #endif
89232 #ifdef EREMOTE
89233 case EREMOTE: return DRMP3_ERROR;
89234 #endif
89235 #ifdef ENOLINK
89236 case ENOLINK: return DRMP3_ERROR;
89237 #endif
89238 #ifdef EADV
89239 case EADV: return DRMP3_ERROR;
89240 #endif
89241 #ifdef ESRMNT
89242 case ESRMNT: return DRMP3_ERROR;
89243 #endif
89244 #ifdef ECOMM
89245 case ECOMM: return DRMP3_ERROR;
89246 #endif
89247 #ifdef EPROTO
89248 case EPROTO: return DRMP3_ERROR;
89249 #endif
89250 #ifdef EMULTIHOP
89251 case EMULTIHOP: return DRMP3_ERROR;
89252 #endif
89253 #ifdef EDOTDOT
89254 case EDOTDOT: return DRMP3_ERROR;
89255 #endif
89256 #ifdef EBADMSG
89257 case EBADMSG: return DRMP3_BAD_MESSAGE;
89258 #endif
89259 #ifdef EOVERFLOW
89260 case EOVERFLOW: return DRMP3_TOO_BIG;
89261 #endif
89262 #ifdef ENOTUNIQ
89263 case ENOTUNIQ: return DRMP3_NOT_UNIQUE;
89264 #endif
89265 #ifdef EBADFD
89266 case EBADFD: return DRMP3_ERROR;
89267 #endif
89268 #ifdef EREMCHG
89269 case EREMCHG: return DRMP3_ERROR;
89270 #endif
89271 #ifdef ELIBACC
89272 case ELIBACC: return DRMP3_ACCESS_DENIED;
89273 #endif
89274 #ifdef ELIBBAD
89275 case ELIBBAD: return DRMP3_INVALID_FILE;
89276 #endif
89277 #ifdef ELIBSCN
89278 case ELIBSCN: return DRMP3_INVALID_FILE;
89279 #endif
89280 #ifdef ELIBMAX
89281 case ELIBMAX: return DRMP3_ERROR;
89282 #endif
89283 #ifdef ELIBEXEC
89284 case ELIBEXEC: return DRMP3_ERROR;
89285 #endif
89286 #ifdef EILSEQ
89287 case EILSEQ: return DRMP3_INVALID_DATA;
89288 #endif
89289 #ifdef ERESTART
89290 case ERESTART: return DRMP3_ERROR;
89291 #endif
89292 #ifdef ESTRPIPE
89293 case ESTRPIPE: return DRMP3_ERROR;
89294 #endif
89295 #ifdef EUSERS
89296 case EUSERS: return DRMP3_ERROR;
89297 #endif
89298 #ifdef ENOTSOCK
89299 case ENOTSOCK: return DRMP3_NOT_SOCKET;
89300 #endif
89301 #ifdef EDESTADDRREQ
89302 case EDESTADDRREQ: return DRMP3_NO_ADDRESS;
89303 #endif
89304 #ifdef EMSGSIZE
89305 case EMSGSIZE: return DRMP3_TOO_BIG;
89306 #endif
89307 #ifdef EPROTOTYPE
89308 case EPROTOTYPE: return DRMP3_BAD_PROTOCOL;
89309 #endif
89310 #ifdef ENOPROTOOPT
89311 case ENOPROTOOPT: return DRMP3_PROTOCOL_UNAVAILABLE;
89312 #endif
89313 #ifdef EPROTONOSUPPORT
89314 case EPROTONOSUPPORT: return DRMP3_PROTOCOL_NOT_SUPPORTED;
89315 #endif
89316 #ifdef ESOCKTNOSUPPORT
89317 case ESOCKTNOSUPPORT: return DRMP3_SOCKET_NOT_SUPPORTED;
89318 #endif
89319 #ifdef EOPNOTSUPP
89320 case EOPNOTSUPP: return DRMP3_INVALID_OPERATION;
89321 #endif
89322 #ifdef EPFNOSUPPORT
89323 case EPFNOSUPPORT: return DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED;
89324 #endif
89325 #ifdef EAFNOSUPPORT
89326 case EAFNOSUPPORT: return DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED;
89327 #endif
89328 #ifdef EADDRINUSE
89329 case EADDRINUSE: return DRMP3_ALREADY_IN_USE;
89330 #endif
89331 #ifdef EADDRNOTAVAIL
89332 case EADDRNOTAVAIL: return DRMP3_ERROR;
89333 #endif
89334 #ifdef ENETDOWN
89335 case ENETDOWN: return DRMP3_NO_NETWORK;
89336 #endif
89337 #ifdef ENETUNREACH
89338 case ENETUNREACH: return DRMP3_NO_NETWORK;
89339 #endif
89340 #ifdef ENETRESET
89341 case ENETRESET: return DRMP3_NO_NETWORK;
89342 #endif
89343 #ifdef ECONNABORTED
89344 case ECONNABORTED: return DRMP3_NO_NETWORK;
89345 #endif
89346 #ifdef ECONNRESET
89347 case ECONNRESET: return DRMP3_CONNECTION_RESET;
89348 #endif
89349 #ifdef ENOBUFS
89350 case ENOBUFS: return DRMP3_NO_SPACE;
89351 #endif
89352 #ifdef EISCONN
89353 case EISCONN: return DRMP3_ALREADY_CONNECTED;
89354 #endif
89355 #ifdef ENOTCONN
89356 case ENOTCONN: return DRMP3_NOT_CONNECTED;
89357 #endif
89358 #ifdef ESHUTDOWN
89359 case ESHUTDOWN: return DRMP3_ERROR;
89360 #endif
89361 #ifdef ETOOMANYREFS
89362 case ETOOMANYREFS: return DRMP3_ERROR;
89363 #endif
89364 #ifdef ETIMEDOUT
89365 case ETIMEDOUT: return DRMP3_TIMEOUT;
89366 #endif
89367 #ifdef ECONNREFUSED
89368 case ECONNREFUSED: return DRMP3_CONNECTION_REFUSED;
89369 #endif
89370 #ifdef EHOSTDOWN
89371 case EHOSTDOWN: return DRMP3_NO_HOST;
89372 #endif
89373 #ifdef EHOSTUNREACH
89374 case EHOSTUNREACH: return DRMP3_NO_HOST;
89375 #endif
89376 #ifdef EALREADY
89377 case EALREADY: return DRMP3_IN_PROGRESS;
89378 #endif
89379 #ifdef EINPROGRESS
89380 case EINPROGRESS: return DRMP3_IN_PROGRESS;
89381 #endif
89382 #ifdef ESTALE
89383 case ESTALE: return DRMP3_INVALID_FILE;
89384 #endif
89385 #ifdef EUCLEAN
89386 case EUCLEAN: return DRMP3_ERROR;
89387 #endif
89388 #ifdef ENOTNAM
89389 case ENOTNAM: return DRMP3_ERROR;
89390 #endif
89391 #ifdef ENAVAIL
89392 case ENAVAIL: return DRMP3_ERROR;
89393 #endif
89394 #ifdef EISNAM
89395 case EISNAM: return DRMP3_ERROR;
89396 #endif
89397 #ifdef EREMOTEIO
89398 case EREMOTEIO: return DRMP3_IO_ERROR;
89399 #endif
89400 #ifdef EDQUOT
89401 case EDQUOT: return DRMP3_NO_SPACE;
89402 #endif
89403 #ifdef ENOMEDIUM
89404 case ENOMEDIUM: return DRMP3_DOES_NOT_EXIST;
89405 #endif
89406 #ifdef EMEDIUMTYPE
89407 case EMEDIUMTYPE: return DRMP3_ERROR;
89408 #endif
89409 #ifdef ECANCELED
89410 case ECANCELED: return DRMP3_CANCELLED;
89411 #endif
89412 #ifdef ENOKEY
89413 case ENOKEY: return DRMP3_ERROR;
89414 #endif
89415 #ifdef EKEYEXPIRED
89416 case EKEYEXPIRED: return DRMP3_ERROR;
89417 #endif
89418 #ifdef EKEYREVOKED
89419 case EKEYREVOKED: return DRMP3_ERROR;
89420 #endif
89421 #ifdef EKEYREJECTED
89422 case EKEYREJECTED: return DRMP3_ERROR;
89423 #endif
89424 #ifdef EOWNERDEAD
89425 case EOWNERDEAD: return DRMP3_ERROR;
89426 #endif
89427 #ifdef ENOTRECOVERABLE
89428 case ENOTRECOVERABLE: return DRMP3_ERROR;
89429 #endif
89430 #ifdef ERFKILL
89431 case ERFKILL: return DRMP3_ERROR;
89432 #endif
89433 #ifdef EHWPOISON
89434 case EHWPOISON: return DRMP3_ERROR;
89435 #endif
89436 default: return DRMP3_ERROR;
89437 }
89438}
89439static drmp3_result drmp3_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
89440{
89441#if defined(_MSC_VER) && _MSC_VER >= 1400
89442 errno_t err;
89443#endif
89444 if (ppFile != NULL) {
89445 *ppFile = NULL;
89446 }
89447 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
89448 return DRMP3_INVALID_ARGS;
89449 }
89450#if defined(_MSC_VER) && _MSC_VER >= 1400
89451 err = fopen_s(ppFile, pFilePath, pOpenMode);
89452 if (err != 0) {
89453 return drmp3_result_from_errno(err);
89454 }
89455#else
89456#if defined(_WIN32) || defined(__APPLE__)
89457 *ppFile = fopen(pFilePath, pOpenMode);
89458#else
89459 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
89460 *ppFile = fopen64(pFilePath, pOpenMode);
89461 #else
89462 *ppFile = fopen(pFilePath, pOpenMode);
89463 #endif
89464#endif
89465 if (*ppFile == NULL) {
89466 drmp3_result result = drmp3_result_from_errno(errno);
89467 if (result == DRMP3_SUCCESS) {
89468 result = DRMP3_ERROR;
89469 }
89470 return result;
89471 }
89472#endif
89473 return DRMP3_SUCCESS;
89474}
89475#if defined(_WIN32)
89476 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
89477 #define DRMP3_HAS_WFOPEN
89478 #endif
89479#endif
89480static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drmp3_allocation_callbacks* pAllocationCallbacks)
89481{
89482 if (ppFile != NULL) {
89483 *ppFile = NULL;
89484 }
89485 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
89486 return DRMP3_INVALID_ARGS;
89487 }
89488#if defined(DRMP3_HAS_WFOPEN)
89489 {
89490 #if defined(_MSC_VER) && _MSC_VER >= 1400
89491 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
89492 if (err != 0) {
89493 return drmp3_result_from_errno(err);
89494 }
89495 #else
89496 *ppFile = _wfopen(pFilePath, pOpenMode);
89497 if (*ppFile == NULL) {
89498 return drmp3_result_from_errno(errno);
89499 }
89500 #endif
89501 (void)pAllocationCallbacks;
89502 }
89503#else
89504 {
89505 mbstate_t mbs;
89506 size_t lenMB;
89507 const wchar_t* pFilePathTemp = pFilePath;
89508 char* pFilePathMB = NULL;
89509 char pOpenModeMB[32] = {0};
89510 DRMP3_ZERO_OBJECT(&mbs);
89511 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
89512 if (lenMB == (size_t)-1) {
89513 return drmp3_result_from_errno(errno);
89514 }
89515 pFilePathMB = (char*)drmp3__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
89516 if (pFilePathMB == NULL) {
89517 return DRMP3_OUT_OF_MEMORY;
89518 }
89519 pFilePathTemp = pFilePath;
89520 DRMP3_ZERO_OBJECT(&mbs);
89521 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
89522 {
89523 size_t i = 0;
89524 for (;;) {
89525 if (pOpenMode[i] == 0) {
89526 pOpenModeMB[i] = '\0';
89527 break;
89528 }
89529 pOpenModeMB[i] = (char)pOpenMode[i];
89530 i += 1;
89531 }
89532 }
89533 *ppFile = fopen(pFilePathMB, pOpenModeMB);
89534 drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
89535 }
89536 if (*ppFile == NULL) {
89537 return DRMP3_ERROR;
89538 }
89539#endif
89540 return DRMP3_SUCCESS;
89541}
89542static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
89543{
89544 return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
89545}
89546static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin)
89547{
89548 return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
89549}
89550DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
89551{
89552 drmp3_bool32 result;
89553 FILE* pFile;
89554 if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) {
89555 return DRMP3_FALSE;
89556 }
89557 result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
89558 if (result != DRMP3_TRUE) {
89559 fclose(pFile);
89560 return result;
89561 }
89562 return DRMP3_TRUE;
89563}
89564DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
89565{
89566 drmp3_bool32 result;
89567 FILE* pFile;
89568 if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) {
89569 return DRMP3_FALSE;
89570 }
89571 result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
89572 if (result != DRMP3_TRUE) {
89573 fclose(pFile);
89574 return result;
89575 }
89576 return DRMP3_TRUE;
89577}
89578#endif
89579DRMP3_API void drmp3_uninit(drmp3* pMP3)
89580{
89581 if (pMP3 == NULL) {
89582 return;
89583 }
89584#ifndef DR_MP3_NO_STDIO
89585 if (pMP3->onRead == drmp3__on_read_stdio) {
89586 FILE* pFile = (FILE*)pMP3->pUserData;
89587 if (pFile != NULL) {
89588 fclose(pFile);
89589 pMP3->pUserData = NULL;
89590 }
89591 }
89592#endif
89593 drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
89594}
89595#if defined(DR_MP3_FLOAT_OUTPUT)
89596static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sampleCount)
89597{
89598 drmp3_uint64 i;
89599 drmp3_uint64 i4;
89600 drmp3_uint64 sampleCount4;
89601 i = 0;
89602 sampleCount4 = sampleCount >> 2;
89603 for (i4 = 0; i4 < sampleCount4; i4 += 1) {
89604 float x0 = src[i+0];
89605 float x1 = src[i+1];
89606 float x2 = src[i+2];
89607 float x3 = src[i+3];
89608 x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
89609 x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
89610 x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
89611 x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
89612 x0 = x0 * 32767.0f;
89613 x1 = x1 * 32767.0f;
89614 x2 = x2 * 32767.0f;
89615 x3 = x3 * 32767.0f;
89616 dst[i+0] = (drmp3_int16)x0;
89617 dst[i+1] = (drmp3_int16)x1;
89618 dst[i+2] = (drmp3_int16)x2;
89619 dst[i+3] = (drmp3_int16)x3;
89620 i += 4;
89621 }
89622 for (; i < sampleCount; i += 1) {
89623 float x = src[i];
89624 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
89625 x = x * 32767.0f;
89626 dst[i] = (drmp3_int16)x;
89627 }
89628}
89629#endif
89630#if !defined(DR_MP3_FLOAT_OUTPUT)
89631static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sampleCount)
89632{
89633 drmp3_uint64 i;
89634 for (i = 0; i < sampleCount; i += 1) {
89635 float x = (float)src[i];
89636 x = x * 0.000030517578125f;
89637 dst[i] = x;
89638 }
89639}
89640#endif
89641static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesToRead, void* pBufferOut)
89642{
89643 drmp3_uint64 totalFramesRead = 0;
89644 DRMP3_ASSERT(pMP3 != NULL);
89645 DRMP3_ASSERT(pMP3->onRead != NULL);
89646 while (framesToRead > 0) {
89647 drmp3_uint32 framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead);
89648 if (pBufferOut != NULL) {
89649 #if defined(DR_MP3_FLOAT_OUTPUT)
89650 float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels);
89651 float* pFramesInF32 = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
89652 DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels);
89653 #else
89654 drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalFramesRead * pMP3->channels);
89655 drmp3_int16* pFramesInS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
89656 DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels);
89657 #endif
89658 }
89659 pMP3->currentPCMFrame += framesToConsume;
89660 pMP3->pcmFramesConsumedInMP3Frame += framesToConsume;
89661 pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume;
89662 totalFramesRead += framesToConsume;
89663 framesToRead -= framesToConsume;
89664 if (framesToRead == 0) {
89665 break;
89666 }
89667 DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0);
89668 if (drmp3_decode_next_frame(pMP3) == 0) {
89669 break;
89670 }
89671 }
89672 return totalFramesRead;
89673}
89674DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut)
89675{
89676 if (pMP3 == NULL || pMP3->onRead == NULL) {
89677 return 0;
89678 }
89679#if defined(DR_MP3_FLOAT_OUTPUT)
89680 return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
89681#else
89682 {
89683 drmp3_int16 pTempS16[8192];
89684 drmp3_uint64 totalPCMFramesRead = 0;
89685 while (totalPCMFramesRead < framesToRead) {
89686 drmp3_uint64 framesJustRead;
89687 drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
89688 drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels;
89689 if (framesToReadNow > framesRemaining) {
89690 framesToReadNow = framesRemaining;
89691 }
89692 framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16);
89693 if (framesJustRead == 0) {
89694 break;
89695 }
89696 drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels);
89697 totalPCMFramesRead += framesJustRead;
89698 }
89699 return totalPCMFramesRead;
89700 }
89701#endif
89702}
89704{
89705 if (pMP3 == NULL || pMP3->onRead == NULL) {
89706 return 0;
89707 }
89708#if !defined(DR_MP3_FLOAT_OUTPUT)
89709 return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
89710#else
89711 {
89712 float pTempF32[4096];
89713 drmp3_uint64 totalPCMFramesRead = 0;
89714 while (totalPCMFramesRead < framesToRead) {
89715 drmp3_uint64 framesJustRead;
89716 drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
89717 drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels;
89718 if (framesToReadNow > framesRemaining) {
89719 framesToReadNow = framesRemaining;
89720 }
89721 framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32);
89722 if (framesJustRead == 0) {
89723 break;
89724 }
89725 drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels);
89726 totalPCMFramesRead += framesJustRead;
89727 }
89728 return totalPCMFramesRead;
89729 }
89730#endif
89731}
89732static void drmp3_reset(drmp3* pMP3)
89733{
89734 DRMP3_ASSERT(pMP3 != NULL);
89737 pMP3->currentPCMFrame = 0;
89738 pMP3->dataSize = 0;
89739 pMP3->atEnd = DRMP3_FALSE;
89740 drmp3dec_init(&pMP3->decoder);
89741}
89742static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3)
89743{
89744 DRMP3_ASSERT(pMP3 != NULL);
89745 DRMP3_ASSERT(pMP3->onSeek != NULL);
89746 if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) {
89747 return DRMP3_FALSE;
89748 }
89749 drmp3_reset(pMP3);
89750 return DRMP3_TRUE;
89751}
89752static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset)
89753{
89754 drmp3_uint64 framesRead;
89755#if defined(DR_MP3_FLOAT_OUTPUT)
89756 framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL);
89757#else
89758 framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL);
89759#endif
89760 if (framesRead != frameOffset) {
89761 return DRMP3_FALSE;
89762 }
89763 return DRMP3_TRUE;
89764}
89765static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex)
89766{
89767 DRMP3_ASSERT(pMP3 != NULL);
89768 if (frameIndex == pMP3->currentPCMFrame) {
89769 return DRMP3_TRUE;
89770 }
89771 if (frameIndex < pMP3->currentPCMFrame) {
89772 if (!drmp3_seek_to_start_of_stream(pMP3)) {
89773 return DRMP3_FALSE;
89774 }
89775 }
89776 DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame);
89777 return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame));
89778}
89779static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex)
89780{
89781 drmp3_uint32 iSeekPoint;
89782 DRMP3_ASSERT(pSeekPointIndex != NULL);
89783 *pSeekPointIndex = 0;
89784 if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) {
89785 return DRMP3_FALSE;
89786 }
89787 for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) {
89788 if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) {
89789 break;
89790 }
89791 *pSeekPointIndex = iSeekPoint;
89792 }
89793 return DRMP3_TRUE;
89794}
89795static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex)
89796{
89797 drmp3_seek_point seekPoint;
89798 drmp3_uint32 priorSeekPointIndex;
89799 drmp3_uint16 iMP3Frame;
89800 drmp3_uint64 leftoverFrames;
89801 DRMP3_ASSERT(pMP3 != NULL);
89802 DRMP3_ASSERT(pMP3->pSeekPoints != NULL);
89803 DRMP3_ASSERT(pMP3->seekPointCount > 0);
89804 if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) {
89805 seekPoint = pMP3->pSeekPoints[priorSeekPointIndex];
89806 } else {
89807 seekPoint.seekPosInBytes = 0;
89808 seekPoint.pcmFrameIndex = 0;
89809 seekPoint.mp3FramesToDiscard = 0;
89810 seekPoint.pcmFramesToDiscard = 0;
89811 }
89812 if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) {
89813 return DRMP3_FALSE;
89814 }
89815 drmp3_reset(pMP3);
89816 for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) {
89817 drmp3_uint32 pcmFramesRead;
89818 drmp3d_sample_t* pPCMFrames;
89819 pPCMFrames = NULL;
89820 if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) {
89821 pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames;
89822 }
89823 pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames);
89824 if (pcmFramesRead == 0) {
89825 return DRMP3_FALSE;
89826 }
89827 }
89828 pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard;
89829 leftoverFrames = frameIndex - pMP3->currentPCMFrame;
89830 return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames);
89831}
89833{
89834 if (pMP3 == NULL || pMP3->onSeek == NULL) {
89835 return DRMP3_FALSE;
89836 }
89837 if (frameIndex == 0) {
89838 return drmp3_seek_to_start_of_stream(pMP3);
89839 }
89840 if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) {
89841 return drmp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex);
89842 } else {
89843 return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex);
89844 }
89845}
89847{
89848 drmp3_uint64 currentPCMFrame;
89849 drmp3_uint64 totalPCMFrameCount;
89850 drmp3_uint64 totalMP3FrameCount;
89851 if (pMP3 == NULL) {
89852 return DRMP3_FALSE;
89853 }
89854 if (pMP3->onSeek == NULL) {
89855 return DRMP3_FALSE;
89856 }
89857 currentPCMFrame = pMP3->currentPCMFrame;
89858 if (!drmp3_seek_to_start_of_stream(pMP3)) {
89859 return DRMP3_FALSE;
89860 }
89861 totalPCMFrameCount = 0;
89862 totalMP3FrameCount = 0;
89863 for (;;) {
89864 drmp3_uint32 pcmFramesInCurrentMP3Frame;
89865 pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL);
89866 if (pcmFramesInCurrentMP3Frame == 0) {
89867 break;
89868 }
89869 totalPCMFrameCount += pcmFramesInCurrentMP3Frame;
89870 totalMP3FrameCount += 1;
89871 }
89872 if (!drmp3_seek_to_start_of_stream(pMP3)) {
89873 return DRMP3_FALSE;
89874 }
89875 if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
89876 return DRMP3_FALSE;
89877 }
89878 if (pMP3FrameCount != NULL) {
89879 *pMP3FrameCount = totalMP3FrameCount;
89880 }
89881 if (pPCMFrameCount != NULL) {
89882 *pPCMFrameCount = totalPCMFrameCount;
89883 }
89884 return DRMP3_TRUE;
89885}
89887{
89888 drmp3_uint64 totalPCMFrameCount;
89889 if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) {
89890 return 0;
89891 }
89892 return totalPCMFrameCount;
89893}
89895{
89896 drmp3_uint64 totalMP3FrameCount;
89897 if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) {
89898 return 0;
89899 }
89900 return totalMP3FrameCount;
89901}
89902static void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart)
89903{
89904 float srcRatio;
89905 float pcmFrameCountOutF;
89906 drmp3_uint32 pcmFrameCountOut;
89907 srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate;
89908 DRMP3_ASSERT(srcRatio > 0);
89909 pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio);
89910 pcmFrameCountOut = (drmp3_uint32)pcmFrameCountOutF;
89911 *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut;
89912 *pRunningPCMFrameCount += pcmFrameCountOut;
89913}
89914typedef struct
89915{
89916 drmp3_uint64 bytePos;
89917 drmp3_uint64 pcmFrameIndex;
89918} drmp3__seeking_mp3_frame_info;
89920{
89921 drmp3_uint32 seekPointCount;
89922 drmp3_uint64 currentPCMFrame;
89923 drmp3_uint64 totalMP3FrameCount;
89924 drmp3_uint64 totalPCMFrameCount;
89925 if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) {
89926 return DRMP3_FALSE;
89927 }
89928 seekPointCount = *pSeekPointCount;
89929 if (seekPointCount == 0) {
89930 return DRMP3_FALSE;
89931 }
89932 currentPCMFrame = pMP3->currentPCMFrame;
89933 if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) {
89934 return DRMP3_FALSE;
89935 }
89936 if (totalMP3FrameCount < DRMP3_SEEK_LEADING_MP3_FRAMES+1) {
89937 seekPointCount = 1;
89938 pSeekPoints[0].seekPosInBytes = 0;
89939 pSeekPoints[0].pcmFrameIndex = 0;
89940 pSeekPoints[0].mp3FramesToDiscard = 0;
89941 pSeekPoints[0].pcmFramesToDiscard = 0;
89942 } else {
89943 drmp3_uint64 pcmFramesBetweenSeekPoints;
89944 drmp3__seeking_mp3_frame_info mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES+1];
89945 drmp3_uint64 runningPCMFrameCount = 0;
89946 float runningPCMFrameCountFractionalPart = 0;
89947 drmp3_uint64 nextTargetPCMFrame;
89948 drmp3_uint32 iMP3Frame;
89949 drmp3_uint32 iSeekPoint;
89950 if (seekPointCount > totalMP3FrameCount-1) {
89951 seekPointCount = (drmp3_uint32)totalMP3FrameCount-1;
89952 }
89953 pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1);
89954 if (!drmp3_seek_to_start_of_stream(pMP3)) {
89955 return DRMP3_FALSE;
89956 }
89957 for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) {
89958 drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
89959 DRMP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize);
89960 mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize;
89961 mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount;
89962 pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL);
89963 if (pcmFramesInCurrentMP3FrameIn == 0) {
89964 return DRMP3_FALSE;
89965 }
89966 drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
89967 }
89968 nextTargetPCMFrame = 0;
89969 for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) {
89970 nextTargetPCMFrame += pcmFramesBetweenSeekPoints;
89971 for (;;) {
89972 if (nextTargetPCMFrame < runningPCMFrameCount) {
89973 pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
89974 pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
89975 pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES;
89976 pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
89977 break;
89978 } else {
89979 size_t i;
89980 drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
89981 for (i = 0; i < DRMP3_COUNTOF(mp3FrameInfo)-1; ++i) {
89982 mp3FrameInfo[i] = mp3FrameInfo[i+1];
89983 }
89984 mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize;
89985 mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount;
89986 pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL);
89987 if (pcmFramesInCurrentMP3FrameIn == 0) {
89988 pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
89989 pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
89990 pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES;
89991 pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
89992 break;
89993 }
89994 drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
89995 }
89996 }
89997 }
89998 if (!drmp3_seek_to_start_of_stream(pMP3)) {
89999 return DRMP3_FALSE;
90000 }
90001 if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
90002 return DRMP3_FALSE;
90003 }
90004 }
90005 *pSeekPointCount = seekPointCount;
90006 return DRMP3_TRUE;
90007}
90009{
90010 if (pMP3 == NULL) {
90011 return DRMP3_FALSE;
90012 }
90013 if (seekPointCount == 0 || pSeekPoints == NULL) {
90014 pMP3->seekPointCount = 0;
90015 pMP3->pSeekPoints = NULL;
90016 } else {
90017 pMP3->seekPointCount = seekPointCount;
90018 pMP3->pSeekPoints = pSeekPoints;
90019 }
90020 return DRMP3_TRUE;
90021}
90022static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
90023{
90024 drmp3_uint64 totalFramesRead = 0;
90025 drmp3_uint64 framesCapacity = 0;
90026 float* pFrames = NULL;
90027 float temp[4096];
90028 DRMP3_ASSERT(pMP3 != NULL);
90029 for (;;) {
90030 drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels;
90031 drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);
90032 if (framesJustRead == 0) {
90033 break;
90034 }
90035 if (framesCapacity < totalFramesRead + framesJustRead) {
90036 drmp3_uint64 oldFramesBufferSize;
90037 drmp3_uint64 newFramesBufferSize;
90038 drmp3_uint64 newFramesCap;
90039 float* pNewFrames;
90040 newFramesCap = framesCapacity * 2;
90041 if (newFramesCap < totalFramesRead + framesJustRead) {
90042 newFramesCap = totalFramesRead + framesJustRead;
90043 }
90044 oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float);
90045 newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float);
90046 if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) {
90047 break;
90048 }
90049 pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
90050 if (pNewFrames == NULL) {
90051 drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
90052 break;
90053 }
90054 pFrames = pNewFrames;
90055 framesCapacity = newFramesCap;
90056 }
90057 DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float)));
90058 totalFramesRead += framesJustRead;
90059 if (framesJustRead != framesToReadRightNow) {
90060 break;
90061 }
90062 }
90063 if (pConfig != NULL) {
90064 pConfig->channels = pMP3->channels;
90065 pConfig->sampleRate = pMP3->sampleRate;
90066 }
90067 drmp3_uninit(pMP3);
90068 if (pTotalFrameCount) {
90069 *pTotalFrameCount = totalFramesRead;
90070 }
90071 return pFrames;
90072}
90073static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
90074{
90075 drmp3_uint64 totalFramesRead = 0;
90076 drmp3_uint64 framesCapacity = 0;
90077 drmp3_int16* pFrames = NULL;
90078 drmp3_int16 temp[4096];
90079 DRMP3_ASSERT(pMP3 != NULL);
90080 for (;;) {
90081 drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels;
90082 drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp);
90083 if (framesJustRead == 0) {
90084 break;
90085 }
90086 if (framesCapacity < totalFramesRead + framesJustRead) {
90087 drmp3_uint64 newFramesBufferSize;
90088 drmp3_uint64 oldFramesBufferSize;
90089 drmp3_uint64 newFramesCap;
90090 drmp3_int16* pNewFrames;
90091 newFramesCap = framesCapacity * 2;
90092 if (newFramesCap < totalFramesRead + framesJustRead) {
90093 newFramesCap = totalFramesRead + framesJustRead;
90094 }
90095 oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(drmp3_int16);
90096 newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(drmp3_int16);
90097 if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) {
90098 break;
90099 }
90100 pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
90101 if (pNewFrames == NULL) {
90102 drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
90103 break;
90104 }
90105 pFrames = pNewFrames;
90106 framesCapacity = newFramesCap;
90107 }
90108 DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(drmp3_int16)));
90109 totalFramesRead += framesJustRead;
90110 if (framesJustRead != framesToReadRightNow) {
90111 break;
90112 }
90113 }
90114 if (pConfig != NULL) {
90115 pConfig->channels = pMP3->channels;
90116 pConfig->sampleRate = pMP3->sampleRate;
90117 }
90118 drmp3_uninit(pMP3);
90119 if (pTotalFrameCount) {
90120 *pTotalFrameCount = totalFramesRead;
90121 }
90122 return pFrames;
90123}
90124DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
90125{
90126 drmp3 mp3;
90127 if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {
90128 return NULL;
90129 }
90130 return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
90131}
90132DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
90133{
90134 drmp3 mp3;
90135 if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {
90136 return NULL;
90137 }
90138 return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
90139}
90140DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
90141{
90142 drmp3 mp3;
90143 if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
90144 return NULL;
90145 }
90146 return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
90147}
90148DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
90149{
90150 drmp3 mp3;
90151 if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
90152 return NULL;
90153 }
90154 return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
90155}
90156#ifndef DR_MP3_NO_STDIO
90157DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
90158{
90159 drmp3 mp3;
90160 if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
90161 return NULL;
90162 }
90163 return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
90164}
90165DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
90166{
90167 drmp3 mp3;
90168 if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
90169 return NULL;
90170 }
90171 return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
90172}
90173#endif
90174DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks)
90175{
90176 if (pAllocationCallbacks != NULL) {
90177 return drmp3__malloc_from_callbacks(sz, pAllocationCallbacks);
90178 } else {
90179 return drmp3__malloc_default(sz, NULL);
90180 }
90181}
90182DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks)
90183{
90184 if (pAllocationCallbacks != NULL) {
90185 drmp3__free_from_callbacks(p, pAllocationCallbacks);
90186 } else {
90187 drmp3__free_default(p, NULL);
90188 }
90189}
90190#endif
90191/* dr_mp3_c end */
90192#endif /* DRMP3_IMPLEMENTATION */
90193#endif /* MA_NO_MP3 */
90194
90195
90196/* End globally disabled warnings. */
90197#if defined(_MSC_VER)
90198 #pragma warning(pop)
90199#endif
90200
90201#endif /* miniaudio_c */
90202#endif /* MINIAUDIO_IMPLEMENTATION */
90203
90204
90205/*
90206This software is available as a choice of the following licenses. Choose
90207whichever you prefer.
90208
90209===============================================================================
90210ALTERNATIVE 1 - Public Domain (www.unlicense.org)
90211===============================================================================
90212This is free and unencumbered software released into the public domain.
90213
90214Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
90215software, either in source code form or as a compiled binary, for any purpose,
90216commercial or non-commercial, and by any means.
90217
90218In jurisdictions that recognize copyright laws, the author or authors of this
90219software dedicate any and all copyright interest in the software to the public
90220domain. We make this dedication for the benefit of the public at large and to
90221the detriment of our heirs and successors. We intend this dedication to be an
90222overt act of relinquishment in perpetuity of all present and future rights to
90223this software under copyright law.
90224
90225THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
90226IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
90227FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
90228AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
90229ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
90230WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
90231
90232For more information, please refer to <http://unlicense.org/>
90233
90234===============================================================================
90235ALTERNATIVE 2 - MIT No Attribution
90236===============================================================================
90237Copyright 2020 David Reid
90238
90239Permission is hereby granted, free of charge, to any person obtaining a copy of
90240this software and associated documentation files (the "Software"), to deal in
90241the Software without restriction, including without limitation the rights to
90242use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
90243of the Software, and to permit persons to whom the Software is furnished to do
90244so.
90245
90246THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
90247IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
90248FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
90249AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
90250LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
90251OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
90252SOFTWARE.
90253*/
void * id
DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac *pFlac, drflac_uint64 framesToRead, drflac_int32 *pBufferOut)
#define DRFLAC_TRUE
Definition: dr_flac.h:271
DRFLAC_API drflac * drflac_open_memory_with_metadata(const void *pData, size_t dataSize, drflac_meta_proc onMeta, void *pUserData, const drflac_allocation_callbacks *pAllocationCallbacks)
drflac_uint8 drflac_bool8
Definition: dr_flac.h:269
#define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET
Definition: dr_flac.h:346
DRFLAC_API drflac * drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void *pUserData, const drflac_allocation_callbacks *pAllocationCallbacks)
DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac *pFlac, drflac_uint64 pcmFrameIndex)
DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac *pFlac, drflac_uint64 framesToRead, drflac_int16 *pBufferOut)
DRFLAC_API void drflac_close(drflac *pFlac)
#define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO
Definition: dr_flac.h:341
DRFLAC_API const char * drflac_version_string(void)
#define DRFLAC_FALSE
Definition: dr_flac.h:272
DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator *pIter, drflac_cuesheet_track *pCuesheetTrack)
drflac_uint32 drflac_cache_t
Definition: dr_flac.h:337
DRFLAC_API void drflac_version(drflac_uint32 *pMajor, drflac_uint32 *pMinor, drflac_uint32 *pRevision)
DRFLAC_API drflac * drflac_open_file_with_metadata_w(const wchar_t *pFileName, drflac_meta_proc onMeta, void *pUserData, const drflac_allocation_callbacks *pAllocationCallbacks)
DRFLAC_API void drflac_free(void *p, const drflac_allocation_callbacks *pAllocationCallbacks)
DRFLAC_API drflac_int16 * drflac_open_memory_and_read_pcm_frames_s16(const void *data, size_t dataSize, unsigned int *channels, unsigned int *sampleRate, drflac_uint64 *totalPCMFrameCount, const drflac_allocation_callbacks *pAllocationCallbacks)
DRFLAC_API const char * drflac_next_vorbis_comment(drflac_vorbis_comment_iterator *pIter, drflac_uint32 *pCommentLengthOut)
#define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION
Definition: dr_flac.h:343
signed char drflac_int8
Definition: dr_flac.h:241
drflac_bool32(* drflac_seek_proc)(void *pUserData, int offset, drflac_seek_origin origin)
Definition: dr_flac.h:541
#define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE
Definition: dr_flac.h:344
DRFLAC_API drflac_int16 * drflac_open_file_and_read_pcm_frames_s16(const char *filename, unsigned int *channels, unsigned int *sampleRate, drflac_uint64 *totalPCMFrameCount, const drflac_allocation_callbacks *pAllocationCallbacks)
DRFLAC_API drflac * drflac_open_file_with_metadata(const char *pFileName, drflac_meta_proc onMeta, void *pUserData, const drflac_allocation_callbacks *pAllocationCallbacks)
DRFLAC_API drflac * drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void *pUserData, const drflac_allocation_callbacks *pAllocationCallbacks)
DRFLAC_API drflac_int32 * drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void *pUserData, unsigned int *channels, unsigned int *sampleRate, drflac_uint64 *totalPCMFrameCount, const drflac_allocation_callbacks *pAllocationCallbacks)
DRFLAC_API drflac * drflac_open_memory(const void *pData, size_t dataSize, const drflac_allocation_callbacks *pAllocationCallbacks)
#define DRFLAC_VERSION_MINOR
Definition: dr_flac.h:234
#define DRFLAC_VERSION_STRING
Definition: dr_flac.h:236
drflac_container
Definition: dr_flac.h:374
@ drflac_container_native
Definition: dr_flac.h:375
@ drflac_container_unknown
Definition: dr_flac.h:377
@ drflac_container_ogg
Definition: dr_flac.h:376
drflac_seek_origin
Definition: dr_flac.h:381
@ drflac_seek_origin_current
Definition: dr_flac.h:383
@ drflac_seek_origin_start
Definition: dr_flac.h:382
DRFLAC_API drflac_int16 * drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void *pUserData, unsigned int *channels, unsigned int *sampleRate, drflac_uint64 *totalPCMFrameCount, const drflac_allocation_callbacks *pAllocationCallbacks)
DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac *pFlac, drflac_uint64 framesToRead, float *pBufferOut)
#define DRFLAC_METADATA_BLOCK_TYPE_PADDING
Definition: dr_flac.h:342
signed long long drflac_int64
Definition: dr_flac.h:258
void(* drflac_meta_proc)(void *pUserData, drflac_metadata *pMetadata)
Definition: dr_flac.h:561
unsigned short drflac_uint16
Definition: dr_flac.h:244
signed int drflac_int32
Definition: dr_flac.h:245
drflac_uint32 drflac_uintptr
Definition: dr_flac.h:267
unsigned int drflac_uint32
Definition: dr_flac.h:246
DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator *pIter, drflac_uint32 trackCount, const void *pTrackData)
size_t(* drflac_read_proc)(void *pUserData, void *pBufferOut, size_t bytesToRead)
Definition: dr_flac.h:510
#define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT
Definition: dr_flac.h:345
DRFLAC_API drflac * drflac_open_file_w(const wchar_t *pFileName, const drflac_allocation_callbacks *pAllocationCallbacks)
DRFLAC_API drflac * drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void *pUserData, const drflac_allocation_callbacks *pAllocationCallbacks)
DRFLAC_API drflac * drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void *pUserData, const drflac_allocation_callbacks *pAllocationCallbacks)
DRFLAC_API float * drflac_open_file_and_read_pcm_frames_f32(const char *filename, unsigned int *channels, unsigned int *sampleRate, drflac_uint64 *totalPCMFrameCount, const drflac_allocation_callbacks *pAllocationCallbacks)
#define DRFLAC_VERSION_REVISION
Definition: dr_flac.h:235
DRFLAC_API drflac_int32 * drflac_open_memory_and_read_pcm_frames_s32(const void *data, size_t dataSize, unsigned int *channels, unsigned int *sampleRate, drflac_uint64 *totalPCMFrameCount, const drflac_allocation_callbacks *pAllocationCallbacks)
#define DRFLAC_METADATA_BLOCK_TYPE_INVALID
Definition: dr_flac.h:348
#define DRFLAC_METADATA_BLOCK_TYPE_PICTURE
Definition: dr_flac.h:347
DRFLAC_API float * drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void *pUserData, unsigned int *channels, unsigned int *sampleRate, drflac_uint64 *totalPCMFrameCount, const drflac_allocation_callbacks *pAllocationCallbacks)
DRFLAC_API drflac * drflac_open_file(const char *pFileName, const drflac_allocation_callbacks *pAllocationCallbacks)
#define DRFLAC_API
Definition: dr_flac.h:299
#define DRFLAC_VERSION_MAJOR
Definition: dr_flac.h:233
signed short drflac_int16
Definition: dr_flac.h:243
unsigned long long drflac_uint64
Definition: dr_flac.h:259
DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator *pIter, drflac_uint32 commentCount, const void *pComments)
DRFLAC_API drflac_int32 * drflac_open_file_and_read_pcm_frames_s32(const char *filename, unsigned int *channels, unsigned int *sampleRate, drflac_uint64 *totalPCMFrameCount, const drflac_allocation_callbacks *pAllocationCallbacks)
unsigned char drflac_uint8
Definition: dr_flac.h:242
DRFLAC_API float * drflac_open_memory_and_read_pcm_frames_f32(const void *data, size_t dataSize, unsigned int *channels, unsigned int *sampleRate, drflac_uint64 *totalPCMFrameCount, const drflac_allocation_callbacks *pAllocationCallbacks)
drflac_uint32 drflac_bool32
Definition: dr_flac.h:270
#define DRMP3_ALREADY_CONNECTED
Definition: dr_mp3.h:214
unsigned char drmp3_uint8
Definition: dr_mp3.h:105
#define DRMP3_INVALID_OPERATION
Definition: dr_mp3.h:171
#define DRMP3_DOES_NOT_EXIST
Definition: dr_mp3.h:175
drmp3_seek_origin
Definition: dr_mp3.h:285
@ drmp3_seek_origin_start
Definition: dr_mp3.h:286
@ drmp3_seek_origin_current
Definition: dr_mp3.h:287
DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3 *pMP3, drmp3_uint64 framesToRead, float *pBufferOut)
#define DRMP3_UNAVAILABLE
Definition: dr_mp3.h:190
unsigned short drmp3_uint16
Definition: dr_mp3.h:107
#define DRMP3_NOT_DIRECTORY
Definition: dr_mp3.h:182
#define DRMP3_NOT_UNIQUE
Definition: dr_mp3.h:204
DRMP3_API drmp3_bool32 drmp3_init(drmp3 *pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void *pUserData, const drmp3_allocation_callbacks *pAllocationCallbacks)
DRMP3_API drmp3_bool32 drmp3_init_file(drmp3 *pMP3, const char *pFilePath, const drmp3_allocation_callbacks *pAllocationCallbacks)
#define DRMP3_ALREADY_IN_USE
Definition: dr_mp3.h:191
#define DRMP3_IN_PROGRESS
Definition: dr_mp3.h:218
signed int drmp3_int32
Definition: dr_mp3.h:108
DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info)
#define DRMP3_BAD_MESSAGE
Definition: dr_mp3.h:199
DRMP3_API drmp3_int16 * drmp3_open_memory_and_read_pcm_frames_s16(const void *pData, size_t dataSize, drmp3_config *pConfig, drmp3_uint64 *pTotalFrameCount, const drmp3_allocation_callbacks *pAllocationCallbacks)
#define DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED
Definition: dr_mp3.h:210
#define DRMP3_SUCCESS
Definition: dr_mp3.h:168
#define DRMP3_PATH_TOO_LONG
Definition: dr_mp3.h:180
signed char drmp3_int8
Definition: dr_mp3.h:104
DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3 *pMP3, drmp3_uint32 *pSeekPointCount, drmp3_seek_point *pSeekPoints)
DRMP3_API drmp3_int16 * drmp3_open_file_and_read_pcm_frames_s16(const char *filePath, drmp3_config *pConfig, drmp3_uint64 *pTotalFrameCount, const drmp3_allocation_callbacks *pAllocationCallbacks)
#define DRMP3_BAD_ADDRESS
Definition: dr_mp3.h:192
#define DRMP3_BAD_SEEK
Definition: dr_mp3.h:193
DRMP3_API void drmp3_uninit(drmp3 *pMP3)
#define DRMP3_PROTOCOL_UNAVAILABLE
Definition: dr_mp3.h:208
drmp3_bool32(* drmp3_seek_proc)(void *pUserData, int offset, drmp3_seek_origin origin)
Definition: dr_mp3.h:324
#define DRMP3_INVALID_ARGS
Definition: dr_mp3.h:170
#define DRMP3_NO_NETWORK
Definition: dr_mp3.h:203
#define DRMP3_IS_DIRECTORY
Definition: dr_mp3.h:183
DRMP3_API float * drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void *pUserData, drmp3_config *pConfig, drmp3_uint64 *pTotalFrameCount, const drmp3_allocation_callbacks *pAllocationCallbacks)
DRMP3_API void drmp3_version(drmp3_uint32 *pMajor, drmp3_uint32 *pMinor, drmp3_uint32 *pRevision)
#define DRMP3_BUSY
Definition: dr_mp3.h:187
#define DRMP3_CONNECTION_RESET
Definition: dr_mp3.h:213
#define DRMP3_INTERRUPT
Definition: dr_mp3.h:189
#define DRMP3_PROTOCOL_NOT_SUPPORTED
Definition: dr_mp3.h:209
#define DRMP3_ALREADY_EXISTS
Definition: dr_mp3.h:176
DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3 *pMP3)
#define DRMP3_OUT_OF_MEMORY
Definition: dr_mp3.h:172
DRMP3_API float * drmp3_open_file_and_read_pcm_frames_f32(const char *filePath, drmp3_config *pConfig, drmp3_uint64 *pTotalFrameCount, const drmp3_allocation_callbacks *pAllocationCallbacks)
DRMP3_API float * drmp3_open_memory_and_read_pcm_frames_f32(const void *pData, size_t dataSize, drmp3_config *pConfig, drmp3_uint64 *pTotalFrameCount, const drmp3_allocation_callbacks *pAllocationCallbacks)
#define DRMP3_NOT_CONNECTED
Definition: dr_mp3.h:215
#define DRMP3_TIMEOUT
Definition: dr_mp3.h:202
drmp3_int32 drmp3_result
Definition: dr_mp3.h:167
DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3 *pMP3, drmp3_uint64 *pMP3FrameCount, drmp3_uint64 *pPCMFrameCount)
#define DRMP3_INVALID_FILE
Definition: dr_mp3.h:178
DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3 *pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point *pSeekPoints)
DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3 *pMP3, drmp3_uint64 framesToRead, drmp3_int16 *pBufferOut)
#define DRMP3_VERSION_MINOR
Definition: dr_mp3.h:97
#define DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED
Definition: dr_mp3.h:211
DRMP3_API void * drmp3_malloc(size_t sz, const drmp3_allocation_callbacks *pAllocationCallbacks)
#define DRMP3_API
Definition: dr_mp3.h:162
DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3 *pMP3, const void *pData, size_t dataSize, const drmp3_allocation_callbacks *pAllocationCallbacks)
size_t(* drmp3_read_proc)(void *pUserData, void *pBufferOut, size_t bytesToRead)
Definition: dr_mp3.h:310
DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3 *pMP3)
DRMP3_API void drmp3dec_init(drmp3dec *dec)
#define DRMP3_NO_MESSAGE
Definition: dr_mp3.h:198
#define DRMP3_NO_HOST
Definition: dr_mp3.h:217
#define DRMP3_DEADLOCK
Definition: dr_mp3.h:195
#define DRMP3_ERROR
Definition: dr_mp3.h:169
drmp3_uint8 drmp3_bool8
Definition: dr_mp3.h:132
#define DRMP3_CANCELLED
Definition: dr_mp3.h:219
DRMP3_API void drmp3_free(void *p, const drmp3_allocation_callbacks *pAllocationCallbacks)
unsigned int drmp3_uint32
Definition: dr_mp3.h:109
DRMP3_API drmp3_int16 * drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void *pUserData, drmp3_config *pConfig, drmp3_uint64 *pTotalFrameCount, const drmp3_allocation_callbacks *pAllocationCallbacks)
#define DRMP3_VERSION_MAJOR
Definition: dr_mp3.h:96
drmp3_uint32 drmp3_uintptr
Definition: dr_mp3.h:130
#define DRMP3_INVALID_DATA
Definition: dr_mp3.h:201
#define DRMP3_IO_ERROR
Definition: dr_mp3.h:188
#define DRMP3_NOT_SOCKET
Definition: dr_mp3.h:205
#define DRMP3_VERSION_REVISION
Definition: dr_mp3.h:98
DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples)
#define DRMP3_CONNECTION_REFUSED
Definition: dr_mp3.h:216
#define DRMP3_FALSE
Definition: dr_mp3.h:135
DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3 *pMP3, const wchar_t *pFilePath, const drmp3_allocation_callbacks *pAllocationCallbacks)
signed long long drmp3_int64
Definition: dr_mp3.h:121
#define DRMP3_NO_DATA_AVAILABLE
Definition: dr_mp3.h:200
DRMP3_API const char * drmp3_version_string(void)
#define DRMP3_BAD_PROTOCOL
Definition: dr_mp3.h:207
#define DRMP3_NO_ADDRESS
Definition: dr_mp3.h:206
#define DRMP3_VERSION_STRING
Definition: dr_mp3.h:99
#define DRMP3_BAD_PIPE
Definition: dr_mp3.h:194
signed short drmp3_int16
Definition: dr_mp3.h:106
#define DRMP3_TOO_MANY_OPEN_FILES
Definition: dr_mp3.h:177
unsigned long long drmp3_uint64
Definition: dr_mp3.h:122
#define DRMP3_TRUE
Definition: dr_mp3.h:134
#define DRMP3_ACCESS_DENIED
Definition: dr_mp3.h:174
#define DRMP3_TOO_MANY_LINKS
Definition: dr_mp3.h:196
#define DRMP3_SOCKET_NOT_SUPPORTED
Definition: dr_mp3.h:212
#define DRMP3_INLINE
Definition: dr_mp3.h:245
#define DRMP3_NO_SPACE
Definition: dr_mp3.h:186
#define DRMP3_DIRECTORY_NOT_EMPTY
Definition: dr_mp3.h:184
#define DRMP3_OUT_OF_RANGE
Definition: dr_mp3.h:173
#define DRMP3_TOO_BIG
Definition: dr_mp3.h:179
drmp3_uint32 drmp3_bool32
Definition: dr_mp3.h:133
#define DRMP3_NOT_IMPLEMENTED
Definition: dr_mp3.h:197
DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3 *pMP3, drmp3_uint64 frameIndex)
#define DRWAV_BUSY
Definition: dr_wav.h:217
#define DRWAV_API
Definition: dr_wav.h:192
DRWAV_API void drwav_s32_to_s16(drwav_int16 *pOut, const drwav_int32 *pIn, size_t sampleCount)
drwav_uint8 drwav_bool8
Definition: dr_wav.h:162
DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav *pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void *pUserData, drwav_uint32 flags, const drwav_allocation_callbacks *pAllocationCallbacks)
drwav_metadata_location
Definition: dr_wav.h:768
@ drwav_metadata_location_invalid
Definition: dr_wav.h:769
@ drwav_metadata_location_inside_info_list
Definition: dr_wav.h:771
@ drwav_metadata_location_inside_adtl_list
Definition: dr_wav.h:772
@ drwav_metadata_location_top_level
Definition: dr_wav.h:770
DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav *pWav, const wchar_t *filename, drwav_chunk_proc onChunk, void *pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API void drwav_f32_to_s16(drwav_int16 *pOut, const float *pIn, size_t sampleCount)
#define DRWAV_OUT_OF_RANGE
Definition: dr_wav.h:203
#define DRWAV_FALSE
Definition: dr_wav.h:165
#define DRWAV_TIMEOUT
Definition: dr_wav.h:232
drwav_container
Definition: dr_wav.h:275
@ drwav_container_w64
Definition: dr_wav.h:277
@ drwav_container_riff
Definition: dr_wav.h:276
@ drwav_container_rf64
Definition: dr_wav.h:278
#define DRWAV_VERSION_MINOR
Definition: dr_wav.h:127
#define DRWAV_TOO_BIG
Definition: dr_wav.h:209
DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav *pWav, const char *filename, drwav_uint32 flags, const drwav_allocation_callbacks *pAllocationCallbacks)
#define DRWAV_SOCKET_NOT_SUPPORTED
Definition: dr_wav.h:242
#define DRWAV_ACCESS_DENIED
Definition: dr_wav.h:204
DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav *pWav, const wchar_t *filename, const drwav_data_format *pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API void drwav_s24_to_f32(float *pOut, const drwav_uint8 *pIn, size_t sampleCount)
DRWAV_API void drwav_s32_to_f32(float *pOut, const drwav_int32 *pIn, size_t sampleCount)
#define DRWAV_TOO_MANY_LINKS
Definition: dr_wav.h:226
drwav_smpl_loop_type
Definition: dr_wav.h:511
@ drwav_smpl_loop_type_backward
Definition: dr_wav.h:514
@ drwav_smpl_loop_type_pingpong
Definition: dr_wav.h:513
@ drwav_smpl_loop_type_forward
Definition: dr_wav.h:512
#define DRWAV_SEQUENTIAL
Definition: dr_wav.h:263
drwav_int32 drwav_result
Definition: dr_wav.h:197
#define DRWAV_INVALID_DATA
Definition: dr_wav.h:231
DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav *pWav, drwav_uint64 framesToWrite, const void *pData)
#define DRWAV_NO_ADDRESS
Definition: dr_wav.h:236
#define DRWAV_IO_ERROR
Definition: dr_wav.h:218
DRWAV_API drwav_int32 * drwav_open_memory_and_read_pcm_frames_s32(const void *data, size_t dataSize, unsigned int *channelsOut, unsigned int *sampleRateOut, drwav_uint64 *totalFrameCountOut, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API const char * drwav_version_string(void)
DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav *pWav, drwav_uint64 framesToRead, void *pBufferOut)
DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8 *data)
#define DRWAV_UNAVAILABLE
Definition: dr_wav.h:220
DRWAV_API void drwav_free(void *p, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API void drwav_alaw_to_s32(drwav_int32 *pOut, const drwav_uint8 *pIn, size_t sampleCount)
DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav *pWav, const drwav_data_format *pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void *pUserData, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API void drwav_f64_to_s16(drwav_int16 *pOut, const double *pIn, size_t sampleCount)
DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav *pWav, const char *filename, const drwav_data_format *pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav *pWav, drwav_uint64 framesToRead, void *pBufferOut)
drwav_bool32(* drwav_seek_proc)(void *pUserData, int offset, drwav_seek_origin origin)
Definition: dr_wav.h:381
DRWAV_API void drwav_s24_to_s32(drwav_int32 *pOut, const drwav_uint8 *pIn, size_t sampleCount)
#define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED
Definition: dr_wav.h:241
#define DRWAV_ERROR
Definition: dr_wav.h:199
drwav_uint32 drwav_bool32
Definition: dr_wav.h:163
#define DRWAV_NOT_DIRECTORY
Definition: dr_wav.h:212
DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav *pWav, drwav_uint64 framesToRead, float *pBufferOut)
#define DR_WAVE_FORMAT_DVI_ADPCM
Definition: dr_wav.h:259
#define DRWAV_VERSION_STRING
Definition: dr_wav.h:129
DRWAV_API void drwav_f64_to_f32(float *pOut, const double *pIn, size_t sampleCount)
#define DRWAV_INVALID_ARGS
Definition: dr_wav.h:200
#define DRWAV_PROTOCOL_UNAVAILABLE
Definition: dr_wav.h:238
DRWAV_API drwav_int16 * drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t *filename, unsigned int *channelsOut, unsigned int *sampleRateOut, drwav_uint64 *totalFrameCountOut, const drwav_allocation_callbacks *pAllocationCallbacks)
#define DR_WAVE_FORMAT_EXTENSIBLE
Definition: dr_wav.h:260
DRWAV_API drwav_bool32 drwav_init_file_w(drwav *pWav, const wchar_t *filename, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API drwav_int32 * drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t *filename, unsigned int *channelsOut, unsigned int *sampleRateOut, drwav_uint64 *totalFrameCountOut, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API size_t drwav_write_raw(drwav *pWav, size_t bytesToWrite, const void *pData)
DRWAV_API void drwav_u8_to_s32(drwav_int32 *pOut, const drwav_uint8 *pIn, size_t sampleCount)
DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav *pWav, drwav_uint64 framesToRead, drwav_int16 *pBufferOut)
DRWAV_API float * drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t *filename, unsigned int *channelsOut, unsigned int *sampleRateOut, drwav_uint64 *totalFrameCountOut, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav *pWav, drwav_uint64 framesToRead, float *pBufferOut)
drwav_uint32 drwav_uintptr
Definition: dr_wav.h:160
DRWAV_API drwav_metadata * drwav_take_ownership_of_metadata(drwav *pWav)
#define DRWAV_ALREADY_IN_USE
Definition: dr_wav.h:221
#define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED
Definition: dr_wav.h:240
DRWAV_API drwav_bool32 drwav_init_file_ex(drwav *pWav, const char *filename, drwav_chunk_proc onChunk, void *pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav *pWav, const drwav_data_format *pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void *pUserData, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API float drwav_bytes_to_f32(const drwav_uint8 *data)
#define DR_WAVE_FORMAT_MULAW
Definition: dr_wav.h:258
DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16])
#define DRWAV_PRIVATE
Definition: dr_wav.h:193
DRWAV_API drwav_int32 * drwav_open_file_and_read_pcm_frames_s32(const char *filename, unsigned int *channelsOut, unsigned int *sampleRateOut, drwav_uint64 *totalFrameCountOut, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API drwav_int16 * drwav_open_file_and_read_pcm_frames_s16(const char *filename, unsigned int *channelsOut, unsigned int *sampleRateOut, drwav_uint64 *totalFrameCountOut, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API void drwav_s16_to_f32(float *pOut, const drwav_int16 *pIn, size_t sampleCount)
#define DRWAV_OUT_OF_MEMORY
Definition: dr_wav.h:202
signed int drwav_int32
Definition: dr_wav.h:138
drwav_uint64(* drwav_chunk_proc)(void *pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void *pReadSeekUserData, const drwav_chunk_header *pChunkHeader, drwav_container container, const drwav_fmt *pFMT)
Definition: dr_wav.h:407
DRWAV_API drwav_bool32 drwav_init_memory_write(drwav *pWav, void **ppData, size_t *pDataSize, const drwav_data_format *pFormat, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav *pWav, void **ppData, size_t *pDataSize, const drwav_data_format *pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks *pAllocationCallbacks)
#define DRWAV_NO_NETWORK
Definition: dr_wav.h:233
DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8 *data)
#define DRWAV_BAD_MESSAGE
Definition: dr_wav.h:229
#define DRWAV_PATH_TOO_LONG
Definition: dr_wav.h:210
#define DRWAV_NOT_SOCKET
Definition: dr_wav.h:235
DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav *pWav, drwav_uint64 framesToRead, drwav_int32 *pBufferOut)
DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav *pWav, drwav_uint64 framesToRead, float *pBufferOut)
DRWAV_API size_t drwav_read_raw(drwav *pWav, size_t bytesToRead, void *pBufferOut)
DRWAV_API void drwav_f64_to_s32(drwav_int32 *pOut, const double *pIn, size_t sampleCount)
signed short drwav_int16
Definition: dr_wav.h:136
DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav *pWav, const drwav_data_format *pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void *pUserData, const drwav_allocation_callbacks *pAllocationCallbacks, drwav_metadata *pMetadata, drwav_uint32 metadataCount)
DRWAV_API void drwav_alaw_to_f32(float *pOut, const drwav_uint8 *pIn, size_t sampleCount)
DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt *pFMT)
DRWAV_API float * drwav_open_memory_and_read_pcm_frames_f32(const void *data, size_t dataSize, unsigned int *channelsOut, unsigned int *sampleRateOut, drwav_uint64 *totalFrameCountOut, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav *pWav, const wchar_t *filename, const drwav_data_format *pFormat, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API drwav_bool32 drwav_init_memory(drwav *pWav, const void *data, size_t dataSize, const drwav_allocation_callbacks *pAllocationCallbacks)
#define DRWAV_NO_SPACE
Definition: dr_wav.h:216
DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav *pWav, void **ppData, size_t *pDataSize, const drwav_data_format *pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks *pAllocationCallbacks)
#define DR_WAVE_FORMAT_PCM
Definition: dr_wav.h:254
#define DRWAV_NO_DATA_AVAILABLE
Definition: dr_wav.h:230
unsigned int drwav_uint32
Definition: dr_wav.h:139
#define DRWAV_INVALID_FILE
Definition: dr_wav.h:208
DRWAV_API void drwav_f32_to_s32(drwav_int32 *pOut, const float *pIn, size_t sampleCount)
#define DRWAV_NOT_CONNECTED
Definition: dr_wav.h:245
DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8 *data)
DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav *pWav, const wchar_t *filename, drwav_uint32 flags, const drwav_allocation_callbacks *pAllocationCallbacks)
#define DRWAV_BAD_PROTOCOL
Definition: dr_wav.h:237
#define DRWAV_DOES_NOT_EXIST
Definition: dr_wav.h:205
DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav *pWav, drwav_uint64 framesToRead, drwav_int32 *pBufferOut)
DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav *pWav, drwav_uint64 framesToWrite, const void *pData)
#define DRWAV_IS_DIRECTORY
Definition: dr_wav.h:213
#define DRWAV_BAD_ADDRESS
Definition: dr_wav.h:222
DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8 *data)
#define DRWAV_BAD_SEEK
Definition: dr_wav.h:223
DRWAV_API drwav_bool32 drwav_init_file(drwav *pWav, const char *filename, const drwav_allocation_callbacks *pAllocationCallbacks)
#define DRWAV_CANCELLED
Definition: dr_wav.h:249
drwav_seek_origin
Definition: dr_wav.h:269
@ drwav_seek_origin_current
Definition: dr_wav.h:271
@ drwav_seek_origin_start
Definition: dr_wav.h:270
#define DRWAV_ALREADY_EXISTS
Definition: dr_wav.h:206
DRWAV_API drwav_bool32 drwav_init_file_write(drwav *pWav, const char *filename, const drwav_data_format *pFormat, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav *pWav, drwav_uint64 targetFrameIndex)
DRWAV_API void drwav_mulaw_to_s16(drwav_int16 *pOut, const drwav_uint8 *pIn, size_t sampleCount)
#define DR_WAVE_FORMAT_ALAW
Definition: dr_wav.h:257
DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8 *a, const char *b)
DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav *pWav, const void *data, size_t dataSize, drwav_chunk_proc onChunk, void *pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API void drwav_s16_to_s32(drwav_int32 *pOut, const drwav_int16 *pIn, size_t sampleCount)
#define DRWAV_VERSION_MAJOR
Definition: dr_wav.h:126
#define DRWAV_TRUE
Definition: dr_wav.h:164
#define DR_WAVE_FORMAT_ADPCM
Definition: dr_wav.h:255
DRWAV_API void drwav_alaw_to_s16(drwav_int16 *pOut, const drwav_uint8 *pIn, size_t sampleCount)
DRWAV_API void drwav_mulaw_to_s32(drwav_int32 *pOut, const drwav_uint8 *pIn, size_t sampleCount)
#define DRWAV_NOT_IMPLEMENTED
Definition: dr_wav.h:227
#define DRWAV_SUCCESS
Definition: dr_wav.h:198
DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav *pWav, drwav_uint64 framesToRead, drwav_int32 *pBufferOut)
signed char drwav_int8
Definition: dr_wav.h:134
DRWAV_API void drwav_mulaw_to_f32(float *pOut, const drwav_uint8 *pIn, size_t sampleCount)
DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav *pWav, drwav_uint64 *pLength)
#define DRWAV_NO_MESSAGE
Definition: dr_wav.h:228
size_t(* drwav_write_proc)(void *pUserData, const void *pData, size_t bytesToWrite)
Definition: dr_wav.h:367
DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8 *data)
DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav *pWav, drwav_uint64 framesToRead, drwav_int16 *pBufferOut)
#define DRWAV_BAD_PIPE
Definition: dr_wav.h:224
#define DRWAV_CONNECTION_RESET
Definition: dr_wav.h:243
#define DRWAV_ALREADY_CONNECTED
Definition: dr_wav.h:244
#define DRWAV_TOO_MANY_OPEN_FILES
Definition: dr_wav.h:207
DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav *pWav, const wchar_t *filename, const drwav_data_format *pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks *pAllocationCallbacks)
#define DRWAV_VERSION_REVISION
Definition: dr_wav.h:128
#define DRWAV_NOT_UNIQUE
Definition: dr_wav.h:234
DRWAV_API float * drwav_open_file_and_read_pcm_frames_f32(const char *filename, unsigned int *channelsOut, unsigned int *sampleRateOut, drwav_uint64 *totalFrameCountOut, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8 *data)
signed long long drwav_int64
Definition: dr_wav.h:151
#define DRWAV_PROTOCOL_NOT_SUPPORTED
Definition: dr_wav.h:239
DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav *pWav, drwav_uint64 framesToRead, drwav_int16 *pBufferOut)
DRWAV_API drwav_result drwav_uninit(drwav *pWav)
DRWAV_API void drwav_u8_to_f32(float *pOut, const drwav_uint8 *pIn, size_t sampleCount)
DRWAV_API void drwav_u8_to_s16(drwav_int16 *pOut, const drwav_uint8 *pIn, size_t sampleCount)
unsigned long long drwav_uint64
Definition: dr_wav.h:152
DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav *pWav, const void *data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks *pAllocationCallbacks)
#define DRWAV_DIRECTORY_NOT_EMPTY
Definition: dr_wav.h:214
#define DRWAV_CONNECTION_REFUSED
Definition: dr_wav.h:246
#define DRWAV_DEADLOCK
Definition: dr_wav.h:225
DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav *pWav, drwav_uint64 framesToWrite, const void *pData)
#define DR_WAVE_FORMAT_IEEE_FLOAT
Definition: dr_wav.h:256
DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format *pFormat, drwav_uint64 totalFrameCount, drwav_metadata *pMetadata, drwav_uint32 metadataCount)
DRWAV_API drwav_int16 * drwav_open_memory_and_read_pcm_frames_s16(const void *data, size_t dataSize, unsigned int *channelsOut, unsigned int *sampleRateOut, drwav_uint64 *totalFrameCountOut, const drwav_allocation_callbacks *pAllocationCallbacks)
unsigned char drwav_uint8
Definition: dr_wav.h:135
DRWAV_API void drwav_version(drwav_uint32 *pMajor, drwav_uint32 *pMinor, drwav_uint32 *pRevision)
DRWAV_API drwav_int32 * drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void *pUserData, unsigned int *channelsOut, unsigned int *sampleRateOut, drwav_uint64 *totalFrameCountOut, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API drwav_int16 * drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void *pUserData, unsigned int *channelsOut, unsigned int *sampleRateOut, drwav_uint64 *totalFrameCountOut, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav *pWav, const char *filename, const drwav_data_format *pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API float * drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void *pUserData, unsigned int *channelsOut, unsigned int *sampleRateOut, drwav_uint64 *totalFrameCountOut, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav *pWav, drwav_uint64 *pCursor)
DRWAV_API drwav_bool32 drwav_init(drwav *pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void *pUserData, const drwav_allocation_callbacks *pAllocationCallbacks)
DRWAV_API drwav_bool32 drwav_init_write(drwav *pWav, const drwav_data_format *pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void *pUserData, const drwav_allocation_callbacks *pAllocationCallbacks)
#define DRWAV_INTERRUPT
Definition: dr_wav.h:219
drwav_metadata_type
Definition: dr_wav.h:445
@ drwav_metadata_type_list_all_adtl
Definition: dr_wav.h:496
@ drwav_metadata_type_list_info_tracknumber
Definition: dr_wav.h:483
@ drwav_metadata_type_list_all_info_strings
Definition: dr_wav.h:486
@ drwav_metadata_type_none
Definition: dr_wav.h:446
@ drwav_metadata_type_list_info_artist
Definition: dr_wav.h:478
@ drwav_metadata_type_list_info_comment
Definition: dr_wav.h:479
@ drwav_metadata_type_list_info_date
Definition: dr_wav.h:480
@ drwav_metadata_type_bext
Definition: dr_wav.h:462
@ drwav_metadata_type_list_info_genre
Definition: dr_wav.h:481
@ drwav_metadata_type_list_info_album
Definition: dr_wav.h:482
@ drwav_metadata_type_list_label
Definition: dr_wav.h:471
@ drwav_metadata_type_acid
Definition: dr_wav.h:461
@ drwav_metadata_type_unknown
Definition: dr_wav.h:455
@ drwav_metadata_type_list_info_software
Definition: dr_wav.h:475
@ drwav_metadata_type_cue
Definition: dr_wav.h:460
@ drwav_metadata_type_list_labelled_cue_region
Definition: dr_wav.h:473
@ drwav_metadata_type_smpl
Definition: dr_wav.h:458
@ drwav_metadata_type_list_info_title
Definition: dr_wav.h:477
@ drwav_metadata_type_list_info_copyright
Definition: dr_wav.h:476
@ drwav_metadata_type_all_including_unknown
Definition: dr_wav.h:501
@ drwav_metadata_type_all
Definition: dr_wav.h:500
@ drwav_metadata_type_inst
Definition: dr_wav.h:459
@ drwav_metadata_type_list_note
Definition: dr_wav.h:472
#define DRWAV_INVALID_OPERATION
Definition: dr_wav.h:201
size_t(* drwav_read_proc)(void *pUserData, void *pBufferOut, size_t bytesToRead)
Definition: dr_wav.h:354
DRWAV_API void drwav_s24_to_s16(drwav_int16 *pOut, const drwav_uint8 *pIn, size_t sampleCount)
unsigned short drwav_uint16
Definition: dr_wav.h:137
#define DRWAV_IN_PROGRESS
Definition: dr_wav.h:248
#define DRWAV_NO_HOST
Definition: dr_wav.h:247
DRWAV_API drwav_bool32 drwav_init_ex(drwav *pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void *pReadSeekUserData, void *pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks *pAllocationCallbacks)
drwav_acid_flag
Definition: dr_wav.h:623
@ drwav_acid_flag_acidizer
Definition: dr_wav.h:628
@ drwav_acid_flag_one_shot
Definition: dr_wav.h:624
@ drwav_acid_flag_root_note_set
Definition: dr_wav.h:625
@ drwav_acid_flag_stretch
Definition: dr_wav.h:626
@ drwav_acid_flag_disk_based
Definition: dr_wav.h:627
#define DRWAV_AT_END
Definition: dr_wav.h:251
DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav *pWav, drwav_uint64 framesToRead, void *pBufferOut)
#define MA_SOUND_SOURCE_CHANNEL_COUNT
Definition: miniaudio.h:10710
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:4058
@ ma_dither_mode_none
Definition: miniaudio.h:4059
@ ma_dither_mode_triangle
Definition: miniaudio.h:4061
@ ma_dither_mode_rectangle
Definition: miniaudio.h:4060
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)
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)
MA_API void ma_delay_node_set_decay(ma_delay_node *pDelayNode, float value)
pthread_t ma_pthread_t
Definition: miniaudio.h:3750
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:10172
MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format)
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:3635
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
Node Graph.
Definition: miniaudio.h:10167
MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config *pConfig, size_t *pHeapSizeInBytes)
#define NULL
Definition: miniaudio.h:3718
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:3752
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:9552
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)
MA_API void ma_pcm_u8_to_s16(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
Format Conversion.
MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb *pRB)
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 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:9554
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:9272
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)
Conversion Helpers.
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:10179
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)
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)
void * ma_ptr
Definition: miniaudio.h:3709
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 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:3826
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:3832
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:9701
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:3897
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:9700
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:3890
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:3751
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_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate)
Utiltities.
ma_sound ma_sound_group
Definition: miniaudio.h:10761
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:5744
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:3700
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)
MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config *pConfig, ma_hishelf_node *pNode)
ma_engine_node_type
Definition: miniaudio.h:10662
@ ma_engine_node_type_group
Definition: miniaudio.h:10664
@ ma_engine_node_type_sound
Definition: miniaudio.h:10663
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:3673
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:5093
@ ma_resample_algorithm_linear
Definition: miniaudio.h:5094
@ ma_resample_algorithm_custom
Definition: miniaudio.h:5095
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)
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)
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:6395
@ ma_ios_session_category_option_default_to_speaker
Definition: miniaudio.h:6399
@ ma_ios_session_category_option_allow_air_play
Definition: miniaudio.h:6402
@ ma_ios_session_category_option_duck_others
Definition: miniaudio.h:6397
@ ma_ios_session_category_option_mix_with_others
Definition: miniaudio.h:6396
@ ma_ios_session_category_option_allow_bluetooth_a2dp
Definition: miniaudio.h:6401
@ ma_ios_session_category_option_allow_bluetooth
Definition: miniaudio.h:6398
@ ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others
Definition: miniaudio.h:6400
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 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:4043
ma_performance_profile
Definition: miniaudio.h:4129
@ ma_performance_profile_low_latency
Definition: miniaudio.h:4130
@ ma_performance_profile_conservative
Definition: miniaudio.h:4131
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)
Synchronization.
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:4864
@ ma_handedness_left
Definition: miniaudio.h:4866
@ ma_handedness_right
Definition: miniaudio.h:4865
ma_ios_session_category
Definition: miniaudio.h:6382
@ ma_ios_session_category_play_and_record
Definition: miniaudio.h:6389
@ ma_ios_session_category_default
Definition: miniaudio.h:6383
@ ma_ios_session_category_multi_route
Definition: miniaudio.h:6390
@ ma_ios_session_category_playback
Definition: miniaudio.h:6387
@ ma_ios_session_category_none
Definition: miniaudio.h:6384
@ ma_ios_session_category_ambient
Definition: miniaudio.h:6385
@ ma_ios_session_category_record
Definition: miniaudio.h:6388
@ ma_ios_session_category_solo_ambient
Definition: miniaudio.h:6386
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:9698
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_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 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:3690
MA_API void ma_engine_node_uninit(ma_engine_node *pEngineNode, const ma_allocation_callbacks *pAllocationCallbacks)
ma_job_queue_flags
Definition: miniaudio.h:6002
@ MA_JOB_QUEUE_FLAG_NON_BLOCKING
Definition: miniaudio.h:6003
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_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:5078
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:3677
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:9504
ma_device_type
Definition: miniaudio.h:6367
@ ma_device_type_duplex
Definition: miniaudio.h:6370
@ ma_device_type_playback
Definition: miniaudio.h:6368
@ ma_device_type_capture
Definition: miniaudio.h:6369
@ ma_device_type_loopback
Definition: miniaudio.h:6371
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_bool32 ma_data_source_is_looping(ma_data_source *pDataSource)
MA_API ma_result ma_biquad_reinit(const ma_biquad_config *pConfig, ma_biquad *pBQ)
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)
Miscellaneous Helpers.
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
Channel Maps.
Definition: miniaudio.h:5397
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 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:3655
ma_result(* ma_job_proc)(ma_job *pJob)
Definition: miniaudio.h:5851
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:6297
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 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 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:9505
ma_uint32 ma_bool32
Definition: miniaudio.h:3704
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:4037
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_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:4781
@ ma_pan_mode_balance
Definition: miniaudio.h:4782
@ ma_pan_mode_pan
Definition: miniaudio.h:4783
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:6452
@ ma_aaudio_content_type_music
Definition: miniaudio.h:6455
@ ma_aaudio_content_type_speech
Definition: miniaudio.h:6457
@ ma_aaudio_content_type_default
Definition: miniaudio.h:6453
@ ma_aaudio_content_type_sonification
Definition: miniaudio.h:6456
@ ma_aaudio_content_type_movie
Definition: miniaudio.h:6454
MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels)
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_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 float ma_fader_get_current_volume(ma_fader *pFader)
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 void ma_fence_uninit(ma_fence *pFence)
ma_handle ma_vfs_file
Definition: miniaudio.h:9451
ma_thread_priority
Definition: miniaudio.h:4155
@ ma_thread_priority_default
Definition: miniaudio.h:4163
@ ma_thread_priority_realtime
Definition: miniaudio.h:4162
@ ma_thread_priority_high
Definition: miniaudio.h:4160
@ ma_thread_priority_lowest
Definition: miniaudio.h:4157
@ ma_thread_priority_low
Definition: miniaudio.h:4158
@ ma_thread_priority_highest
Definition: miniaudio.h:4161
@ ma_thread_priority_idle
Definition: miniaudio.h:4156
@ ma_thread_priority_normal
Definition: miniaudio.h:4159
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 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:3672
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:10656
ma_result(* ma_read_proc)(void *pUserData, void *pBufferOut, size_t bytesToRead, size_t *pBytesRead)
Definition: miniaudio.h:9503
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:5226
@ ma_mono_expansion_mode_stereo_only
Definition: miniaudio.h:5229
@ ma_mono_expansion_mode_average
Definition: miniaudio.h:5228
@ ma_mono_expansion_mode_default
Definition: miniaudio.h:5230
@ ma_mono_expansion_mode_duplicate
Definition: miniaudio.h:5227
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_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:3703
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:3899
@ MA_CHANNEL_MONO
Definition: miniaudio.h:3901
@ MA_CHANNEL_BACK_LEFT
Definition: miniaudio.h:3906
@ MA_CHANNEL_AUX_6
Definition: miniaudio.h:3926
@ MA_CHANNEL_FRONT_LEFT_CENTER
Definition: miniaudio.h:3908
@ MA_CHANNEL_AUX_29
Definition: miniaudio.h:3949
@ MA_CHANNEL_AUX_18
Definition: miniaudio.h:3938
@ MA_CHANNEL_AUX_21
Definition: miniaudio.h:3941
@ MA_CHANNEL_AUX_15
Definition: miniaudio.h:3935
@ MA_CHANNEL_BACK_CENTER
Definition: miniaudio.h:3910
@ MA_CHANNEL_AUX_3
Definition: miniaudio.h:3923
@ MA_CHANNEL_AUX_9
Definition: miniaudio.h:3929
@ MA_CHANNEL_AUX_28
Definition: miniaudio.h:3948
@ MA_CHANNEL_AUX_2
Definition: miniaudio.h:3922
@ MA_CHANNEL_AUX_14
Definition: miniaudio.h:3934
@ MA_CHANNEL_FRONT_RIGHT_CENTER
Definition: miniaudio.h:3909
@ MA_CHANNEL_LFE
Definition: miniaudio.h:3905
@ MA_CHANNEL_TOP_FRONT_LEFT
Definition: miniaudio.h:3914
@ MA_CHANNEL_AUX_20
Definition: miniaudio.h:3940
@ MA_CHANNEL_TOP_CENTER
Definition: miniaudio.h:3913
@ MA_CHANNEL_AUX_1
Definition: miniaudio.h:3921
@ MA_CHANNEL_AUX_25
Definition: miniaudio.h:3945
@ MA_CHANNEL_AUX_5
Definition: miniaudio.h:3925
@ MA_CHANNEL_TOP_BACK_RIGHT
Definition: miniaudio.h:3919
@ MA_CHANNEL_TOP_FRONT_CENTER
Definition: miniaudio.h:3915
@ MA_CHANNEL_AUX_27
Definition: miniaudio.h:3947
@ MA_CHANNEL_FRONT_RIGHT
Definition: miniaudio.h:3903
@ MA_CHANNEL_AUX_13
Definition: miniaudio.h:3933
@ MA_CHANNEL_NONE
Definition: miniaudio.h:3900
@ MA_CHANNEL_AUX_7
Definition: miniaudio.h:3927
@ MA_CHANNEL_BACK_RIGHT
Definition: miniaudio.h:3907
@ MA_CHANNEL_AUX_24
Definition: miniaudio.h:3944
@ MA_CHANNEL_AUX_30
Definition: miniaudio.h:3950
@ MA_CHANNEL_FRONT_LEFT
Definition: miniaudio.h:3902
@ MA_CHANNEL_AUX_12
Definition: miniaudio.h:3932
@ MA_CHANNEL_AUX_31
Definition: miniaudio.h:3951
@ MA_CHANNEL_AUX_17
Definition: miniaudio.h:3937
@ MA_CHANNEL_SIDE_LEFT
Definition: miniaudio.h:3911
@ MA_CHANNEL_AUX_8
Definition: miniaudio.h:3928
@ MA_CHANNEL_LEFT
Definition: miniaudio.h:3952
@ MA_CHANNEL_AUX_0
Definition: miniaudio.h:3920
@ MA_CHANNEL_AUX_19
Definition: miniaudio.h:3939
@ MA_CHANNEL_AUX_23
Definition: miniaudio.h:3943
@ MA_CHANNEL_TOP_BACK_CENTER
Definition: miniaudio.h:3918
@ MA_CHANNEL_FRONT_CENTER
Definition: miniaudio.h:3904
@ MA_CHANNEL_TOP_FRONT_RIGHT
Definition: miniaudio.h:3916
@ MA_CHANNEL_AUX_10
Definition: miniaudio.h:3930
@ MA_CHANNEL_POSITION_COUNT
Definition: miniaudio.h:3954
@ MA_CHANNEL_SIDE_RIGHT
Definition: miniaudio.h:3912
@ MA_CHANNEL_AUX_22
Definition: miniaudio.h:3942
@ MA_CHANNEL_AUX_4
Definition: miniaudio.h:3924
@ MA_CHANNEL_TOP_BACK_LEFT
Definition: miniaudio.h:3917
@ MA_CHANNEL_AUX_16
Definition: miniaudio.h:3936
@ MA_CHANNEL_AUX_11
Definition: miniaudio.h:3931
@ MA_CHANNEL_AUX_26
Definition: miniaudio.h:3946
@ MA_CHANNEL_RIGHT
Definition: miniaudio.h:3953
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 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 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:4108
@ ma_channel_mix_mode_default
Definition: miniaudio.h:4112
@ ma_channel_mix_mode_simple
Definition: miniaudio.h:4110
@ ma_channel_mix_mode_rectangular
Definition: miniaudio.h:4109
@ ma_channel_mix_mode_custom_weights
Definition: miniaudio.h:4111
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:3675
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 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:3633
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:3860
@ MA_LOG_LEVEL_ERROR
Definition: miniaudio.h:3864
@ MA_LOG_LEVEL_WARNING
Definition: miniaudio.h:3863
@ MA_LOG_LEVEL_INFO
Definition: miniaudio.h:3862
@ MA_LOG_LEVEL_DEBUG
Definition: miniaudio.h:3861
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:6375
@ ma_share_mode_exclusive
Definition: miniaudio.h:6377
@ ma_share_mode_shared
Definition: miniaudio.h:6376
MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend)
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
Channel Conversion.
Definition: miniaudio.h:5216
@ ma_channel_conversion_path_mono_out
Definition: miniaudio.h:5219
@ ma_channel_conversion_path_mono_in
Definition: miniaudio.h:5220
@ ma_channel_conversion_path_passthrough
Definition: miniaudio.h:5218
@ ma_channel_conversion_path_unknown
Definition: miniaudio.h:5217
@ ma_channel_conversion_path_shuffle
Definition: miniaudio.h:5221
@ ma_channel_conversion_path_weights
Definition: miniaudio.h:5222
MA_API ma_data_source * ma_data_source_get_current(ma_data_source *pDataSource)
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:6171
@ ma_backend_sndio
Definition: miniaudio.h:6176
@ ma_backend_alsa
Definition: miniaudio.h:6180
@ ma_backend_webaudio
Definition: miniaudio.h:6184
@ ma_backend_winmm
Definition: miniaudio.h:6174
@ ma_backend_pulseaudio
Definition: miniaudio.h:6179
@ ma_backend_aaudio
Definition: miniaudio.h:6182
@ ma_backend_opensl
Definition: miniaudio.h:6183
@ ma_backend_oss
Definition: miniaudio.h:6178
@ ma_backend_custom
Definition: miniaudio.h:6185
@ ma_backend_coreaudio
Definition: miniaudio.h:6175
@ ma_backend_wasapi
Definition: miniaudio.h:6172
@ ma_backend_null
Definition: miniaudio.h:6186
@ ma_backend_audio4
Definition: miniaudio.h:6177
@ ma_backend_dsound
Definition: miniaudio.h:6173
@ ma_backend_jack
Definition: miniaudio.h:6181
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:9863
@ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC
Definition: miniaudio.h:9866
@ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE
Definition: miniaudio.h:9865
@ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM
Definition: miniaudio.h:9864
@ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT
Definition: miniaudio.h:9867
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:9259
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:4150
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_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_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 float ma_sound_get_current_fade_volume(ma_sound *pSound)
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)
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:3705
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_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_lpf_init(const ma_lpf_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_lpf *pLPF)
MA_API void ma_sound_set_positioning(ma_sound *pSound, ma_positioning positioning)
MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(ma_data_source *pDataSource)
MA_API void ma_fader_set_fade(ma_fader *pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames)
ma_aaudio_usage
Definition: miniaudio.h:6430
@ ma_aaudio_usage_emergency
Definition: miniaudio.h:6433
@ ma_aaudio_usage_default
Definition: miniaudio.h:6431
@ ma_aaudio_usage_voice_communication_signalling
Definition: miniaudio.h:6447
@ ma_aaudio_usage_voice_communication
Definition: miniaudio.h:6446
@ ma_aaudio_usage_assitant
Definition: miniaudio.h:6440
@ ma_aaudio_usage_safety
Definition: miniaudio.h:6434
@ ma_aaudio_usage_media
Definition: miniaudio.h:6442
@ ma_aaudio_usage_notification
Definition: miniaudio.h:6443
@ ma_aaudio_usage_announcement
Definition: miniaudio.h:6432
@ ma_aaudio_usage_vehicle_status
Definition: miniaudio.h:6435
@ ma_aaudio_usage_assistance_navigation_guidance
Definition: miniaudio.h:6438
@ ma_aaudio_usage_assistance_sonification
Definition: miniaudio.h:6439
@ ma_aaudio_usage_notification_event
Definition: miniaudio.h:6444
@ ma_aaudio_usage_assistance_accessibility
Definition: miniaudio.h:6437
@ ma_aaudio_usage_alarm
Definition: miniaudio.h:6436
@ ma_aaudio_usage_game
Definition: miniaudio.h:6441
@ ma_aaudio_usage_notification_ringtone
Definition: miniaudio.h:6445
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 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 ma_result ma_pcm_rb_seek_read(ma_pcm_rb *pRB, ma_uint32 offsetInFrames)
ma_seek_origin
Definition: miniaudio.h:9460
@ ma_seek_origin_end
Definition: miniaudio.h:9463
@ ma_seek_origin_start
Definition: miniaudio.h:9461
@ ma_seek_origin_current
Definition: miniaudio.h:9462
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)
MA_API ma_data_source * ma_data_source_get_next(ma_data_source *pDataSource)
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:9917
@ MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING
Definition: miniaudio.h:9919
@ MA_RESOURCE_MANAGER_FLAG_NO_THREADING
Definition: miniaudio.h:9922
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:6462
@ ma_aaudio_input_preset_voice_communication
Definition: miniaudio.h:6468
@ ma_aaudio_input_preset_default
Definition: miniaudio.h:6463
@ ma_aaudio_input_preset_camcorder
Definition: miniaudio.h:6465
@ ma_aaudio_input_preset_voice_performance
Definition: miniaudio.h:6469
@ ma_aaudio_input_preset_unprocessed
Definition: miniaudio.h:6466
@ ma_aaudio_input_preset_generic
Definition: miniaudio.h:6464
@ ma_aaudio_input_preset_voice_recognition
Definition: miniaudio.h:6467
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)
signed int ma_int32
Definition: miniaudio.h:3676
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:10760
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 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:9553
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:3724
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)
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:6340
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_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:3706
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)
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)
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:6419
@ ma_opensl_recording_preset_default
Definition: miniaudio.h:6420
@ ma_opensl_recording_preset_voice_communication
Definition: miniaudio.h:6424
@ ma_opensl_recording_preset_generic
Definition: miniaudio.h:6421
@ ma_opensl_recording_preset_voice_unprocessed
Definition: miniaudio.h:6425
@ ma_opensl_recording_preset_voice_recognition
Definition: miniaudio.h:6423
@ ma_opensl_recording_preset_camcorder
Definition: miniaudio.h:6422
ma_standard_channel_map
Definition: miniaudio.h:4116
@ ma_standard_channel_map_default
Definition: miniaudio.h:4125
@ ma_standard_channel_map_vorbis
Definition: miniaudio.h:4121
@ ma_standard_channel_map_sound4
Definition: miniaudio.h:4122
@ ma_standard_channel_map_sndio
Definition: miniaudio.h:4123
@ ma_standard_channel_map_webaudio
Definition: miniaudio.h:4124
@ ma_standard_channel_map_flac
Definition: miniaudio.h:4120
@ ma_standard_channel_map_alsa
Definition: miniaudio.h:4118
@ ma_standard_channel_map_rfc3551
Definition: miniaudio.h:4119
@ ma_standard_channel_map_microsoft
Definition: miniaudio.h:4117
MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config *pConfig, ma_bpf2 *pBPF)
ma_job_type
Definition: miniaudio.h:5855
@ MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM
Definition: miniaudio.h:5869
@ MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM
Definition: miniaudio.h:5868
@ MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER
Definition: miniaudio.h:5864
@ MA_JOB_TYPE_QUIT
Definition: miniaudio.h:5857
@ MA_JOB_TYPE_CUSTOM
Definition: miniaudio.h:5858
@ MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER
Definition: miniaudio.h:5865
@ MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM
Definition: miniaudio.h:5867
@ MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE
Definition: miniaudio.h:5861
@ MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM
Definition: miniaudio.h:5866
@ MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE
Definition: miniaudio.h:5863
@ MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE
Definition: miniaudio.h:5872
@ MA_JOB_TYPE_COUNT
Definition: miniaudio.h:5875
@ MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE
Definition: miniaudio.h:5862
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)
MA_API void ma_data_source_get_range_in_pcm_frames(ma_data_source *pDataSource, ma_uint64 *pRangeBegInFrames, ma_uint64 *pRangeEndInFrames)
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)
MA_API void ma_rb_uninit(ma_rb *pRB)
MA_API void ma_sound_set_max_gain(ma_sound *pSound, float maxGain)
ma_device_state
Definition: miniaudio.h:6151
@ ma_device_state_uninitialized
Definition: miniaudio.h:6152
@ ma_device_state_stopped
Definition: miniaudio.h:6153
@ ma_device_state_starting
Definition: miniaudio.h:6155
@ ma_device_state_stopping
Definition: miniaudio.h:6156
@ ma_device_state_started
Definition: miniaudio.h:6154
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
Data Source.
Definition: miniaudio.h:9257
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:6625
ma_node_flags
Definition: miniaudio.h:10184
@ MA_NODE_FLAG_SILENT_OUTPUT
Definition: miniaudio.h:10189
@ MA_NODE_FLAG_CONTINUOUS_PROCESSING
Definition: miniaudio.h:10186
@ MA_NODE_FLAG_PASSTHROUGH
Definition: miniaudio.h:10185
@ MA_NODE_FLAG_ALLOW_NULL_INPUT
Definition: miniaudio.h:10187
@ MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES
Definition: miniaudio.h:10188
MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source *pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames)
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 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)
MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder *pDecoder, ma_uint64 *pLength)
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:6407
@ ma_opensl_stream_type_media
Definition: miniaudio.h:6412
@ ma_opensl_stream_type_ring
Definition: miniaudio.h:6411
@ ma_opensl_stream_type_alarm
Definition: miniaudio.h:6413
@ ma_opensl_stream_type_notification
Definition: miniaudio.h:6414
@ ma_opensl_stream_type_voice
Definition: miniaudio.h:6409
@ ma_opensl_stream_type_default
Definition: miniaudio.h:6408
@ ma_opensl_stream_type_system
Definition: miniaudio.h:6410
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_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:4177
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_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:4052
@ ma_stream_layout_deinterleaved
Definition: miniaudio.h:4054
@ ma_stream_layout_interleaved
Definition: miniaudio.h:4053
MA_API void ma_sound_set_pitch(ma_sound *pSound, float pitch)
MA_API ma_result ma_waveform_set_amplitude(ma_waveform *pWaveform, double amplitude)
ma_pthread_t ma_thread
Definition: miniaudio.h:4170
MA_API void ma_pcm_u8_to_s32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
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:3634
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_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_result ma_data_source_set_current(ma_data_source *pDataSource, ma_data_source *pCurrentDataSource)
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_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:4850
@ ma_attenuation_model_inverse
Definition: miniaudio.h:4852
@ ma_attenuation_model_none
Definition: miniaudio.h:4851
@ ma_attenuation_model_linear
Definition: miniaudio.h:4853
@ ma_attenuation_model_exponential
Definition: miniaudio.h:4854
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:10176
#define MA_MAX_CHANNELS
Definition: miniaudio.h:4039
MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer *pSpatializer)
signed long long ma_int64
Definition: miniaudio.h:3689
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 void ma_data_source_get_loop_point_in_pcm_frames(ma_data_source *pDataSource, ma_uint64 *pLoopBegInFrames, ma_uint64 *pLoopEndInFrames)
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)
void ma_vfs
Definition: miniaudio.h:9450
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:9789
@ ma_noise_type_brownian
Definition: miniaudio.h:9792
@ ma_noise_type_white
Definition: miniaudio.h:9790
@ ma_noise_type_pink
Definition: miniaudio.h:9791
ma_result(* ma_encoder_init_proc)(ma_encoder *pEncoder)
Definition: miniaudio.h:9699
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_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:3958
@ MA_NO_DATA_AVAILABLE
Definition: miniaudio.h:3991
@ MA_IS_DIRECTORY
Definition: miniaudio.h:3974
@ MA_NO_ADDRESS
Definition: miniaudio.h:3997
@ MA_FAILED_TO_STOP_BACKEND_DEVICE
Definition: miniaudio.h:4033
@ MA_MEMORY_ALREADY_MAPPED
Definition: miniaudio.h:4011
@ MA_DOES_NOT_EXIST
Definition: miniaudio.h:3966
@ MA_PATH_TOO_LONG
Definition: miniaudio.h:3971
@ MA_PROTOCOL_UNAVAILABLE
Definition: miniaudio.h:3999
@ MA_API_NOT_FOUND
Definition: miniaudio.h:4019
@ MA_NOT_IMPLEMENTED
Definition: miniaudio.h:3988
@ MA_NO_NETWORK
Definition: miniaudio.h:3994
@ MA_TIMEOUT
Definition: miniaudio.h:3993
@ MA_ALREADY_EXISTS
Definition: miniaudio.h:3967
@ MA_IO_ERROR
Definition: miniaudio.h:3979
@ MA_FAILED_TO_OPEN_BACKEND_DEVICE
Definition: miniaudio.h:4031
@ MA_BAD_MESSAGE
Definition: miniaudio.h:3990
@ MA_ERROR
Definition: miniaudio.h:3960
@ MA_OUT_OF_RANGE
Definition: miniaudio.h:3964
@ MA_CANCELLED
Definition: miniaudio.h:4010
@ MA_DEVICE_NOT_STOPPED
Definition: miniaudio.h:4027
@ MA_BAD_SEEK
Definition: miniaudio.h:3984
@ MA_ALREADY_IN_USE
Definition: miniaudio.h:3982
@ MA_INVALID_ARGS
Definition: miniaudio.h:3961
@ MA_FAILED_TO_START_BACKEND_DEVICE
Definition: miniaudio.h:4032
@ MA_ADDRESS_FAMILY_NOT_SUPPORTED
Definition: miniaudio.h:4002
@ MA_INVALID_FILE
Definition: miniaudio.h:3969
@ MA_DEADLOCK
Definition: miniaudio.h:3986
@ MA_NO_BACKEND
Definition: miniaudio.h:4017
@ MA_DEVICE_TYPE_NOT_SUPPORTED
Definition: miniaudio.h:4015
@ MA_BAD_PROTOCOL
Definition: miniaudio.h:3998
@ MA_PROTOCOL_NOT_SUPPORTED
Definition: miniaudio.h:4000
@ MA_NOT_CONNECTED
Definition: miniaudio.h:4006
@ MA_INVALID_OPERATION
Definition: miniaudio.h:3962
@ MA_DEVICE_NOT_INITIALIZED
Definition: miniaudio.h:4024
@ MA_NOT_SOCKET
Definition: miniaudio.h:3996
@ MA_BUSY
Definition: miniaudio.h:3978
@ MA_TOO_MANY_OPEN_FILES
Definition: miniaudio.h:3968
@ MA_PROTOCOL_FAMILY_NOT_SUPPORTED
Definition: miniaudio.h:4001
@ MA_FAILED_TO_INIT_BACKEND
Definition: miniaudio.h:4030
@ MA_NOT_DIRECTORY
Definition: miniaudio.h:3973
@ MA_NO_HOST
Definition: miniaudio.h:4008
@ MA_NOT_UNIQUE
Definition: miniaudio.h:3995
@ MA_CONNECTION_RESET
Definition: miniaudio.h:4004
@ MA_LOOP
Definition: miniaudio.h:4021
@ MA_BAD_PIPE
Definition: miniaudio.h:3985
@ MA_INTERRUPT
Definition: miniaudio.h:3980
@ MA_ALREADY_CONNECTED
Definition: miniaudio.h:4005
@ MA_DEVICE_NOT_STARTED
Definition: miniaudio.h:4026
@ MA_TOO_BIG
Definition: miniaudio.h:3970
@ MA_ACCESS_DENIED
Definition: miniaudio.h:3965
@ MA_AT_END
Definition: miniaudio.h:3976
@ MA_UNAVAILABLE
Definition: miniaudio.h:3981
@ MA_IN_PROGRESS
Definition: miniaudio.h:4009
@ MA_INVALID_DATA
Definition: miniaudio.h:3992
@ MA_OUT_OF_MEMORY
Definition: miniaudio.h:3963
@ MA_BAD_ADDRESS
Definition: miniaudio.h:3983
@ MA_CONNECTION_REFUSED
Definition: miniaudio.h:4007
@ MA_DIRECTORY_NOT_EMPTY
Definition: miniaudio.h:3975
@ MA_DEVICE_ALREADY_INITIALIZED
Definition: miniaudio.h:4025
@ MA_NO_SPACE
Definition: miniaudio.h:3977
@ MA_NAME_TOO_LONG
Definition: miniaudio.h:3972
@ MA_TOO_MANY_LINKS
Definition: miniaudio.h:3987
@ MA_NO_DEVICE
Definition: miniaudio.h:4018
@ MA_SHARE_MODE_NOT_SUPPORTED
Definition: miniaudio.h:4016
@ MA_INVALID_DEVICE_CONFIG
Definition: miniaudio.h:4020
@ MA_NO_MESSAGE
Definition: miniaudio.h:3989
@ MA_SOCKET_NOT_SUPPORTED
Definition: miniaudio.h:4003
@ MA_FORMAT_NOT_SUPPORTED
Definition: miniaudio.h:4014
@ MA_SUCCESS
Definition: miniaudio.h:3959
ma_stream_format
Definition: miniaudio.h:4047
@ ma_stream_format_pcm
Definition: miniaudio.h:4048
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:4239
ma_encoding_format
Definition: miniaudio.h:9511
@ ma_encoding_format_flac
Definition: miniaudio.h:9514
@ ma_encoding_format_mp3
Definition: miniaudio.h:9515
@ ma_encoding_format_vorbis
Definition: miniaudio.h:9516
@ ma_encoding_format_unknown
Definition: miniaudio.h:9512
@ ma_encoding_format_wav
Definition: miniaudio.h:9513
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:9943
@ ma_resource_manager_data_supply_type_decoded
Definition: miniaudio.h:9946
@ ma_resource_manager_data_supply_type_unknown
Definition: miniaudio.h:9944
@ ma_resource_manager_data_supply_type_encoded
Definition: miniaudio.h:9945
@ ma_resource_manager_data_supply_type_decoded_paged
Definition: miniaudio.h:9947
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_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:4065
@ ma_format_s24
Definition: miniaudio.h:4073
@ ma_format_count
Definition: miniaudio.h:4076
@ ma_format_f32
Definition: miniaudio.h:4075
@ ma_format_s16
Definition: miniaudio.h:4072
@ ma_format_u8
Definition: miniaudio.h:4071
@ ma_format_unknown
Definition: miniaudio.h:4070
@ ma_format_s32
Definition: miniaudio.h:4074
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:5303
@ ma_data_converter_execution_path_channels_first
Definition: miniaudio.h:5309
@ ma_data_converter_execution_path_resample_first
Definition: miniaudio.h:5308
@ ma_data_converter_execution_path_resample_only
Definition: miniaudio.h:5307
@ ma_data_converter_execution_path_passthrough
Definition: miniaudio.h:5304
@ ma_data_converter_execution_path_format_only
Definition: miniaudio.h:5305
@ ma_data_converter_execution_path_channels_only
Definition: miniaudio.h:5306
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:9454
@ MA_OPEN_MODE_WRITE
Definition: miniaudio.h:9456
@ MA_OPEN_MODE_READ
Definition: miniaudio.h:9455
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:10645
@ MA_SOUND_FLAG_STREAM
Definition: miniaudio.h:10646
@ MA_SOUND_FLAG_WAIT_INIT
Definition: miniaudio.h:10649
@ MA_SOUND_FLAG_NO_PITCH
Definition: miniaudio.h:10651
@ MA_SOUND_FLAG_ASYNC
Definition: miniaudio.h:10648
@ MA_SOUND_FLAG_DECODE
Definition: miniaudio.h:10647
@ MA_SOUND_FLAG_NO_SPATIALIZATION
Definition: miniaudio.h:10652
@ MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT
Definition: miniaudio.h:10650
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 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_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_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:9913
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)
ma_device_notification_type
Definition: miniaudio.h:6224
@ ma_device_notification_type_started
Definition: miniaudio.h:6225
@ ma_device_notification_type_stopped
Definition: miniaudio.h:6226
@ ma_device_notification_type_interruption_ended
Definition: miniaudio.h:6229
@ ma_device_notification_type_rerouted
Definition: miniaudio.h:6227
@ ma_device_notification_type_interruption_began
Definition: miniaudio.h:6228
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:3674
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:3632
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)
void(* ma_proc)(void)
Definition: miniaudio.h:3710
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:6364
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:4080
@ ma_standard_sample_rate_44100
Definition: miniaudio.h:4083
@ ma_standard_sample_rate_max
Definition: miniaudio.h:4102
@ ma_standard_sample_rate_176400
Definition: miniaudio.h:4091
@ ma_standard_sample_rate_min
Definition: miniaudio.h:4101
@ ma_standard_sample_rate_24000
Definition: miniaudio.h:4086
@ ma_standard_sample_rate_352800
Definition: miniaudio.h:4098
@ ma_standard_sample_rate_32000
Definition: miniaudio.h:4085
@ ma_standard_sample_rate_count
Definition: miniaudio.h:4103
@ ma_standard_sample_rate_8000
Definition: miniaudio.h:4096
@ ma_standard_sample_rate_192000
Definition: miniaudio.h:4092
@ ma_standard_sample_rate_88200
Definition: miniaudio.h:4089
@ ma_standard_sample_rate_16000
Definition: miniaudio.h:4094
@ ma_standard_sample_rate_48000
Definition: miniaudio.h:4082
@ ma_standard_sample_rate_96000
Definition: miniaudio.h:4090
@ ma_standard_sample_rate_11025
Definition: miniaudio.h:4095
@ ma_standard_sample_rate_384000
Definition: miniaudio.h:4099
@ ma_standard_sample_rate_22050
Definition: miniaudio.h:4087
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:4272
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_node_state
Definition: miniaudio.h:10195
@ ma_node_state_started
Definition: miniaudio.h:10196
@ ma_node_state_stopped
Definition: miniaudio.h:10197
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:3798
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_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_slot_allocator_get_heap_size(const ma_slot_allocator_config *pConfig, size_t *pHeapSizeInBytes)
#define MA_ATTRIBUTE_FORMAT(fmt, va)
Logging.
Definition: miniaudio.h:4235
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:3708
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:4858
@ ma_positioning_relative
Definition: miniaudio.h:4860
@ ma_positioning_absolute
Definition: miniaudio.h:4859
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)
MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler *pResampler, float ratioInOut)
ma_waveform_type
Generation.
Definition: miniaudio.h:9752
@ ma_waveform_type_square
Definition: miniaudio.h:9754
@ ma_waveform_type_sawtooth
Definition: miniaudio.h:9756
@ ma_waveform_type_sine
Definition: miniaudio.h:9753
@ ma_waveform_type_triangle
Definition: miniaudio.h:9755
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 const char * ma_get_format_name(ma_format format)
struct ma_notch2_config ma_notch_config
#define MA_LISTENER_INDEX_CLOSEST
Definition: miniaudio.h:10659
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)
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:6508
#define MA_MAX_DEVICE_NAME_LENGTH
Definition: miniaudio.h:6511
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:9697
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_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 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)
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)
#define AudioBuffer
Definition: raudio.c:349
#define DRMP3_FREE
Definition: raudio.c:234
#define DRWAV_MALLOC
Definition: raudio.c:223
#define MA_FREE
Definition: raudio.c:164
#define DRWAV_FREE
Definition: raudio.c:225
#define DRMP3_REALLOC
Definition: raudio.c:233
#define DRWAV_REALLOC
Definition: raudio.c:224
#define DRMP3_MALLOC
Definition: raudio.c:232
#define MA_MALLOC
raudio v1.0 - A simple and easy-to-use audio library based on miniaudio
Definition: raudio.c:163
STBVDEF stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc_buffer)
@ VORBIS_need_more_data
Definition: stb_vorbis.h:361
STBVDEF void stb_vorbis_close(stb_vorbis *f)
STBVDEF int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats)
STBVDEF int stb_vorbis_decode_frame_pushdata(stb_vorbis *f, const unsigned char *datablock, int datablock_length_in_bytes, int *channels, float ***output, int *samples)
STBVDEF void stb_vorbis_flush_pushdata(stb_vorbis *f)
STBVDEF stb_vorbis * stb_vorbis_open_pushdata(const unsigned char *datablock, int datablock_length_in_bytes, int *datablock_memory_consumed_in_bytes, int *error, const stb_vorbis_alloc *alloc_buffer)
struct stb_vorbis stb_vorbis
Definition: stb_vorbis.h:127
STBVDEF unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f)
STBVDEF int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number)
STBVDEF stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f)
STBVDEF stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc_buffer)
#define INT32_MAX
Definition: stdint.h:137
signed short int16_t
Definition: stdint.h:76
unsigned short uint16_t
Definition: stdint.h:79
signed __int64 int64_t
Definition: stdint.h:89
unsigned int uint32_t
Definition: stdint.h:80
signed int int32_t
Definition: stdint.h:77
unsigned char uint8_t
Definition: stdint.h:78
unsigned __int64 uint64_t
Definition: stdint.h:90
signed char int8_t
Definition: stdint.h:75
const drflac_uint8 * data
Definition: dr_flac.h:575
void(* onFree)(void *p, void *pUserData)
Definition: dr_flac.h:569
void *(* onMalloc)(size_t sz, void *pUserData)
Definition: dr_flac.h:567
void *(* onRealloc)(void *p, size_t sz, void *pUserData)
Definition: dr_flac.h:568
drflac_uint32 consumedBits
Definition: dr_flac.h:607
drflac_uint32 crc16CacheIgnoredBytes
Definition: dr_flac.h:622
drflac_seek_proc onSeek
Definition: dr_flac.h:587
drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)]
Definition: dr_flac.h:613
drflac_uint16 crc16
Definition: dr_flac.h:620
drflac_read_proc onRead
Definition: dr_flac.h:584
drflac_cache_t unalignedCache
Definition: dr_flac.h:601
drflac_uint32 nextL2Line
Definition: dr_flac.h:604
void * pUserData
Definition: dr_flac.h:590
size_t unalignedByteCount
Definition: dr_flac.h:598
drflac_cache_t crc16Cache
Definition: dr_flac.h:621
drflac_cache_t cache
Definition: dr_flac.h:614
drflac_uint32 countRemaining
Definition: dr_flac.h:1279
drflac_uint8 trackNumber
Definition: dr_flac.h:1296
drflac_bool8 isAudio
Definition: dr_flac.h:1298
drflac_uint8 indexCount
Definition: dr_flac.h:1300
drflac_bool8 preEmphasis
Definition: dr_flac.h:1299
const drflac_cuesheet_track_index * pIndexPoints
Definition: dr_flac.h:1301
drflac_uint64 offset
Definition: dr_flac.h:1295
drflac_uint16 blockSizeInPCMFrames
Definition: dr_flac.h:658
drflac_uint32 sampleRate
Definition: dr_flac.h:655
drflac_uint32 flacFrameNumber
Definition: dr_flac.h:652
drflac_uint64 pcmFrameNumber
Definition: dr_flac.h:646
drflac_uint8 crc8
Definition: dr_flac.h:670
drflac_uint8 channelAssignment
Definition: dr_flac.h:664
drflac_uint8 bitsPerSample
Definition: dr_flac.h:667
drflac_frame_header header
Definition: dr_flac.h:676
drflac_uint32 pcmFramesRemaining
Definition: dr_flac.h:682
drflac_subframe subframes[8]
Definition: dr_flac.h:685
drflac_uint32 vendorLength
Definition: dr_flac.h:451
drflac_uint32 rawDataSize
Definition: dr_flac.h:425
drflac_bool32 isCD
Definition: dr_flac.h:461
struct drflac_metadata::@1::@4 seektable
union drflac_metadata::@1 data
const void * pComments
Definition: dr_flac.h:454
const void * pRawData
Definition: dr_flac.h:422
struct drflac_metadata::@1::@6 cuesheet
drflac_uint32 seekpointCount
Definition: dr_flac.h:445
const char * vendor
Definition: dr_flac.h:452
drflac_uint32 descriptionLength
Definition: dr_flac.h:471
struct drflac_metadata::@1::@3 application
struct drflac_metadata::@1::@2 padding
drflac_uint8 trackCount
Definition: dr_flac.h:462
const char * mime
Definition: dr_flac.h:470
drflac_uint32 id
Definition: dr_flac.h:438
struct drflac_metadata::@1::@5 vorbis_comment
drflac_streaminfo streaminfo
Definition: dr_flac.h:429
const char * description
Definition: dr_flac.h:472
drflac_uint64 leadInSampleCount
Definition: dr_flac.h:460
drflac_uint32 commentCount
Definition: dr_flac.h:453
drflac_uint32 pictureDataSize
Definition: dr_flac.h:477
drflac_uint32 type
Definition: dr_flac.h:415
char catalog[128]
Definition: dr_flac.h:459
const drflac_uint8 * pPictureData
Definition: dr_flac.h:478
drflac_uint32 width
Definition: dr_flac.h:473
drflac_uint32 dataSize
Definition: dr_flac.h:440
const drflac_seekpoint * pSeekpoints
Definition: dr_flac.h:446
drflac_uint32 indexColorCount
Definition: dr_flac.h:476
drflac_uint32 colorDepth
Definition: dr_flac.h:475
struct drflac_metadata::@1::@7 picture
drflac_uint32 height
Definition: dr_flac.h:474
const void * pData
Definition: dr_flac.h:439
drflac_uint32 mimeLength
Definition: dr_flac.h:469
const void * pTrackData
Definition: dr_flac.h:463
drflac_uint64 flacFrameOffset
Definition: dr_flac.h:391
drflac_uint16 pcmFrameCount
Definition: dr_flac.h:392
drflac_uint64 firstPCMFrame
Definition: dr_flac.h:390
drflac_uint16 maxBlockSizeInPCMFrames
Definition: dr_flac.h:399
drflac_uint32 sampleRate
Definition: dr_flac.h:402
drflac_uint32 minFrameSizeInPCMFrames
Definition: dr_flac.h:400
drflac_uint8 channels
Definition: dr_flac.h:403
drflac_uint64 totalPCMFrameCount
Definition: dr_flac.h:405
drflac_uint32 maxFrameSizeInPCMFrames
Definition: dr_flac.h:401
drflac_uint8 bitsPerSample
Definition: dr_flac.h:404
drflac_uint8 md5[16]
Definition: dr_flac.h:406
drflac_uint16 minBlockSizeInPCMFrames
Definition: dr_flac.h:398
drflac_uint8 subframeType
Definition: dr_flac.h:628
drflac_uint8 wastedBitsPerSample
Definition: dr_flac.h:631
drflac_int32 * pSamplesS32
Definition: dr_flac.h:637
drflac_uint8 lpcOrder
Definition: dr_flac.h:634
drflac_uint32 countRemaining
Definition: dr_flac.h:1259
drflac_uint8 pExtraData[1]
Definition: dr_flac.h:762
drflac_int32 * pDecodedSamples
Definition: dr_flac.h:745
void * _oggbs
Definition: dr_flac.h:751
drflac_uint32 seekpointCount
Definition: dr_flac.h:726
void * pUserDataMD
Definition: dr_flac.h:694
drflac_bool32 _noBinarySearchSeek
Definition: dr_flac.h:755
drflac_meta_proc onMeta
Definition: dr_flac.h:691
drflac_bs bs
Definition: dr_flac.h:759
drflac_uint8 channels
Definition: dr_flac.h:707
drflac_uint8 bitsPerSample
Definition: dr_flac.h:710
drflac_bool32 _noBruteForceSeek
Definition: dr_flac.h:756
drflac_seekpoint * pSeekpoints
Definition: dr_flac.h:748
drflac_uint16 maxBlockSizeInPCMFrames
Definition: dr_flac.h:713
drflac_uint64 totalPCMFrameCount
Definition: dr_flac.h:719
drflac_frame currentFLACFrame
Definition: dr_flac.h:730
drflac__memory_stream memoryStream
Definition: dr_flac.h:741
drflac_allocation_callbacks allocationCallbacks
Definition: dr_flac.h:697
drflac_uint32 sampleRate
Definition: dr_flac.h:701
drflac_uint64 firstFLACFramePosInBytes
Definition: dr_flac.h:737
drflac_uint64 currentPCMFrame
Definition: dr_flac.h:734
drflac_container container
Definition: dr_flac.h:723
drflac_bool32 _noSeekTableSeek
Definition: dr_flac.h:754
void(* onFree)(void *p, void *pUserData)
Definition: dr_mp3.h:331
void *(* onRealloc)(void *p, size_t sz, void *pUserData)
Definition: dr_mp3.h:330
void *(* onMalloc)(size_t sz, void *pUserData)
Definition: dr_mp3.h:329
drmp3_uint32 sampleRate
Definition: dr_mp3.h:337
drmp3_uint32 channels
Definition: dr_mp3.h:336
drmp3_uint64 seekPosInBytes
Definition: dr_mp3.h:292
drmp3_uint64 pcmFrameIndex
Definition: dr_mp3.h:293
drmp3_uint16 mp3FramesToDiscard
Definition: dr_mp3.h:294
drmp3_uint16 pcmFramesToDiscard
Definition: dr_mp3.h:295
Definition: dr_mp3.h:341
drmp3_uint32 sampleRate
Definition: dr_mp3.h:345
size_t dataConsumed
Definition: dr_mp3.h:361
drmp3_uint32 pcmFramesConsumedInMP3Frame
Definition: dr_mp3.h:352
drmp3_uint64 currentPCMFrame
Definition: dr_mp3.h:355
drmp3_uint64 streamCursor
Definition: dr_mp3.h:356
drmp3_uint8 pcmFrames[sizeof(float) *DRMP3_MAX_SAMPLES_PER_FRAME]
Definition: dr_mp3.h:354
struct drmp3::@8 memory
drmp3_uint8 * pData
Definition: dr_mp3.h:362
drmp3_seek_proc onSeek
Definition: dr_mp3.h:347
drmp3_read_proc onRead
Definition: dr_mp3.h:346
drmp3_allocation_callbacks allocationCallbacks
Definition: dr_mp3.h:349
size_t currentReadPos
Definition: dr_mp3.h:368
size_t dataCapacity
Definition: dr_mp3.h:360
size_t dataSize
Definition: dr_mp3.h:359
void * pUserData
Definition: dr_mp3.h:348
drmp3_uint32 channels
Definition: dr_mp3.h:344
drmp3_uint32 mp3FrameSampleRate
Definition: dr_mp3.h:351
drmp3_seek_point * pSeekPoints
Definition: dr_mp3.h:357
drmp3_uint32 pcmFramesRemainingInMP3Frame
Definition: dr_mp3.h:353
drmp3_uint32 seekPointCount
Definition: dr_mp3.h:358
drmp3_bool32 atEnd
Definition: dr_mp3.h:363
drmp3_uint32 mp3FrameChannels
Definition: dr_mp3.h:350
drmp3dec decoder
Definition: dr_mp3.h:342
drmp3_uint8 reserv_buf[511]
Definition: dr_mp3.h:266
int free_format_bytes
Definition: dr_mp3.h:265
int reserv
Definition: dr_mp3.h:265
drmp3_uint8 header[4]
Definition: dr_mp3.h:266
float mdct_overlap[2][9 *32]
Definition: dr_mp3.h:264
float qmf_state[15 *2 *32]
Definition: dr_mp3.h:264
const drwav_uint8 * data
Definition: dr_wav.h:420
size_t currentReadPos
Definition: dr_wav.h:422
drwav_uint16 reserved1
Definition: dr_wav.h:640
drwav_uint16 midiUnityNote
Definition: dr_wav.h:637
drwav_uint32 flags
Definition: dr_wav.h:634
float tempo
Definition: dr_wav.h:651
drwav_uint32 numBeats
Definition: dr_wav.h:644
drwav_uint16 meterNumerator
Definition: dr_wav.h:648
float reserved2
Definition: dr_wav.h:641
drwav_uint16 meterDenominator
Definition: dr_wav.h:647
void *(* onRealloc)(void *p, size_t sz, void *pUserData)
Definition: dr_wav.h:413
void(* onFree)(void *p, void *pUserData)
Definition: dr_wav.h:414
void *(* onMalloc)(size_t sz, void *pUserData)
Definition: dr_wav.h:412
drwav_uint16 version
Definition: dr_wav.h:692
char * pOriginatorName
Definition: dr_wav.h:687
char * pCodingHistory
Definition: dr_wav.h:698
drwav_uint16 maxTruePeakLevel
Definition: dr_wav.h:707
drwav_uint16 loudnessValue
Definition: dr_wav.h:705
char pOriginationTime[8]
Definition: dr_wav.h:690
char pOriginationDate[10]
Definition: dr_wav.h:689
char * pOriginatorReference
Definition: dr_wav.h:688
drwav_uint16 maxShortTermLoudness
Definition: dr_wav.h:709
drwav_uint16 maxMomentaryLoudness
Definition: dr_wav.h:708
char * pDescription
Definition: dr_wav.h:686
drwav_uint32 codingHistorySize
Definition: dr_wav.h:699
drwav_uint16 loudnessRange
Definition: dr_wav.h:706
drwav_uint64 timeReference
Definition: dr_wav.h:691
drwav_uint8 * pUMID
Definition: dr_wav.h:702
drwav_uint8 fourcc[4]
Definition: dr_wav.h:285
drwav_uint8 guid[16]
Definition: dr_wav.h:286
union drwav_chunk_header::@9 id
drwav_uint64 sizeInBytes
Definition: dr_wav.h:290
unsigned int paddingSize
Definition: dr_wav.h:296
drwav_uint32 blockStart
Definition: dr_wav.h:605
drwav_uint32 id
Definition: dr_wav.h:593
drwav_uint8 dataChunkId[4]
Definition: dr_wav.h:599
drwav_uint32 sampleByteOffset
Definition: dr_wav.h:608
drwav_uint32 playOrderPosition
Definition: dr_wav.h:596
drwav_uint32 chunkStart
Definition: dr_wav.h:602
drwav_uint32 cuePointCount
Definition: dr_wav.h:613
drwav_cue_point * pCuePoints
Definition: dr_wav.h:614
drwav_uint32 bitsPerSample
Definition: dr_wav.h:441
drwav_uint32 channels
Definition: dr_wav.h:439
drwav_uint32 format
Definition: dr_wav.h:438
drwav_container container
Definition: dr_wav.h:437
drwav_uint32 sampleRate
Definition: dr_wav.h:440
drwav_uint16 bitsPerSample
Definition: dr_wav.h:320
drwav_uint16 formatTag
Definition: dr_wav.h:305
drwav_uint32 avgBytesPerSec
Definition: dr_wav.h:314
drwav_uint32 channelMask
Definition: dr_wav.h:333
drwav_uint32 sampleRate
Definition: dr_wav.h:311
drwav_uint16 blockAlign
Definition: dr_wav.h:317
drwav_uint16 channels
Definition: dr_wav.h:308
drwav_uint8 subFormat[16]
Definition: dr_wav.h:336
drwav_uint16 validBitsPerSample
Definition: dr_wav.h:330
drwav_uint16 extendedSize
Definition: dr_wav.h:323
drwav_int8 highVelocity
Definition: dr_wav.h:581
drwav_int8 lowNote
Definition: dr_wav.h:578
drwav_int8 midiUnityNote
Definition: dr_wav.h:575
drwav_int8 highNote
Definition: dr_wav.h:579
drwav_int8 gainDecibels
Definition: dr_wav.h:577
drwav_int8 lowVelocity
Definition: dr_wav.h:580
drwav_int8 fineTuneCents
Definition: dr_wav.h:576
drwav_uint32 stringLength
Definition: dr_wav.h:722
drwav_uint32 stringLength
Definition: dr_wav.h:667
drwav_uint32 cuePointId
Definition: dr_wav.h:664
drwav_uint8 purposeId[4]
Definition: dr_wav.h:743
drwav_list_label_or_note labelOrNote
Definition: dr_wav.h:798
drwav_bext bext
Definition: dr_wav.h:797
drwav_smpl smpl
Definition: dr_wav.h:794
drwav_acid acid
Definition: dr_wav.h:795
drwav_metadata_type type
Definition: dr_wav.h:789
drwav_list_info_text infoText
Definition: dr_wav.h:800
drwav_unknown_metadata unknown
Definition: dr_wav.h:801
drwav_inst inst
Definition: dr_wav.h:796
drwav_cue cue
Definition: dr_wav.h:793
drwav_list_labelled_cue_region labelledCueRegion
Definition: dr_wav.h:799
union drwav_metadata::@10 data
drwav_uint32 sampleFraction
Definition: dr_wav.h:532
drwav_uint32 cuePointId
Definition: dr_wav.h:520
drwav_uint32 firstSampleByteOffset
Definition: dr_wav.h:526
drwav_uint32 type
Definition: dr_wav.h:523
drwav_uint32 lastSampleByteOffset
Definition: dr_wav.h:529
drwav_uint32 playCount
Definition: dr_wav.h:535
drwav_uint32 manufacturerId
Definition: dr_wav.h:541
drwav_uint32 samplePeriodNanoseconds
Definition: dr_wav.h:545
drwav_uint32 midiPitchFraction
Definition: dr_wav.h:551
drwav_uint32 smpteOffset
Definition: dr_wav.h:555
drwav_uint8 * pSamplerSpecificData
Definition: dr_wav.h:564
drwav_uint32 smpteFormat
Definition: dr_wav.h:554
drwav_uint32 sampleLoopCount
Definition: dr_wav.h:558
drwav_uint32 samplerSpecificDataSizeInBytes
Definition: dr_wav.h:561
drwav_uint32 productId
Definition: dr_wav.h:542
drwav_smpl_loop * pLoops
Definition: dr_wav.h:563
drwav_uint32 midiUnityNote
Definition: dr_wav.h:548
drwav_uint8 id[4]
Definition: dr_wav.h:777
drwav_metadata_location chunkLocation
Definition: dr_wav.h:778
drwav_uint32 dataSizeInBytes
Definition: dr_wav.h:779
drwav_uint8 * pData
Definition: dr_wav.h:780
Definition: dr_wav.h:806
drwav_uint32 metadataCount
Definition: dr_wav.h:874
drwav_uint64 dataChunkDataSizeTargetWrite
Definition: dr_wav.h:863
drwav_uint16 predictor[2]
Definition: dr_wav.h:886
drwav_write_proc onWrite
Definition: dr_wav.h:811
struct drwav::@11 msadpcm
drwav_container container
Definition: dr_wav.h:824
drwav_int32 prevFrames[2][2]
Definition: dr_wav.h:890
drwav_metadata_type allowedMetadataTypes
Definition: dr_wav.h:870
drwav_uint64 readCursorInPCMFrames
Definition: dr_wav.h:856
drwav_int32 cachedFrames[4]
Definition: dr_wav.h:888
drwav_int32 stepIndex[2]
Definition: dr_wav.h:898
drwav_uint16 channels
Definition: dr_wav.h:834
drwav_int32 delta[2]
Definition: dr_wav.h:887
drwav__memory_stream memoryStream
Definition: dr_wav.h:878
drwav_allocation_callbacks allocationCallbacks
Definition: dr_wav.h:820
drwav__memory_stream_write memoryStreamWrite
Definition: dr_wav.h:879
drwav_bool32 isSequentialWrite
Definition: dr_wav.h:866
drwav_uint32 sampleRate
Definition: dr_wav.h:831
drwav_seek_proc onSeek
Definition: dr_wav.h:814
drwav_uint16 translatedFormatTag
Definition: dr_wav.h:840
drwav_fmt fmt
Definition: dr_wav.h:828
void * pUserData
Definition: dr_wav.h:817
drwav_uint64 dataChunkDataSize
Definition: dr_wav.h:847
struct drwav::@12 ima
drwav_uint32 bytesRemainingInBlock
Definition: dr_wav.h:885
drwav_read_proc onRead
Definition: dr_wav.h:808
drwav_uint16 bitsPerSample
Definition: dr_wav.h:837
drwav_uint64 dataChunkDataPos
Definition: dr_wav.h:850
drwav_uint64 totalPCMFrameCount
Definition: dr_wav.h:843
drwav_metadata * pMetadata
Definition: dr_wav.h:873
drwav_uint32 cachedFrameCount
Definition: dr_wav.h:889
drwav_uint64 bytesRemaining
Definition: dr_wav.h:853
void *(* onMalloc)(size_t sz, void *pUserData)
Definition: miniaudio.h:4138
void(* onFree)(void *p, void *pUserData)
Definition: miniaudio.h:4140
void *(* onRealloc)(void *p, size_t sz, void *pUserData)
Definition: miniaudio.h:4139
void(* onSignal)(ma_async_notification *pNotification)
Definition: miniaudio.h:5748
ma_async_notification_callbacks cb
Definition: miniaudio.h:5776
ma_async_notification_callbacks cb
Definition: miniaudio.h:5761
const void * pData
Definition: miniaudio.h:9348
ma_allocation_callbacks allocationCallbacks
Definition: miniaudio.h:9349
ma_uint64 sizeInFrames
Definition: miniaudio.h:9325
ma_uint32 channels
Definition: miniaudio.h:9323
ma_data_source_base ds
Definition: miniaudio.h:9321
const void * pData
Definition: miniaudio.h:9326
ma_audio_buffer_ref ref
Definition: miniaudio.h:9356
ma_allocation_callbacks allocationCallbacks
Definition: miniaudio.h:9357
ma_uint8 _pExtraData[1]
Definition: miniaudio.h:9359
ma_bool32 ownsData
Definition: miniaudio.h:9358
ma_result(* onDeviceStop)(ma_device *pDevice)
Definition: miniaudio.h:6721
ma_result(* onDeviceUninit)(ma_device *pDevice)
Definition: miniaudio.h:6719
ma_result(* onDeviceWrite)(ma_device *pDevice, const void *pFrames, ma_uint32 frameCount, ma_uint32 *pFramesWritten)
Definition: miniaudio.h:6723
ma_result(* onDeviceRead)(ma_device *pDevice, void *pFrames, ma_uint32 frameCount, ma_uint32 *pFramesRead)
Definition: miniaudio.h:6722
ma_result(* onDeviceInit)(ma_device *pDevice, const ma_device_config *pConfig, ma_device_descriptor *pDescriptorPlayback, ma_device_descriptor *pDescriptorCapture)
Definition: miniaudio.h:6718
ma_result(* onDeviceGetInfo)(ma_device *pDevice, ma_device_type type, ma_device_info *pDeviceInfo)
Definition: miniaudio.h:6726
ma_result(* onDeviceStart)(ma_device *pDevice)
Definition: miniaudio.h:6720
ma_result(* onContextInit)(ma_context *pContext, const ma_context_config *pConfig, ma_backend_callbacks *pCallbacks)
Definition: miniaudio.h:6714
ma_result(* onDeviceDataLoop)(ma_device *pDevice)
Definition: miniaudio.h:6724
ma_result(* onDeviceDataLoopWakeup)(ma_device *pDevice)
Definition: miniaudio.h:6725
ma_result(* onContextUninit)(ma_context *pContext)
Definition: miniaudio.h:6715
ma_result(* onContextGetDeviceInfo)(ma_context *pContext, ma_device_type deviceType, const ma_device_id *pDeviceID, ma_device_info *pDeviceInfo)
Definition: miniaudio.h:6717
ma_result(* onContextEnumerateDevices)(ma_context *pContext, ma_enum_devices_callback_proc callback, void *pUserData)
Definition: miniaudio.h:6716
ma_uint32 channels
Definition: miniaudio.h:4316
ma_format format
Definition: miniaudio.h:4315
ma_node_config nodeConfig
Definition: miniaudio.h:10428
ma_biquad_config biquad
Definition: miniaudio.h:10429
ma_node_base baseNode
Definition: miniaudio.h:10437
ma_biquad biquad
Definition: miniaudio.h:10438
ma_biquad_coefficient b0
Definition: miniaudio.h:4331
ma_biquad_coefficient a1
Definition: miniaudio.h:4334
ma_biquad_coefficient a2
Definition: miniaudio.h:4335
ma_biquad_coefficient b1
Definition: miniaudio.h:4332
ma_format format
Definition: miniaudio.h:4329
void * _pHeap
Definition: miniaudio.h:4340
ma_biquad_coefficient b2
Definition: miniaudio.h:4333
ma_biquad_coefficient * pR2
Definition: miniaudio.h:4337
ma_biquad_coefficient * pR1
Definition: miniaudio.h:4336
ma_bool32 _ownsHeap
Definition: miniaudio.h:4341
ma_uint32 channels
Definition: miniaudio.h:4330
Band-Pass Filtering.
Definition: miniaudio.h:4531
ma_format format
Definition: miniaudio.h:4532
ma_uint32 sampleRate
Definition: miniaudio.h:4534
ma_uint32 channels
Definition: miniaudio.h:4533
double cutoffFrequency
Definition: miniaudio.h:4535
ma_biquad bq
Definition: miniaudio.h:4543
ma_uint32 channels
Definition: miniaudio.h:4558
ma_format format
Definition: miniaudio.h:4557
ma_uint32 sampleRate
Definition: miniaudio.h:4559
ma_uint32 order
Definition: miniaudio.h:4561
double cutoffFrequency
Definition: miniaudio.h:4560
ma_bpf_config bpf
Definition: miniaudio.h:10498
ma_node_config nodeConfig
Definition: miniaudio.h:10497
ma_bpf bpf
Definition: miniaudio.h:10507
ma_node_base baseNode
Definition: miniaudio.h:10506
ma_uint32 channels
Definition: miniaudio.h:4569
void * _pHeap
Definition: miniaudio.h:4574
ma_bool32 _ownsHeap
Definition: miniaudio.h:4575
ma_bpf2 * pBPF2
Definition: miniaudio.h:4571
ma_uint32 bpf2Count
Definition: miniaudio.h:4570
ma_format format
Definition: miniaudio.h:4568
const ma_channel * pChannelMapOut
Definition: miniaudio.h:5239
const ma_channel * pChannelMapIn
Definition: miniaudio.h:5238
ma_channel_mix_mode mixingMode
Definition: miniaudio.h:5240
ma_channel * pChannelMapOut
Definition: miniaudio.h:5254
ma_channel_conversion_path conversionPath
Definition: miniaudio.h:5252
ma_uint8 * pShuffleTable
Definition: miniaudio.h:5255
ma_channel * pChannelMapIn
Definition: miniaudio.h:5253
ma_uint32 channelsOut
Definition: miniaudio.h:5250
ma_uint32 channelsIn
Definition: miniaudio.h:5249
ma_channel_mix_mode mixingMode
Definition: miniaudio.h:5251
union ma_channel_converter::@57 weights
struct ma_context_command__wasapi::@94::@97 releaseAudioClient
struct ma_context_command__wasapi::@94::@96 createAudioClient
ma_device_type deviceType
Definition: miniaudio.h:6774
union ma_context_command__wasapi::@94 data
ma_uint32 sessionCategoryOptions
Definition: miniaudio.h:6749
ma_bool32 noAudioSessionActivate
Definition: miniaudio.h:6750
ma_thread_priority threadPriority
Definition: miniaudio.h:6732
ma_bool32 noAudioSessionDeactivate
Definition: miniaudio.h:6751
struct ma_context_config::@93 jack
const char * pClientName
Definition: miniaudio.h:6755
ma_backend_callbacks custom
Definition: miniaudio.h:6758
ma_allocation_callbacks allocationCallbacks
Definition: miniaudio.h:6735
const char * pApplicationName
Definition: miniaudio.h:6742
struct ma_context_config::@90 alsa
ma_bool32 tryAutoSpawn
Definition: miniaudio.h:6744
ma_bool32 tryStartServer
Definition: miniaudio.h:6756
const char * pServerName
Definition: miniaudio.h:6743
struct ma_context_config::@91 pulse
ma_bool32 useVerboseDeviceEnumeration
Definition: miniaudio.h:6738
struct ma_context_config::@92 coreaudio
size_t threadStackSize
Definition: miniaudio.h:6733
ma_ios_session_category sessionCategory
Definition: miniaudio.h:6748
ma_uint32 playbackDeviceInfoCount
Definition: miniaudio.h:6800
ma_proc pthread_mutex_unlock
Definition: miniaudio.h:7184
struct ma_context::@100::@103 posix
ma_proc pthread_create
Definition: miniaudio.h:7179
size_t threadStackSize
Definition: miniaudio.h:6794
ma_backend_callbacks callbacks
Definition: miniaudio.h:6789
ma_proc pthread_attr_init
Definition: miniaudio.h:7189
ma_uint32 deviceInfoCapacity
Definition: miniaudio.h:6799
struct ma_context::@98::@102 null_backend
ma_device_info * pDeviceInfos
Definition: miniaudio.h:6802
ma_proc pthread_cond_wait
Definition: miniaudio.h:7187
ma_proc pthread_join
Definition: miniaudio.h:7180
ma_handle pthreadSO
Definition: miniaudio.h:7178
ma_thread_priority threadPriority
Definition: miniaudio.h:6793
ma_mutex deviceEnumLock
Definition: miniaudio.h:6797
ma_backend backend
Definition: miniaudio.h:6790
ma_proc pthread_attr_setschedpolicy
Definition: miniaudio.h:7191
ma_allocation_callbacks allocationCallbacks
Definition: miniaudio.h:6796
ma_log log
Definition: miniaudio.h:6792
ma_proc pthread_mutex_lock
Definition: miniaudio.h:7183
ma_proc pthread_attr_setschedparam
Definition: miniaudio.h:7193
ma_proc pthread_mutex_init
Definition: miniaudio.h:7181
ma_proc pthread_cond_signal
Definition: miniaudio.h:7188
ma_proc pthread_attr_destroy
Definition: miniaudio.h:7190
ma_uint32 captureDeviceInfoCount
Definition: miniaudio.h:6801
ma_proc pthread_cond_init
Definition: miniaudio.h:7185
ma_mutex deviceInfoLock
Definition: miniaudio.h:6798
ma_proc pthread_cond_destroy
Definition: miniaudio.h:7186
ma_proc pthread_attr_getschedparam
Definition: miniaudio.h:7192
ma_log * pLog
Definition: miniaudio.h:6791
void * pUserData
Definition: miniaudio.h:6795
ma_proc pthread_mutex_destroy
Definition: miniaudio.h:7182
Data Conversion.
Definition: miniaudio.h:5282
ma_bool32 allowDynamicSampleRate
Definition: miniaudio.h:5294
ma_channel_mix_mode channelMixMode
Definition: miniaudio.h:5292
ma_resampler_config resampling
Definition: miniaudio.h:5295
ma_dither_mode ditherMode
Definition: miniaudio.h:5291
ma_channel * pChannelMapIn
Definition: miniaudio.h:5289
ma_channel * pChannelMapOut
Definition: miniaudio.h:5290
ma_bool8 hasResampler
Definition: miniaudio.h:5327
ma_uint32 sampleRateOut
Definition: miniaudio.h:5319
ma_format formatOut
Definition: miniaudio.h:5315
ma_uint32 channelsOut
Definition: miniaudio.h:5317
ma_uint32 channelsIn
Definition: miniaudio.h:5316
ma_format formatIn
Definition: miniaudio.h:5314
ma_channel_converter channelConverter
Definition: miniaudio.h:5322
ma_uint32 sampleRateIn
Definition: miniaudio.h:5318
ma_bool8 _ownsHeap
Definition: miniaudio.h:5331
ma_bool8 hasChannelConverter
Definition: miniaudio.h:5326
ma_dither_mode ditherMode
Definition: miniaudio.h:5320
ma_bool8 hasPostFormatConversion
Definition: miniaudio.h:5325
ma_resampler resampler
Definition: miniaudio.h:5323
ma_bool8 isPassthrough
Definition: miniaudio.h:5328
ma_bool8 hasPreFormatConversion
Definition: miniaudio.h:5324
ma_data_converter_execution_path executionPath
Definition: miniaudio.h:5321
ma_data_source * pNext
Definition: miniaudio.h:9290
ma_data_source_get_next_proc onGetNext
Definition: miniaudio.h:9291
ma_uint64 loopBegInFrames
Definition: miniaudio.h:9287
ma_data_source * pCurrent
Definition: miniaudio.h:9289
ma_uint64 rangeBegInFrames
Definition: miniaudio.h:9285
ma_uint64 loopEndInFrames
Definition: miniaudio.h:9288
MA_ATOMIC(4, ma_bool32) isLooping
const ma_data_source_vtable * vtable
Definition: miniaudio.h:9284
ma_uint64 rangeEndInFrames
Definition: miniaudio.h:9286
const ma_data_source_vtable * vtable
Definition: miniaudio.h:9276
ma_node_config nodeConfig
Definition: miniaudio.h:10385
ma_data_source * pDataSource
Definition: miniaudio.h:10386
ma_node_base base
Definition: miniaudio.h:10394
ma_data_source * pDataSource
Definition: miniaudio.h:10395
ma_result(* onRead)(ma_data_source *pDataSource, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead)
Definition: miniaudio.h:9263
ma_result(* onGetLength)(ma_data_source *pDataSource, ma_uint64 *pLength)
Definition: miniaudio.h:9267
ma_result(* onSetLooping)(ma_data_source *pDataSource, ma_bool32 isLooping)
Definition: miniaudio.h:9268
ma_result(* onGetCursor)(ma_data_source *pDataSource, ma_uint64 *pCursor)
Definition: miniaudio.h:9266
ma_result(* onSeek)(ma_data_source *pDataSource, ma_uint64 frameIndex)
Definition: miniaudio.h:9264
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:9265
ma_dither_mode ditherMode
Definition: miniaudio.h:9563
ma_resampler_config resampling
Definition: miniaudio.h:9564
ma_encoding_format encodingFormat
Definition: miniaudio.h:9566
ma_uint32 customBackendCount
Definition: miniaudio.h:9569
ma_channel_mix_mode channelMixMode
Definition: miniaudio.h:9562
ma_uint32 seekPointCount
Definition: miniaudio.h:9567
ma_format format
Definition: miniaudio.h:9558
ma_channel * pChannelMap
Definition: miniaudio.h:9561
ma_allocation_callbacks allocationCallbacks
Definition: miniaudio.h:9565
ma_uint32 channels
Definition: miniaudio.h:9559
ma_decoding_backend_vtable ** ppCustomBackendVTables
Definition: miniaudio.h:9568
void * pCustomBackendUserData
Definition: miniaudio.h:9570
ma_uint32 sampleRate
Definition: miniaudio.h:9560
size_t currentReadPos
Definition: miniaudio.h:9604
const ma_decoding_backend_vtable * pBackendVTable
Definition: miniaudio.h:9577
ma_uint64 inputCacheConsumed
Definition: miniaudio.h:9590
void * pInputCache
Definition: miniaudio.h:9588
ma_uint32 outputChannels
Definition: miniaudio.h:9585
ma_uint64 readPointerInPCMFrames
Definition: miniaudio.h:9583
ma_data_converter converter
Definition: miniaudio.h:9587
ma_uint64 inputCacheRemaining
Definition: miniaudio.h:9591
ma_format outputFormat
Definition: miniaudio.h:9584
ma_uint32 outputSampleRate
Definition: miniaudio.h:9586
void * pBackendUserData
Definition: miniaudio.h:9578
ma_data_source * pBackend
Definition: miniaudio.h:9576
ma_decoder_tell_proc onTell
Definition: miniaudio.h:9581
const ma_uint8 * pData
Definition: miniaudio.h:9602
ma_vfs_file file
Definition: miniaudio.h:9598
ma_allocation_callbacks allocationCallbacks
Definition: miniaudio.h:9592
struct ma_decoder::@111::@112 vfs
ma_decoder_read_proc onRead
Definition: miniaudio.h:9579
ma_uint64 inputCacheCap
Definition: miniaudio.h:9589
void * pUserData
Definition: miniaudio.h:9582
ma_decoder_seek_proc onSeek
Definition: miniaudio.h:9580
size_t dataSize
Definition: miniaudio.h:9603
ma_data_source_base ds
Definition: miniaudio.h:9575
ma_vfs * pVFS
Definition: miniaudio.h:9597
union ma_decoder::@111 data
struct ma_decoder::@111::@113 memory
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:9544
void(* onUninit)(void *pUserData, ma_data_source *pBackend, const ma_allocation_callbacks *pAllocationCallbacks)
Definition: miniaudio.h:9548
ma_vfs_callbacks cb
Definition: miniaudio.h:9495
ma_allocation_callbacks allocationCallbacks
Definition: miniaudio.h:9496
ma_uint32 sampleRate
Definition: miniaudio.h:4717
ma_uint32 channels
Definition: miniaudio.h:4716
ma_uint32 delayInFrames
Definition: miniaudio.h:4718
ma_bool32 delayStart
Definition: miniaudio.h:4719
ma_node_config nodeConfig
Definition: miniaudio.h:10609
ma_delay_config delay
Definition: miniaudio.h:10610
ma_node_base baseNode
Definition: miniaudio.h:10618
ma_delay delay
Definition: miniaudio.h:10619
ma_delay_config config
Definition: miniaudio.h:4730
ma_uint32 cursor
Definition: miniaudio.h:4731
ma_uint32 bufferSizeInFrames
Definition: miniaudio.h:4732
float * pBuffer
Definition: miniaudio.h:4733
ma_opensl_stream_type streamType
Definition: miniaudio.h:6592
ma_channel * pChannelMap
Definition: miniaudio.h:6553
ma_share_mode shareMode
Definition: miniaudio.h:6555
ma_aaudio_content_type contentType
Definition: miniaudio.h:6598
ma_device_type deviceType
Definition: miniaudio.h:6533
ma_bool32 noAutoStartAfterReroute
Definition: miniaudio.h:6600
ma_channel_mix_mode channelMixMode
Definition: miniaudio.h:6554
struct ma_device_config::@84 wasapi
ma_resampler_config resampling
Definition: miniaudio.h:6547
struct ma_device_config::@85 alsa
ma_performance_profile performanceProfile
Definition: miniaudio.h:6538
ma_device_data_proc dataCallback
Definition: miniaudio.h:6543
ma_device_notification_proc notificationCallback
Definition: miniaudio.h:6544
ma_aaudio_input_preset inputPreset
Definition: miniaudio.h:6599
const char * pStreamNameCapture
Definition: miniaudio.h:6584
ma_uint32 sampleRate
Definition: miniaudio.h:6534
const ma_device_id * pDeviceID
Definition: miniaudio.h:6550
ma_bool32 noAutoChannels
Definition: miniaudio.h:6578
ma_bool32 allowNominalSampleRateChange
Definition: miniaudio.h:6588
ma_uint32 periodSizeInFrames
Definition: miniaudio.h:6535
struct ma_device_config::@89 aaudio
ma_uint32 periodSizeInMilliseconds
Definition: miniaudio.h:6536
ma_stop_proc stopCallback
Definition: miniaudio.h:6545
ma_bool8 noDefaultQualitySRC
Definition: miniaudio.h:6570
ma_bool32 noMMap
Definition: miniaudio.h:6576
struct ma_device_config::@87 coreaudio
ma_bool8 noHardwareOffloading
Definition: miniaudio.h:6572
ma_bool8 noAutoStreamRouting
Definition: miniaudio.h:6571
ma_bool32 noAutoFormat
Definition: miniaudio.h:6577
ma_bool8 noFixedSizedCallback
Definition: miniaudio.h:6542
ma_bool8 noClip
Definition: miniaudio.h:6540
ma_aaudio_usage usage
Definition: miniaudio.h:6597
ma_uint32 periods
Definition: miniaudio.h:6537
struct ma_device_config::@86 pulse
struct ma_device_config::@83 capture
ma_bool8 noDisableDenormals
Definition: miniaudio.h:6541
ma_uint32 channels
Definition: miniaudio.h:6552
const char * pStreamNamePlayback
Definition: miniaudio.h:6583
ma_format format
Definition: miniaudio.h:6551
ma_bool32 noAutoResample
Definition: miniaudio.h:6579
ma_bool8 noAutoConvertSRC
Definition: miniaudio.h:6569
struct ma_device_config::@82 playback
ma_bool8 noPreSilencedOutputBuffer
Definition: miniaudio.h:6539
ma_opensl_recording_preset recordingPreset
Definition: miniaudio.h:6593
struct ma_device_config::@88 opensl
ma_channel channelMap[MA_MAX_CHANNELS]
Definition: miniaudio.h:6638
ma_uint32 periodSizeInMilliseconds
Definition: miniaudio.h:6640
ma_uint32 periodCount
Definition: miniaudio.h:6641
ma_share_mode shareMode
Definition: miniaudio.h:6634
ma_uint32 sampleRate
Definition: miniaudio.h:6637
ma_uint32 periodSizeInFrames
Definition: miniaudio.h:6639
const ma_device_id * pDeviceID
Definition: miniaudio.h:6633
ma_uint32 flags
Definition: miniaudio.h:6527
ma_uint32 channels
Definition: miniaudio.h:6525
struct ma_device_info::@81 nativeDataFormats[/*ma_format_count *ma_standard_sample_rate_count *MA_MAX_CHANNELS */64]
ma_bool32 isDefault
Definition: miniaudio.h:6519
char name[MA_MAX_DEVICE_NAME_LENGTH+1]
Definition: miniaudio.h:6518
ma_device_id id
Definition: miniaudio.h:6517
ma_uint32 sampleRate
Definition: miniaudio.h:6526
ma_format format
Definition: miniaudio.h:6524
ma_uint32 nativeDataFormatCount
Definition: miniaudio.h:6521
ma_job_queue jobQueue
Definition: miniaudio.h:6211
ma_bool32 _hasThread
Definition: miniaudio.h:6212
ma_device_notification_type type
Definition: miniaudio.h:6235
ma_result operationResult
Definition: miniaudio.h:7470
ma_uint32 intermediaryBufferCap
Definition: miniaudio.h:7251
ma_bool8 noClip
Definition: miniaudio.h:7218
char name[MA_MAX_DEVICE_NAME_LENGTH+1]
Definition: miniaudio.h:7237
ma_format format
Definition: miniaudio.h:7239
ma_uint32 intermediaryBufferLen
Definition: miniaudio.h:7252
void * pBackendUserData
Definition: miniaudio.h:7227
void * pUserData
Definition: miniaudio.h:7209
ma_device_data_proc onData
Definition: miniaudio.h:7206
ma_bool8 noDisableDenormals
Definition: miniaudio.h:7219
ma_device_notification_proc onNotification
Definition: miniaudio.h:7207
ma_context * pContext
Definition: miniaudio.h:7202
ma_uint64 lastProcessedFrameCapture
Definition: miniaudio.h:7476
ma_uint32 currentPeriodFramesRemainingCapture
Definition: miniaudio.h:7474
ma_bool8 noFixedSizedCallback
Definition: miniaudio.h:7220
ma_bool8 noPreSilencedOutputBuffer
Definition: miniaudio.h:7217
void * pIntermediaryBuffer
Definition: miniaudio.h:7250
struct ma_device::@104::@109 linear
ma_duplex_rb duplexRB
Definition: miniaudio.h:7222
ma_uint32 internalSampleRate
Definition: miniaudio.h:7244
ma_result workResult
Definition: miniaudio.h:7215
ma_uint32 internalPeriods
Definition: miniaudio.h:7247
ma_device_id * pID
Definition: miniaudio.h:7235
ma_mutex startStopLock
Definition: miniaudio.h:7210
ma_event startEvent
Definition: miniaudio.h:7212
ma_event stopEvent
Definition: miniaudio.h:7213
ma_semaphore operationSemaphore
Definition: miniaudio.h:7468
MA_ATOMIC(4, ma_device_state) state
ma_uint32 internalPeriodSizeInFrames
Definition: miniaudio.h:7246
void * pInputCache
Definition: miniaudio.h:7253
ma_uint32 lpfOrder
Definition: miniaudio.h:7230
ma_data_converter converter
Definition: miniaudio.h:7249
double priorRunTime
Definition: miniaudio.h:7472
ma_device_type type
Definition: miniaudio.h:7203
ma_channel_mix_mode channelMixMode
Definition: miniaudio.h:7248
ma_resample_algorithm algorithm
Definition: miniaudio.h:7225
struct ma_device::@105 playback
ma_channel internalChannelMap[MA_MAX_CHANNELS]
Definition: miniaudio.h:7245
ma_resampling_backend_vtable * pBackendVTable
Definition: miniaudio.h:7226
ma_event wakeupEvent
Definition: miniaudio.h:7211
ma_thread thread
Definition: miniaudio.h:7214
ma_share_mode shareMode
Definition: miniaudio.h:7238
ma_event operationCompletionEvent
Definition: miniaudio.h:7467
ma_format internalFormat
Definition: miniaudio.h:7242
struct ma_device::@106 capture
ma_uint32 internalChannels
Definition: miniaudio.h:7243
struct ma_device::@104 resampling
ma_uint32 channels
Definition: miniaudio.h:7240
ma_uint32 operation
Definition: miniaudio.h:7469
ma_bool8 isOwnerOfContext
Definition: miniaudio.h:7216
ma_event operationEvent
Definition: miniaudio.h:7466
ma_uint32 sampleRate
Definition: miniaudio.h:7204
ma_thread deviceThread
Definition: miniaudio.h:7465
struct ma_device::@107::@110 null_device
ma_uint64 inputCacheRemaining
Definition: miniaudio.h:7256
ma_uint32 currentPeriodFramesRemainingPlayback
Definition: miniaudio.h:7473
ma_device_id id
Definition: miniaudio.h:7236
ma_timer timer
Definition: miniaudio.h:7471
ma_uint64 inputCacheConsumed
Definition: miniaudio.h:7255
ma_uint64 inputCacheCap
Definition: miniaudio.h:7254
ma_stop_proc onStop
Definition: miniaudio.h:7208
ma_uint64 lastProcessedFramePlayback
Definition: miniaudio.h:7475
ma_channel channelMap[MA_MAX_CHANNELS]
Definition: miniaudio.h:7241
MA_ATOMIC(4, float) masterVolumeFactor
ma_pcm_rb rb
Definition: miniaudio.h:5567
ma_format format
Definition: miniaudio.h:9706
ma_uint32 channels
Definition: miniaudio.h:9707
ma_allocation_callbacks allocationCallbacks
Definition: miniaudio.h:9709
ma_encoding_format encodingFormat
Definition: miniaudio.h:9705
ma_uint32 sampleRate
Definition: miniaudio.h:9708
ma_encoder_write_proc onWrite
Definition: miniaudio.h:9717
ma_encoder_seek_proc onSeek
Definition: miniaudio.h:9718
ma_vfs * pVFS
Definition: miniaudio.h:9728
union ma_encoder::@114 data
void * pInternalEncoder
Definition: miniaudio.h:9723
struct ma_encoder::@114::@115 vfs
ma_encoder_write_pcm_frames_proc onWritePCMFrames
Definition: miniaudio.h:9721
ma_vfs_file file
Definition: miniaudio.h:9729
ma_encoder_uninit_proc onUninit
Definition: miniaudio.h:9720
ma_encoder_init_proc onInit
Definition: miniaudio.h:9719
ma_encoder_config config
Definition: miniaudio.h:9716
void * pUserData
Definition: miniaudio.h:9722
ma_uint32 gainSmoothTimeInMilliseconds
Definition: miniaudio.h:10783
ma_vfs * pResourceManagerVFS
Definition: miniaudio.h:10788
ma_uint32 listenerCount
Definition: miniaudio.h:10777
ma_allocation_callbacks allocationCallbacks
Definition: miniaudio.h:10784
ma_context * pContext
Definition: miniaudio.h:10772
ma_device_id * pPlaybackDeviceID
Definition: miniaudio.h:10774
ma_uint32 periodSizeInMilliseconds
Definition: miniaudio.h:10781
ma_bool32 noDevice
Definition: miniaudio.h:10786
ma_device * pDevice
Definition: miniaudio.h:10773
ma_uint32 channels
Definition: miniaudio.h:10778
ma_uint32 sampleRate
Definition: miniaudio.h:10779
ma_resource_manager * pResourceManager
Definition: miniaudio.h:10769
ma_uint32 periodSizeInFrames
Definition: miniaudio.h:10780
ma_mono_expansion_mode monoExpansionMode
Definition: miniaudio.h:10787
ma_bool32 noAutoStart
Definition: miniaudio.h:10785
ma_uint32 gainSmoothTimeInFrames
Definition: miniaudio.h:10782
ma_engine_node_type type
Definition: miniaudio.h:10670
ma_uint8 pinnedListenerIndex
Definition: miniaudio.h:10676
ma_bool8 isSpatializationDisabled
Definition: miniaudio.h:10675
MA_ATOMIC(4, ma_bool32) isSpatializationDisabled
ma_fader fader
Definition: miniaudio.h:10688
ma_spatializer spatializer
Definition: miniaudio.h:10690
MA_ATOMIC(4, ma_bool32) isPitchDisabled
MA_ATOMIC(4, float) pitch
ma_bool8 _ownsHeap
Definition: miniaudio.h:10700
ma_engine * pEngine
Definition: miniaudio.h:10686
ma_linear_resampler resampler
Definition: miniaudio.h:10689
float oldDopplerPitch
Definition: miniaudio.h:10694
MA_ATOMIC(4, ma_uint32) pinnedListenerIndex
ma_uint32 sampleRate
Definition: miniaudio.h:10687
ma_panner panner
Definition: miniaudio.h:10691
ma_node_base baseNode
Definition: miniaudio.h:10685
ma_uint32 gainSmoothTimeInFrames
Definition: miniaudio.h:10813
ma_uint32 listenerCount
Definition: miniaudio.h:10805
ma_bool8 ownsResourceManager
Definition: miniaudio.h:10808
ma_log * pLog
Definition: miniaudio.h:10803
ma_spatializer_listener listeners[MA_ENGINE_MAX_LISTENERS]
Definition: miniaudio.h:10806
ma_spinlock inlinedSoundLock
Definition: miniaudio.h:10810
ma_resource_manager * pResourceManager
Definition: miniaudio.h:10798
ma_bool8 ownsDevice
Definition: miniaudio.h:10809
ma_mono_expansion_mode monoExpansionMode
Definition: miniaudio.h:10814
ma_device * pDevice
Definition: miniaudio.h:10801
ma_allocation_callbacks allocationCallbacks
Definition: miniaudio.h:10807
ma_node_graph nodeGraph
Definition: miniaudio.h:10796
ma_uint32 sampleRate
Definition: miniaudio.h:10804
MA_ATOMIC(4, ma_uint32) inlinedSoundCount
ma_sound_inlined * pInlinedSoundHead
Definition: miniaudio.h:10811
ma_pthread_mutex_t lock
Definition: miniaudio.h:4187
ma_uint32 value
Definition: miniaudio.h:4186
ma_pthread_cond_t cond
Definition: miniaudio.h:4188
ma_format format
Definition: miniaudio.h:4817
ma_uint32 channels
Definition: miniaudio.h:4818
ma_uint32 sampleRate
Definition: miniaudio.h:4819
ma_uint64 cursorInFrames
Definition: miniaudio.h:4830
float volumeEnd
Definition: miniaudio.h:4828
ma_uint64 lengthInFrames
Definition: miniaudio.h:4829
float volumeBeg
Definition: miniaudio.h:4827
ma_fader_config config
Definition: miniaudio.h:4826
ma_uint32 counter
Definition: miniaudio.h:5730
ma_event e
Definition: miniaudio.h:5728
ma_uint64 sizeInBytes
Definition: miniaudio.h:9468
ma_uint32 channels
Definition: miniaudio.h:4750
ma_uint32 smoothTimeInFrames
Definition: miniaudio.h:4751
ma_gainer_config config
Definition: miniaudio.h:4759
ma_uint32 t
Definition: miniaudio.h:4760
float * pNewGains
Definition: miniaudio.h:4762
ma_bool32 _ownsHeap
Definition: miniaudio.h:4766
float * pOldGains
Definition: miniaudio.h:4761
void * _pHeap
Definition: miniaudio.h:4765
High Shelf Filter.
Definition: miniaudio.h:4685
ma_uint32 sampleRate
Definition: miniaudio.h:4688
ma_uint32 channels
Definition: miniaudio.h:4687
ma_format format
Definition: miniaudio.h:4686
ma_biquad bq
Definition: miniaudio.h:4698
ma_node_config nodeConfig
Definition: miniaudio.h:10589
ma_hishelf_config hishelf
Definition: miniaudio.h:10590
ma_node_base baseNode
Definition: miniaudio.h:10598
ma_hishelf2 hishelf
Definition: miniaudio.h:10599
High-Pass Filtering.
Definition: miniaudio.h:4445
ma_uint32 channels
Definition: miniaudio.h:4447
ma_format format
Definition: miniaudio.h:4446
double cutoffFrequency
Definition: miniaudio.h:4449
ma_uint32 sampleRate
Definition: miniaudio.h:4448
void * _pHeap
Definition: miniaudio.h:4464
ma_uint32 channels
Definition: miniaudio.h:4459
ma_bool32 _ownsHeap
Definition: miniaudio.h:4465
ma_biquad_coefficient * pR1
Definition: miniaudio.h:4461
ma_format format
Definition: miniaudio.h:4458
ma_biquad_coefficient a
Definition: miniaudio.h:4460
ma_biquad bq
Definition: miniaudio.h:4478
ma_uint32 order
Definition: miniaudio.h:4496
ma_format format
Definition: miniaudio.h:4492
double cutoffFrequency
Definition: miniaudio.h:4495
ma_uint32 channels
Definition: miniaudio.h:4493
ma_uint32 sampleRate
Definition: miniaudio.h:4494
ma_hpf_config hpf
Definition: miniaudio.h:10475
ma_node_config nodeConfig
Definition: miniaudio.h:10474
ma_hpf hpf
Definition: miniaudio.h:10484
ma_node_base baseNode
Definition: miniaudio.h:10483
ma_format format
Definition: miniaudio.h:4503
ma_uint32 sampleRate
Definition: miniaudio.h:4505
ma_uint32 hpf2Count
Definition: miniaudio.h:4507
ma_bool32 _ownsHeap
Definition: miniaudio.h:4513
ma_hpf2 * pHPF2
Definition: miniaudio.h:4509
ma_uint32 hpf1Count
Definition: miniaudio.h:4506
void * _pHeap
Definition: miniaudio.h:4512
ma_uint32 channels
Definition: miniaudio.h:4504
ma_hpf1 * pHPF1
Definition: miniaudio.h:4508
ma_uint32 capacity
Definition: miniaudio.h:6009
ma_uint32 flags
Definition: miniaudio.h:6017
MA_ATOMIC(8, ma_uint64) head
void * _pHeap
Definition: miniaudio.h:6031
MA_ATOMIC(8, ma_uint64) tail
ma_semaphore sem
Definition: miniaudio.h:6022
ma_job * pJobs
Definition: miniaudio.h:6025
ma_spinlock lock
Definition: miniaudio.h:6027
ma_bool32 _ownsHeap
Definition: miniaudio.h:6032
ma_uint32 capacity
Definition: miniaudio.h:6018
ma_slot_allocator allocator
Definition: miniaudio.h:6024
ma_uint16 code
Definition: miniaudio.h:5884
char * pFilePath
Definition: miniaudio.h:5910
struct ma_job::@59::@62::@65 freeDataBufferNode
ma_bool32 decode
Definition: miniaudio.h:5912
union ma_job::@59::@62 resourceManager
union ma_job::@59 data
ma_async_notification * pDoneNotification
Definition: miniaudio.h:5914
struct ma_job::@59::@62::@68 freeDataBuffer
ma_uint32 refcount
Definition: miniaudio.h:5886
ma_async_notification * pInitNotification
Definition: miniaudio.h:5913
wchar_t * pFilePathW
Definition: miniaudio.h:5911
struct ma_job::@59::@62::@69 loadDataStream
union ma_job::@58 toc
struct ma_job::@59::@62::@70 freeDataStream
struct ma_job::@59::@62::@71 pageDataStream
ma_fence * pDoneFence
Definition: miniaudio.h:5916
ma_uint32 pageIndex
Definition: miniaudio.h:5967
struct ma_job::@59::@62::@66 pageDataBufferNode
void * pDataBufferNode
Definition: miniaudio.h:5909
ma_uint64 frameIndex
Definition: miniaudio.h:5972
ma_uintptr data1
Definition: miniaudio.h:5900
union ma_job::@59::@63::@73 aaudio
union ma_job::@59::@63 device
struct ma_job::@59::@62::@72 seekDataStream
ma_uint32 order
Definition: miniaudio.h:5891
void * pDataBuffer
Definition: miniaudio.h:5936
ma_fence * pInitFence
Definition: miniaudio.h:5915
void * pDevice
Definition: miniaudio.h:5983
ma_job_proc proc
Definition: miniaudio.h:5898
void * pDecoder
Definition: miniaudio.h:5929
MA_ATOMIC(8, ma_uint64) next
struct ma_job::@59::@62::@64 loadDataBufferNode
ma_uint64 initialSeekPoint
Definition: miniaudio.h:5954
ma_uint64 allocation
Definition: miniaudio.h:5888
struct ma_job::@58::@60 breakup
ma_uint32 deviceType
Definition: miniaudio.h:5984
ma_uint16 slot
Definition: miniaudio.h:5885
struct ma_job::@59::@63::@73::@74 reroute
ma_uintptr data0
Definition: miniaudio.h:5899
void * pDataStream
Definition: miniaudio.h:5951
struct ma_job::@59::@61 custom
void * pResourceManager
Definition: miniaudio.h:5908
struct ma_job::@59::@62::@67 loadDataBuffer
ma_int32 state
Definition: miniaudio.h:4145
union ma_linear_resampler::@53 x0
ma_linear_resampler_config config
Definition: miniaudio.h:5041
union ma_linear_resampler::@54 x1
ma_bool32 _ownsHeap
Definition: miniaudio.h:5060
ma_uint32 inAdvanceInt
Definition: miniaudio.h:5042
ma_uint32 inTimeInt
Definition: miniaudio.h:5044
ma_uint32 inAdvanceFrac
Definition: miniaudio.h:5043
ma_uint32 inTimeFrac
Definition: miniaudio.h:5045
ma_log_callback_proc onLog
Definition: miniaudio.h:4276
void * pUserData
Definition: miniaudio.h:4277
ma_log_callback callbacks[MA_MAX_LOG_CALLBACKS]
Definition: miniaudio.h:4285
ma_allocation_callbacks allocationCallbacks
Definition: miniaudio.h:4287
ma_uint32 callbackCount
Definition: miniaudio.h:4286
ma_mutex lock
Definition: miniaudio.h:4289
Low Shelf Filter.
Definition: miniaudio.h:4654
ma_uint32 sampleRate
Definition: miniaudio.h:4657
ma_format format
Definition: miniaudio.h:4655
ma_uint32 channels
Definition: miniaudio.h:4656
ma_biquad bq
Definition: miniaudio.h:4667
ma_node_config nodeConfig
Definition: miniaudio.h:10566
ma_loshelf_config loshelf
Definition: miniaudio.h:10567
ma_loshelf2 loshelf
Definition: miniaudio.h:10576
ma_node_base baseNode
Definition: miniaudio.h:10575
Low-Pass Filtering.
Definition: miniaudio.h:4359
ma_format format
Definition: miniaudio.h:4360
ma_uint32 channels
Definition: miniaudio.h:4361
double cutoffFrequency
Definition: miniaudio.h:4363
ma_uint32 sampleRate
Definition: miniaudio.h:4362
ma_bool32 _ownsHeap
Definition: miniaudio.h:4379
ma_biquad_coefficient * pR1
Definition: miniaudio.h:4375
ma_format format
Definition: miniaudio.h:4372
ma_biquad_coefficient a
Definition: miniaudio.h:4374
void * _pHeap
Definition: miniaudio.h:4378
ma_uint32 channels
Definition: miniaudio.h:4373
ma_biquad bq
Definition: miniaudio.h:4392
ma_uint32 channels
Definition: miniaudio.h:4407
double cutoffFrequency
Definition: miniaudio.h:4409
ma_uint32 order
Definition: miniaudio.h:4410
ma_format format
Definition: miniaudio.h:4406
ma_uint32 sampleRate
Definition: miniaudio.h:4408
ma_lpf_config lpf
Definition: miniaudio.h:10452
ma_node_config nodeConfig
Definition: miniaudio.h:10451
ma_lpf lpf
Definition: miniaudio.h:10461
ma_node_base baseNode
Definition: miniaudio.h:10460
ma_format format
Definition: miniaudio.h:4417
ma_lpf1 * pLPF1
Definition: miniaudio.h:4422
ma_uint32 channels
Definition: miniaudio.h:4418
ma_bool32 _ownsHeap
Definition: miniaudio.h:4427
ma_lpf2 * pLPF2
Definition: miniaudio.h:4423
void * _pHeap
Definition: miniaudio.h:4426
ma_uint32 lpf2Count
Definition: miniaudio.h:4421
ma_uint32 lpf1Count
Definition: miniaudio.h:4420
ma_uint32 sampleRate
Definition: miniaudio.h:4419
ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]
Definition: miniaudio.h:10322
MA_ATOMIC(8, ma_uint64) localTime
MA_ATOMIC(8, ma_uint64) stateTimes[2]
ma_bool32 _ownsHeap
Definition: miniaudio.h:10325
ma_node_graph * pNodeGraph
Definition: miniaudio.h:10302
ma_uint16 cachedFrameCountIn
Definition: miniaudio.h:10309
ma_uint16 consumedFrameCountIn
Definition: miniaudio.h:10310
void * _pHeap
Definition: miniaudio.h:10324
const ma_node_vtable * vtable
Definition: miniaudio.h:10303
ma_uint32 outputBusCount
Definition: miniaudio.h:10317
ma_uint32 inputBusCount
Definition: miniaudio.h:10316
ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]
Definition: miniaudio.h:10323
ma_uint16 cachedDataCapInFramesPerBus
Definition: miniaudio.h:10305
ma_uint16 cachedFrameCountOut
Definition: miniaudio.h:10308
float * pCachedData
Definition: miniaudio.h:10304
ma_node_input_bus * pInputBuses
Definition: miniaudio.h:10318
MA_ATOMIC(4, ma_node_state) state
ma_node_output_bus * pOutputBuses
Definition: miniaudio.h:10319
ma_uint32 outputBusCount
Definition: miniaudio.h:10249
ma_uint32 inputBusCount
Definition: miniaudio.h:10248
const ma_node_vtable * vtable
Definition: miniaudio.h:10246
const ma_uint32 * pInputChannels
Definition: miniaudio.h:10250
const ma_uint32 * pOutputChannels
Definition: miniaudio.h:10251
ma_node_state initialState
Definition: miniaudio.h:10247
ma_uint16 nodeCacheCapInFrames
Definition: miniaudio.h:10355
ma_node_base base
Definition: miniaudio.h:10364
MA_ATOMIC(4, ma_bool32) isReading
ma_node_base endpoint
Definition: miniaudio.h:10365
ma_uint16 nodeCacheCapInFrames
Definition: miniaudio.h:10366
ma_node_output_bus head
Definition: miniaudio.h:10289
MA_ATOMIC(4, ma_spinlock) lock
MA_ATOMIC(4, ma_uint32) nextCounter
MA_ATOMIC(4, ma_uint32) refCount
MA_ATOMIC(4, ma_bool32) isAttached
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:10266
MA_ATOMIC(4, ma_spinlock) lock
MA_ATOMIC(1, ma_uint8) inputNodeInputBusIndex
MA_ATOMIC(MA_SIZEOF_PTR, ma_node *) pInputNode
MA_ATOMIC(4, float) volume
ma_uint8 outputBusCount
Definition: miniaudio.h:10235
ma_uint32 flags
Definition: miniaudio.h:10241
void(* onProcess)(ma_node *pNode, const float **ppFramesIn, ma_uint32 *pFrameCountIn, float **ppFramesOut, ma_uint32 *pFrameCountOut)
Definition: miniaudio.h:10214
ma_result(* onGetRequiredInputFrameCount)(ma_node *pNode, ma_uint32 outputFrameCount, ma_uint32 *pInputFrameCount)
Definition: miniaudio.h:10223
ma_uint8 inputBusCount
Definition: miniaudio.h:10229
double amplitude
Definition: miniaudio.h:9802
ma_noise_type type
Definition: miniaudio.h:9800
ma_format format
Definition: miniaudio.h:9798
ma_bool32 duplicateChannels
Definition: miniaudio.h:9803
ma_int32 seed
Definition: miniaudio.h:9801
ma_uint32 channels
Definition: miniaudio.h:9799
void * _pHeap
Definition: miniaudio.h:9828
ma_uint32 * counter
Definition: miniaudio.h:9819
ma_bool32 _ownsHeap
Definition: miniaudio.h:9829
double * accumulation
Definition: miniaudio.h:9818
ma_data_source_vtable ds
Definition: miniaudio.h:9810
ma_noise_config config
Definition: miniaudio.h:9811
struct ma_noise::@116::@118 brownian
ma_lcg lcg
Definition: miniaudio.h:9812
double ** bin
Definition: miniaudio.h:9817
struct ma_noise::@116::@117 pink
union ma_noise::@116 state
Notching Filter.
Definition: miniaudio.h:4593
ma_uint32 sampleRate
Definition: miniaudio.h:4596
ma_uint32 channels
Definition: miniaudio.h:4595
ma_format format
Definition: miniaudio.h:4594
ma_biquad bq
Definition: miniaudio.h:4605
ma_notch_config notch
Definition: miniaudio.h:10521
ma_node_config nodeConfig
Definition: miniaudio.h:10520
ma_node_base baseNode
Definition: miniaudio.h:10529
ma_notch2 notch
Definition: miniaudio.h:10530
ma_paged_audio_buffer_data * pData
Definition: miniaudio.h:9417
MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page *) pTail
ma_paged_audio_buffer_page head
Definition: miniaudio.h:9400
MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page *) pNext
ma_uint64 relativeCursor
Definition: miniaudio.h:9428
ma_data_source_base ds
Definition: miniaudio.h:9425
ma_paged_audio_buffer_data * pData
Definition: miniaudio.h:9426
ma_paged_audio_buffer_page * pCurrent
Definition: miniaudio.h:9427
ma_uint64 absoluteCursor
Definition: miniaudio.h:9429
ma_format format
Definition: miniaudio.h:4788
ma_uint32 channels
Definition: miniaudio.h:4789
ma_pan_mode mode
Definition: miniaudio.h:4790
ma_pan_mode mode
Definition: miniaudio.h:4801
ma_uint32 channels
Definition: miniaudio.h:4800
float pan
Definition: miniaudio.h:4802
ma_format format
Definition: miniaudio.h:4799
ma_uint32 channels
Definition: miniaudio.h:5533
ma_rb rb
Definition: miniaudio.h:5531
ma_format format
Definition: miniaudio.h:5532
Peaking EQ Filter.
Definition: miniaudio.h:4623
double frequency
Definition: miniaudio.h:4629
ma_uint32 sampleRate
Definition: miniaudio.h:4626
ma_format format
Definition: miniaudio.h:4624
ma_uint32 channels
Definition: miniaudio.h:4625
ma_biquad bq
Definition: miniaudio.h:4636
ma_node_config nodeConfig
Definition: miniaudio.h:10543
ma_peak_config peak
Definition: miniaudio.h:10544
ma_peak2 peak
Definition: miniaudio.h:10553
ma_node_base baseNode
Definition: miniaudio.h:10552
Ring Buffer.
Definition: miniaudio.h:5498
void * pBuffer
Definition: miniaudio.h:5499
ma_allocation_callbacks allocationCallbacks
Definition: miniaudio.h:5507
ma_bool8 clearOnWriteAcquire
Definition: miniaudio.h:5506
MA_ATOMIC(4, ma_uint32) encodedReadOffset
ma_uint32 subbufferCount
Definition: miniaudio.h:5501
ma_uint32 subbufferSizeInBytes
Definition: miniaudio.h:5500
MA_ATOMIC(4, ma_uint32) encodedWriteOffset
ma_uint32 subbufferStrideInBytes
Definition: miniaudio.h:5502
ma_bool8 ownsBuffer
Definition: miniaudio.h:5505
ma_resample_algorithm algorithm
Definition: miniaudio.h:5104
ma_uint32 channels
Definition: miniaudio.h:5101
ma_resampling_backend_vtable * pBackendVTable
Definition: miniaudio.h:5105
ma_uint32 sampleRateIn
Definition: miniaudio.h:5102
ma_uint32 sampleRateOut
Definition: miniaudio.h:5103
ma_uint32 lpfOrder
Definition: miniaudio.h:5109
struct ma_resampler_config::@55 linear
void * _pHeap
Definition: miniaudio.h:5130
ma_resampling_backend_vtable * pBackendVTable
Definition: miniaudio.h:5118
ma_bool32 _ownsHeap
Definition: miniaudio.h:5131
ma_resampling_backend * pBackend
Definition: miniaudio.h:5117
void * pBackendUserData
Definition: miniaudio.h:5119
ma_uint32 channels
Definition: miniaudio.h:5121
ma_uint32 sampleRateIn
Definition: miniaudio.h:5122
ma_uint32 sampleRateOut
Definition: miniaudio.h:5123
union ma_resampler::@56 state
ma_linear_resampler linear
Definition: miniaudio.h:5126
ma_format format
Definition: miniaudio.h:5120
ma_uint64(* onGetOutputLatency)(void *pUserData, const ma_resampling_backend *pBackend)
Definition: miniaudio.h:5087
ma_result(* onGetRequiredInputFrameCount)(void *pUserData, const ma_resampling_backend *pBackend, ma_uint64 outputFrameCount, ma_uint64 *pInputFrameCount)
Definition: miniaudio.h:5088
ma_uint64(* onGetInputLatency)(void *pUserData, const ma_resampling_backend *pBackend)
Definition: miniaudio.h:5086
ma_result(* onProcess)(void *pUserData, ma_resampling_backend *pBackend, const void *pFramesIn, ma_uint64 *pFrameCountIn, void *pFramesOut, ma_uint64 *pFrameCountOut)
Definition: miniaudio.h:5084
ma_result(* onGetExpectedOutputFrameCount)(void *pUserData, const ma_resampling_backend *pBackend, ma_uint64 inputFrameCount, ma_uint64 *pOutputFrameCount)
Definition: miniaudio.h:5089
ma_result(* onSetRate)(void *pUserData, ma_resampling_backend *pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
Definition: miniaudio.h:5085
void(* onUninit)(void *pUserData, ma_resampling_backend *pBackend, const ma_allocation_callbacks *pAllocationCallbacks)
Definition: miniaudio.h:5083
ma_result(* onInit)(void *pUserData, const ma_resampler_config *pConfig, void *pHeap, ma_resampling_backend **ppBackend)
Definition: miniaudio.h:5082
ma_result(* onGetHeapSize)(void *pUserData, const ma_resampler_config *pConfig, size_t *pHeapSizeInBytes)
Definition: miniaudio.h:5081
ma_allocation_callbacks allocationCallbacks
Definition: miniaudio.h:10056
ma_decoding_backend_vtable ** ppCustomDecodingBackendVTables
Definition: miniaudio.h:10065
ma_uint32 customDecodingBackendCount
Definition: miniaudio.h:10066
ma_resource_manager_data_buffer_node * pChildHi
Definition: miniaudio.h:9989
ma_resource_manager_data_buffer_node * pChildLo
Definition: miniaudio.h:9988
MA_ATOMIC(4, ma_uint32) executionPointer
MA_ATOMIC(4, ma_uint32) executionCounter
ma_resource_manager_data_supply data
Definition: miniaudio.h:9986
ma_resource_manager_data_buffer_node * pParent
Definition: miniaudio.h:9987
ma_resource_manager_data_buffer_node * pNode
Definition: miniaudio.h:9996
MA_ATOMIC(4, ma_uint32) executionCounter
MA_ATOMIC(4, ma_result) result
union ma_resource_manager_data_buffer::@123 connector
ma_resource_manager * pResourceManager
Definition: miniaudio.h:9995
MA_ATOMIC(4, ma_bool32) isLooping
ma_paged_audio_buffer pagedBuffer
Definition: miniaudio.h:10009
ma_data_source_base ds
Definition: miniaudio.h:9994
MA_ATOMIC(4, ma_uint32) executionPointer
const ma_resource_manager_pipeline_notifications * pNotifications
Definition: miniaudio.h:9929
union ma_resource_manager_data_source::@124 backend
ma_resource_manager_data_stream stream
Definition: miniaudio.h:10046
MA_ATOMIC(4, ma_uint32) executionCounter
ma_resource_manager_data_buffer buffer
Definition: miniaudio.h:10045
MA_ATOMIC(4, ma_uint32) executionPointer
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:10016
MA_ATOMIC(4, ma_bool32) seekCounter
MA_ATOMIC(8, ma_uint64) absoluteCursor
MA_ATOMIC(4, ma_result) result
MA_ATOMIC(4, ma_bool32) isDecoderAtEnd
MA_ATOMIC(4, ma_bool32) isLooping
MA_ATOMIC(4, ma_uint32) executionCounter
struct ma_resource_manager_data_supply::@119::@122 decodedPaged
ma_paged_audio_buffer_data data
Definition: miniaudio.h:9971
struct ma_resource_manager_data_supply::@119::@121 decoded
struct ma_resource_manager_data_supply::@119::@120 encoded
union ma_resource_manager_data_supply::@119 backend
MA_ATOMIC(4, ma_resource_manager_data_supply_type) type
ma_resource_manager_pipeline_stage_notification done
Definition: miniaudio.h:9883
ma_resource_manager_pipeline_stage_notification init
Definition: miniaudio.h:9882
ma_resource_manager_config config
Definition: miniaudio.h:10074
ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]
Definition: miniaudio.h:10078
ma_mutex dataBufferBSTLock
Definition: miniaudio.h:10077
ma_job_queue jobQueue
Definition: miniaudio.h:10080
ma_default_vfs defaultVFS
Definition: miniaudio.h:10081
ma_resource_manager_data_buffer_node * pRootDataBufferNode
Definition: miniaudio.h:10075
ma_pthread_mutex_t lock
Definition: miniaudio.h:4199
ma_pthread_cond_t cond
Definition: miniaudio.h:4200
MA_ATOMIC(4, ma_uint32) bitfield
ma_slot_allocator_group * pGroups
Definition: miniaudio.h:5827
ma_uint32 * pSlots
Definition: miniaudio.h:5828
ma_uint32 count
Definition: miniaudio.h:5829
ma_bool32 _ownsHeap
Definition: miniaudio.h:5833
ma_uint32 capacity
Definition: miniaudio.h:5830
ma_uint32 flags
Definition: miniaudio.h:10721
ma_data_source * pDataSource
Definition: miniaudio.h:10716
ma_uint64 rangeBegInPCMFrames
Definition: miniaudio.h:10723
ma_uint64 initialSeekPointInPCMFrames
Definition: miniaudio.h:10722
ma_uint64 rangeEndInPCMFrames
Definition: miniaudio.h:10724
ma_uint32 channelsIn
Definition: miniaudio.h:10719
ma_node * pInitialAttachment
Definition: miniaudio.h:10717
ma_fence * pDoneFence
Definition: miniaudio.h:10728
ma_uint64 loopPointBegInPCMFrames
Definition: miniaudio.h:10725
ma_uint64 loopPointEndInPCMFrames
Definition: miniaudio.h:10726
ma_uint32 initialAttachmentInputBusIndex
Definition: miniaudio.h:10718
ma_bool32 isLooping
Definition: miniaudio.h:10727
ma_uint32 channelsOut
Definition: miniaudio.h:10720
const wchar_t * pFilePathW
Definition: miniaudio.h:10715
const char * pFilePath
Definition: miniaudio.h:10714
ma_sound_inlined * pPrev
Definition: miniaudio.h:10756
ma_sound_inlined * pNext
Definition: miniaudio.h:10755
ma_bool8 ownsDataSource
Definition: miniaudio.h:10739
ma_engine_node engineNode
Definition: miniaudio.h:10735
ma_data_source * pDataSource
Definition: miniaudio.h:10736
ma_uint64 seekTarget
Definition: miniaudio.h:10737
ma_resource_manager_data_source * pResourceManagerDataSource
Definition: miniaudio.h:10746
MA_ATOMIC(4, ma_bool32) atEnd
ma_attenuation_model attenuationModel
Definition: miniaudio.h:4924
ma_uint32 gainSmoothTimeInFrames
Definition: miniaudio.h:4937
float directionalAttenuationFactor
Definition: miniaudio.h:4936
ma_handedness handedness
Definition: miniaudio.h:4926
ma_positioning positioning
Definition: miniaudio.h:4925
ma_channel * pChannelMapIn
Definition: miniaudio.h:4923
ma_spatializer_listener_config config
Definition: miniaudio.h:4887
ma_handedness handedness
Definition: miniaudio.h:4950
ma_channel * pChannelMapIn
Definition: miniaudio.h:4947
ma_uint32 channelsIn
Definition: miniaudio.h:4945
float minDistance
Definition: miniaudio.h:4953
ma_vec3f position
Definition: miniaudio.h:4962
float coneOuterAngleInRadians
Definition: miniaudio.h:4957
float maxDistance
Definition: miniaudio.h:4954
float coneInnerAngleInRadians
Definition: miniaudio.h:4956
float dopplerFactor
Definition: miniaudio.h:4959
float * pNewChannelGainsOut
Definition: miniaudio.h:4967
ma_vec3f velocity
Definition: miniaudio.h:4964
ma_uint32 gainSmoothTimeInFrames
Definition: miniaudio.h:4961
float directionalAttenuationFactor
Definition: miniaudio.h:4960
ma_vec3f direction
Definition: miniaudio.h:4963
float dopplerPitch
Definition: miniaudio.h:4965
ma_gainer gainer
Definition: miniaudio.h:4966
ma_uint32 channelsOut
Definition: miniaudio.h:4946
ma_positioning positioning
Definition: miniaudio.h:4949
ma_bool32 _ownsHeap
Definition: miniaudio.h:4971
float coneOuterGain
Definition: miniaudio.h:4958
ma_attenuation_model attenuationModel
Definition: miniaudio.h:4948
ma_node_config nodeConfig
Definition: miniaudio.h:10407
ma_node_base base
Definition: miniaudio.h:10416
float z
Definition: miniaudio.h:4846
float x
Definition: miniaudio.h:4844
float y
Definition: miniaudio.h:4845
ma_result(* onWrite)(ma_vfs *pVFS, ma_vfs_file file, const void *pSrc, size_t sizeInBytes, size_t *pBytesWritten)
Definition: miniaudio.h:9477
ma_result(* onInfo)(ma_vfs *pVFS, ma_vfs_file file, ma_file_info *pInfo)
Definition: miniaudio.h:9480
ma_result(* onOpen)(ma_vfs *pVFS, const char *pFilePath, ma_uint32 openMode, ma_vfs_file *pFile)
Definition: miniaudio.h:9473
ma_result(* onTell)(ma_vfs *pVFS, ma_vfs_file file, ma_int64 *pCursor)
Definition: miniaudio.h:9479
ma_result(* onSeek)(ma_vfs *pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
Definition: miniaudio.h:9478
ma_result(* onRead)(ma_vfs *pVFS, ma_vfs_file file, void *pDst, size_t sizeInBytes, size_t *pBytesRead)
Definition: miniaudio.h:9476
ma_result(* onOpenW)(ma_vfs *pVFS, const wchar_t *pFilePath, ma_uint32 openMode, ma_vfs_file *pFile)
Definition: miniaudio.h:9474
ma_result(* onClose)(ma_vfs *pVFS, ma_vfs_file file)
Definition: miniaudio.h:9475
ma_format format
Definition: miniaudio.h:9761
ma_uint32 channels
Definition: miniaudio.h:9762
ma_uint32 sampleRate
Definition: miniaudio.h:9763
ma_waveform_type type
Definition: miniaudio.h:9764
ma_data_source_base ds
Definition: miniaudio.h:9773
double advance
Definition: miniaudio.h:9775
double time
Definition: miniaudio.h:9776
ma_waveform_config config
Definition: miniaudio.h:9774
unsigned int sample_rate
Definition: stb_vorbis.h:131
Biquad Filtering.
Definition: miniaudio.h:4308
char coreaudio[256]
Definition: miniaudio.h:6487
char webaudio[32]
Definition: miniaudio.h:6493
ma_int32 aaudio
Definition: miniaudio.h:6491
char pulse[256]
Definition: miniaudio.h:6485
ma_uint32 opensl
Definition: miniaudio.h:6492
char oss[64]
Definition: miniaudio.h:6490
ma_uint32 winmm
Definition: miniaudio.h:6483
ma_uint8 dsound[16]
Definition: miniaudio.h:6482
wchar_t wasapi[64]
Definition: miniaudio.h:6481
char audio4[256]
Definition: miniaudio.h:6489
char alsa[256]
Definition: miniaudio.h:6484
char sndio[256]
Definition: miniaudio.h:6488
double counterD
Definition: miniaudio.h:6476
ma_int64 counter
Definition: miniaudio.h:6475