1/*
2Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
3miniaudio - v0.10.40 - 2021-07-23
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 one .c file:
16
17 ```c
18 #define MINIAUDIO_IMPLEMENTATION
19 #include "miniaudio.h"
20 ```
21
22You can do `#include "miniaudio.h"` in other parts of the program just like any other header.
23
24miniaudio uses the concept of a "device" as the abstraction for physical devices. The idea is that you choose a physical device to emit or capture audio from,
25and then move data to/from the device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a callback which you specify when
26initializing the device.
27
28When initializing the device you first need to configure it. The device configuration allows you to specify things like the format of the data delivered via
29the callback, the size of the internal buffer and the ID of the device you want to emit or capture audio from.
30
31Once you have the device configuration set up you can initialize the device. When initializing a device you need to allocate memory for the device object
32beforehand. This gives the application complete control over how the memory is allocated. In the example below we initialize a playback device on the stack,
33but you could allocate it on the heap if that suits your situation better.
34
35 ```c
36 void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
37 {
38 // In playback mode copy data to pOutput. In capture mode read data from pInput. In full-duplex mode, both
39 // pOutput and pInput will be valid and you can move data from pInput into pOutput. Never process more than
40 // frameCount frames.
41 }
42
43 int main()
44 {
45 ma_device_config config = ma_device_config_init(ma_device_type_playback);
46 config.playback.format = ma_format_f32; // Set to ma_format_unknown to use the device's native format.
47 config.playback.channels = 2; // Set to 0 to use the device's native channel count.
48 config.sampleRate = 48000; // Set to 0 to use the device's native sample rate.
49 config.dataCallback = data_callback; // This function will be called when miniaudio needs more data.
50 config.pUserData = pMyCustomData; // Can be accessed from the device object (device.pUserData).
51
52 ma_device device;
53 if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) {
54 return -1; // Failed to initialize the device.
55 }
56
57 ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually.
58
59 // Do something here. Probably your program's main loop.
60
61 ma_device_uninit(&device); // This will stop the device so no need to do that manually.
62 return 0;
63 }
64 ```
65
66In the example above, `data_callback()` is where audio data is written and read from the device. The idea is in playback mode you cause sound to be emitted
67from the speakers by writing audio data to the output buffer (`pOutput` in the example). In capture mode you read data from the input buffer (`pInput`) to
68extract sound captured by the microphone. The `frameCount` parameter tells you how many frames can be written to the output buffer and read from the input
69buffer. A "frame" is one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2 samples: one for the left, one for the right.
70The channel count is defined by the device config. The size in bytes of an individual sample is defined by the sample format which is also specified in the
71device config. Multi-channel audio data is always interleaved, which means the samples for each frame are stored next to each other in memory. For example, in
72a stereo stream the first pair of samples will be the left and right samples for the first frame, the second pair of samples will be the left and right samples
73for the second frame, etc.
74
75The configuration of the device is defined by the `ma_device_config` structure. The config object is always initialized with `ma_device_config_init()`. It's
76important to always initialize the config with this function as it initializes it with logical defaults and ensures your program doesn't break when new members
77are added to the `ma_device_config` structure. The example above uses a fairly simple and standard device configuration. The call to `ma_device_config_init()`
78takes a single parameter, which is whether or not the device is a playback, capture, duplex or loopback device (loopback devices are not supported on all
79backends). The `config.playback.format` member sets the sample format which can be one of the following (all formats are native-endian):
80
81 +---------------+----------------------------------------+---------------------------+
82 | Symbol | Description | Range |
83 +---------------+----------------------------------------+---------------------------+
84 | ma_format_f32 | 32-bit floating point | [-1, 1] |
85 | ma_format_s16 | 16-bit signed integer | [-32768, 32767] |
86 | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] |
87 | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] |
88 | ma_format_u8 | 8-bit unsigned integer | [0, 255] |
89 +---------------+----------------------------------------+---------------------------+
90
91The `config.playback.channels` member sets the number of channels to use with the device. The channel count cannot exceed MA_MAX_CHANNELS. The
92`config.sampleRate` member sets the sample rate (which must be the same for both playback and capture in full-duplex configurations). This is usually set to
9344100 or 48000, but can be set to anything. It's recommended to keep this between 8000 and 384000, however.
94
95Note that leaving the format, channel count and/or sample rate at their default values will result in the internal device's native configuration being used
96which is useful if you want to avoid the overhead of miniaudio's automatic data conversion.
97
98In addition to the sample format, channel count and sample rate, the data callback and user data pointer are also set via the config. The user data pointer is
99not passed into the callback as a parameter, but is instead set to the `pUserData` member of `ma_device` which you can access directly since all miniaudio
100structures are transparent.
101
102Initializing the device is done with `ma_device_init()`. This will return a result code telling you what went wrong, if anything. On success it will return
103`MA_SUCCESS`. After initialization is complete the device will be in a stopped state. To start it, use `ma_device_start()`. Uninitializing the device will stop
104it, which is what the example above does, but you can also stop the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again.
105Note that it's important to never stop or start the device from inside the callback. This will result in a deadlock. Instead you set a variable or signal an
106event indicating that the device needs to stop and handle it in a different thread. The following APIs must never be called inside the callback:
107
108 ```c
109 ma_device_init()
110 ma_device_init_ex()
111 ma_device_uninit()
112 ma_device_start()
113 ma_device_stop()
114 ```
115
116You must never try uninitializing and reinitializing a device inside the callback. You must also never try to stop and start it from inside the callback. There
117are a few other things you shouldn't do in the callback depending on your requirements, however this isn't so much a thread-safety thing, but rather a
118real-time processing thing which is beyond the scope of this introduction.
119
120The example above demonstrates the initialization of a playback device, but it works exactly the same for capture. All you need to do is change the device type
121from `ma_device_type_playback` to `ma_device_type_capture` when setting up the config, like so:
122
123 ```c
124 ma_device_config config = ma_device_config_init(ma_device_type_capture);
125 config.capture.format = MY_FORMAT;
126 config.capture.channels = MY_CHANNEL_COUNT;
127 ```
128
129In the data callback you just read from the input buffer (`pInput` in the example above) and leave the output buffer alone (it will be set to NULL when the
130device type is set to `ma_device_type_capture`).
131
132These are the available device types and how you should handle the buffers in the callback:
133
134 +-------------------------+--------------------------------------------------------+
135 | Device Type | Callback Behavior |
136 +-------------------------+--------------------------------------------------------+
137 | ma_device_type_playback | Write to output buffer, leave input buffer untouched. |
138 | ma_device_type_capture | Read from input buffer, leave output buffer untouched. |
139 | ma_device_type_duplex | Read from input buffer, write to output buffer. |
140 | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. |
141 +-------------------------+--------------------------------------------------------+
142
143You will notice in the example above that the sample format and channel count is specified separately for playback and capture. This is to support different
144data formats between the playback and capture devices in a full-duplex system. An example may be that you want to capture audio data as a monaural stream (one
145channel), but output sound to a stereo speaker system. Note that if you use different formats between playback and capture in a full-duplex configuration you
146will need to convert the data yourself. There are functions available to help you do this which will be explained later.
147
148The example above did not specify a physical device to connect to which means it will use the operating system's default device. If you have multiple physical
149devices connected and you want to use a specific one you will need to specify the device ID in the configuration, like so:
150
151 ```c
152 config.playback.pDeviceID = pMyPlaybackDeviceID; // Only if requesting a playback or duplex device.
153 config.capture.pDeviceID = pMyCaptureDeviceID; // Only if requesting a capture, duplex or loopback device.
154 ```
155
156To retrieve the device ID you will need to perform device enumeration, however this requires the use of a new concept called the "context". Conceptually
157speaking the context sits above the device. There is one context to many devices. The purpose of the context is to represent the backend at a more global level
158and to perform operations outside the scope of an individual device. Mainly it is used for performing run-time linking against backend libraries, initializing
159backends and enumerating devices. The example below shows how to enumerate devices.
160
161 ```c
162 ma_context context;
163 if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) {
164 // Error.
165 }
166
167 ma_device_info* pPlaybackInfos;
168 ma_uint32 playbackCount;
169 ma_device_info* pCaptureInfos;
170 ma_uint32 captureCount;
171 if (ma_context_get_devices(&context, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount) != MA_SUCCESS) {
172 // Error.
173 }
174
175 // Loop over each device info and do something with it. Here we just print the name with their index. You may want
176 // to give the user the opportunity to choose which device they'd prefer.
177 for (ma_uint32 iDevice = 0; iDevice < playbackCount; iDevice += 1) {
178 printf("%d - %s\n", iDevice, pPlaybackInfos[iDevice].name);
179 }
180
181 ma_device_config config = ma_device_config_init(ma_device_type_playback);
182 config.playback.pDeviceID = &pPlaybackInfos[chosenPlaybackDeviceIndex].id;
183 config.playback.format = MY_FORMAT;
184 config.playback.channels = MY_CHANNEL_COUNT;
185 config.sampleRate = MY_SAMPLE_RATE;
186 config.dataCallback = data_callback;
187 config.pUserData = pMyCustomData;
188
189 ma_device device;
190 if (ma_device_init(&context, &config, &device) != MA_SUCCESS) {
191 // Error
192 }
193
194 ...
195
196 ma_device_uninit(&device);
197 ma_context_uninit(&context);
198 ```
199
200The first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`. The first parameter is a pointer to a list of `ma_backend`
201values which are used to override the default backend priorities. When this is NULL, as in this example, miniaudio's default priorities are used. The second
202parameter is the number of backends listed in the array pointed to by the first parameter. The third parameter is a pointer to a `ma_context_config` object
203which can be NULL, in which case defaults are used. The context configuration is used for setting the logging callback, custom memory allocation callbacks,
204user-defined data and some backend-specific configurations.
205
206Once the context has been initialized you can enumerate devices. In the example above we use the simpler `ma_context_get_devices()`, however you can also use a
207callback for handling devices by using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer to a pointer that will,
208upon output, be set to a pointer to a buffer containing a list of `ma_device_info` structures. You also provide a pointer to an unsigned integer that will
209receive the number of items in the returned buffer. Do not free the returned buffers as their memory is managed internally by miniaudio.
210
211The `ma_device_info` structure contains an `id` member which is the ID you pass to the device config. It also contains the name of the device which is useful
212for presenting a list of devices to the user via the UI.
213
214When creating your own context you will want to pass it to `ma_device_init()` when initializing the device. Passing in NULL, like we do in the first example,
215will result in miniaudio creating the context for you, which you don't want to do since you've already created a context. Note that internally the context is
216only tracked by it's pointer which means you must not change the location of the `ma_context` object. If this is an issue, consider using `malloc()` to
217allocate memory for the context.
218
219
220
2212. Building
222===========
223miniaudio should work cleanly out of the box without the need to download or install any dependencies. See below for platform-specific details.
224
225
2262.1. Windows
227------------
228The Windows build should compile cleanly on all popular compilers without the need to configure any include paths nor link to any libraries.
229
2302.2. macOS and iOS
231------------------
232The macOS build should compile cleanly without the need to download any dependencies nor link to any libraries or frameworks. The iOS build needs to be
233compiled as Objective-C and will need to link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling through the command line
234requires linking to `-lpthread` and `-lm`.
235
236Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's notarization process. To fix this there are two options. The
237first is to use the `MA_NO_RUNTIME_LINKING` option, like so:
238
239 ```c
240 #ifdef __APPLE__
241 #define MA_NO_RUNTIME_LINKING
242 #endif
243 #define MINIAUDIO_IMPLEMENTATION
244 #include "miniaudio.h"
245 ```
246
247This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioUnit`. Alternatively, if you would rather keep using runtime
248linking you can add the following to your entitlements.xcent file:
249
250 ```
251 <key>com.apple.security.cs.allow-dyld-environment-variables</key>
252 <true/>
253 <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
254 <true/>
255 ```
256
257
2582.3. Linux
259----------
260The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any development packages.
261
2622.4. BSD
263--------
264The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses sndio and FreeBSD uses OSS.
265
2662.5. Android
267------------
268AAudio is the highest priority backend on Android. This should work out of the box without needing any kind of compiler configuration. Support for AAudio
269starts with Android 8 which means older versions will fall back to OpenSL|ES which requires API level 16+.
270
271There have been reports that the OpenSL|ES backend fails to initialize on some Android based devices due to `dlopen()` failing to open "libOpenSLES.so". If
272this happens on your platform you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES.
273
2742.6. Emscripten
275---------------
276The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. You cannot use -std=c* compiler flags, nor -ansi.
277
278
2792.7. Build Options
280------------------
281`#define` these options before including miniaudio.h.
282
283 +----------------------------------+--------------------------------------------------------------------+
284 | Option | Description |
285 +----------------------------------+--------------------------------------------------------------------+
286 | MA_NO_WASAPI | Disables the WASAPI backend. |
287 +----------------------------------+--------------------------------------------------------------------+
288 | MA_NO_DSOUND | Disables the DirectSound backend. |
289 +----------------------------------+--------------------------------------------------------------------+
290 | MA_NO_WINMM | Disables the WinMM backend. |
291 +----------------------------------+--------------------------------------------------------------------+
292 | MA_NO_ALSA | Disables the ALSA backend. |
293 +----------------------------------+--------------------------------------------------------------------+
294 | MA_NO_PULSEAUDIO | Disables the PulseAudio backend. |
295 +----------------------------------+--------------------------------------------------------------------+
296 | MA_NO_JACK | Disables the JACK backend. |
297 +----------------------------------+--------------------------------------------------------------------+
298 | MA_NO_COREAUDIO | Disables the Core Audio backend. |
299 +----------------------------------+--------------------------------------------------------------------+
300 | MA_NO_SNDIO | Disables the sndio backend. |
301 +----------------------------------+--------------------------------------------------------------------+
302 | MA_NO_AUDIO4 | Disables the audio(4) backend. |
303 +----------------------------------+--------------------------------------------------------------------+
304 | MA_NO_OSS | Disables the OSS backend. |
305 +----------------------------------+--------------------------------------------------------------------+
306 | MA_NO_AAUDIO | Disables the AAudio backend. |
307 +----------------------------------+--------------------------------------------------------------------+
308 | MA_NO_OPENSL | Disables the OpenSL|ES backend. |
309 +----------------------------------+--------------------------------------------------------------------+
310 | MA_NO_WEBAUDIO | Disables the Web Audio backend. |
311 +----------------------------------+--------------------------------------------------------------------+
312 | MA_NO_NULL | Disables the null backend. |
313 +----------------------------------+--------------------------------------------------------------------+
314 | MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to |
315 | | enable specific backends. |
316 +----------------------------------+--------------------------------------------------------------------+
317 | MA_ENABLE_WASAPI | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
318 | | enable the WASAPI backend. |
319 +----------------------------------+--------------------------------------------------------------------+
320 | MA_ENABLE_DSOUND | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
321 | | enable the DirectSound backend. |
322 +----------------------------------+--------------------------------------------------------------------+
323 | MA_ENABLE_WINMM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
324 | | enable the WinMM backend. |
325 +----------------------------------+--------------------------------------------------------------------+
326 | MA_ENABLE_ALSA | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
327 | | enable the ALSA backend. |
328 +----------------------------------+--------------------------------------------------------------------+
329 | MA_ENABLE_PULSEAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
330 | | enable the PulseAudio backend. |
331 +----------------------------------+--------------------------------------------------------------------+
332 | MA_ENABLE_JACK | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
333 | | enable the JACK backend. |
334 +----------------------------------+--------------------------------------------------------------------+
335 | MA_ENABLE_COREAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
336 | | enable the Core Audio backend. |
337 +----------------------------------+--------------------------------------------------------------------+
338 | MA_ENABLE_SNDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
339 | | enable the sndio backend. |
340 +----------------------------------+--------------------------------------------------------------------+
341 | MA_ENABLE_AUDIO4 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
342 | | enable the audio(4) backend. |
343 +----------------------------------+--------------------------------------------------------------------+
344 | MA_ENABLE_OSS | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
345 | | enable the OSS backend. |
346 +----------------------------------+--------------------------------------------------------------------+
347 | MA_ENABLE_AAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
348 | | enable the AAudio backend. |
349 +----------------------------------+--------------------------------------------------------------------+
350 | MA_ENABLE_OPENSL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
351 | | enable the OpenSL|ES backend. |
352 +----------------------------------+--------------------------------------------------------------------+
353 | MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
354 | | enable the Web Audio backend. |
355 +----------------------------------+--------------------------------------------------------------------+
356 | MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
357 | | enable the null backend. |
358 +----------------------------------+--------------------------------------------------------------------+
359 | MA_NO_DECODING | Disables decoding APIs. |
360 +----------------------------------+--------------------------------------------------------------------+
361 | MA_NO_ENCODING | Disables encoding APIs. |
362 +----------------------------------+--------------------------------------------------------------------+
363 | MA_NO_WAV | Disables the built-in WAV decoder and encoder. |
364 +----------------------------------+--------------------------------------------------------------------+
365 | MA_NO_FLAC | Disables the built-in FLAC decoder. |
366 +----------------------------------+--------------------------------------------------------------------+
367 | MA_NO_MP3 | Disables the built-in MP3 decoder. |
368 +----------------------------------+--------------------------------------------------------------------+
369 | MA_NO_DEVICE_IO | Disables playback and recording. This will disable ma_context and |
370 | | ma_device APIs. This is useful if you only want to use miniaudio's |
371 | | data conversion and/or decoding APIs. |
372 +----------------------------------+--------------------------------------------------------------------+
373 | MA_NO_THREADING | Disables the ma_thread, ma_mutex, ma_semaphore and ma_event APIs. |
374 | | This option is useful if you only need to use miniaudio for data |
375 | | conversion, decoding and/or encoding. Some families of APIs |
376 | | require threading which means the following options must also be |
377 | | set: |
378 | | |
379 | | ``` |
380 | | MA_NO_DEVICE_IO |
381 | | ``` |
382 +----------------------------------+--------------------------------------------------------------------+
383 | MA_NO_GENERATION | Disables generation APIs such a ma_waveform and ma_noise. |
384 +----------------------------------+--------------------------------------------------------------------+
385 | MA_NO_SSE2 | Disables SSE2 optimizations. |
386 +----------------------------------+--------------------------------------------------------------------+
387 | MA_NO_AVX2 | Disables AVX2 optimizations. |
388 +----------------------------------+--------------------------------------------------------------------+
389 | MA_NO_AVX512 | Disables AVX-512 optimizations. |
390 +----------------------------------+--------------------------------------------------------------------+
391 | MA_NO_NEON | Disables NEON optimizations. |
392 +----------------------------------+--------------------------------------------------------------------+
393 | MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's |
394 | | notarization process. When enabling this, you may need to avoid |
395 | | using `-std=c89` or `-std=c99` on Linux builds or else you may end |
396 | | up with compilation errors due to conflicts with `timespec` and |
397 | | `timeval` data types. |
398 | | |
399 | | You may need to enable this if your target platform does not allow |
400 | | runtime linking via `dlopen()`. |
401 +----------------------------------+--------------------------------------------------------------------+
402 | MA_DEBUG_OUTPUT | Enable processing of MA_LOG_LEVEL_DEBUG messages and `printf()` |
403 | | output. |
404 +----------------------------------+--------------------------------------------------------------------+
405 | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to |
406 | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. |
407 +----------------------------------+--------------------------------------------------------------------+
408 | MA_API | Controls how public APIs should be decorated. Default is `extern`. |
409 +----------------------------------+--------------------------------------------------------------------+
410 | MA_DLL | If set, configures MA_API to either import or export APIs |
411 | | depending on whether or not the implementation is being defined. |
412 | | If defining the implementation, MA_API will be configured to |
413 | | export. Otherwise it will be configured to import. This has no |
414 | | effect if MA_API is defined externally. |
415 +----------------------------------+--------------------------------------------------------------------+
416
417
4183. Definitions
419==============
420This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity in the use of terms throughout the audio space, so this
421section is intended to clarify how miniaudio uses each term.
422
4233.1. Sample
424-----------
425A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit floating point number.
426
4273.2. Frame / PCM Frame
428----------------------
429A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2 samples, a mono frame is 1 sample, a 5.1 surround sound frame
430is 6 samples, etc. The terms "frame" and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame. If ever miniaudio
431needs to refer to a compressed frame, such as a FLAC frame, it will always clarify what it's referring to with something like "FLAC frame".
432
4333.3. Channel
434------------
435A stream of monaural audio that is emitted from an individual speaker in a speaker system, or received from an individual microphone in a microphone system. A
436stereo stream has two channels (a left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio systems refer to a channel as
437a complex audio stream that's mixed with other channels to produce the final mix - this is completely different to miniaudio's use of the term "channel" and
438should not be confused.
439
4403.4. Sample Rate
441----------------
442The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number of PCM frames that are processed per second.
443
4443.5. Formats
445------------
446Throughout miniaudio you will see references to different sample formats:
447
448 +---------------+----------------------------------------+---------------------------+
449 | Symbol | Description | Range |
450 +---------------+----------------------------------------+---------------------------+
451 | ma_format_f32 | 32-bit floating point | [-1, 1] |
452 | ma_format_s16 | 16-bit signed integer | [-32768, 32767] |
453 | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] |
454 | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] |
455 | ma_format_u8 | 8-bit unsigned integer | [0, 255] |
456 +---------------+----------------------------------------+---------------------------+
457
458All formats are native-endian.
459
460
461
4624. Decoding
463===========
464The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from devices and can be used independently. The following formats are
465supported:
466
467 +---------+------------------+----------+
468 | Format | Decoding Backend | Built-In |
469 +---------+------------------+----------+
470 | WAV | dr_wav | Yes |
471 | MP3 | dr_mp3 | Yes |
472 | FLAC | dr_flac | Yes |
473 | Vorbis | stb_vorbis | No |
474 +---------+------------------+----------+
475
476Vorbis is supported via stb_vorbis which can be enabled by including the header section before the implementation of miniaudio, like the following:
477
478 ```c
479 #define STB_VORBIS_HEADER_ONLY
480 #include "extras/stb_vorbis.c" // Enables Vorbis decoding.
481
482 #define MINIAUDIO_IMPLEMENTATION
483 #include "miniaudio.h"
484
485 // The stb_vorbis implementation must come after the implementation of miniaudio.
486 #undef STB_VORBIS_HEADER_ONLY
487 #include "extras/stb_vorbis.c"
488 ```
489
490A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/mackron/miniaudio).
491
492Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the built-in decoders by specifying one or more of the
493following options before the miniaudio implementation:
494
495 ```c
496 #define MA_NO_WAV
497 #define MA_NO_MP3
498 #define MA_NO_FLAC
499 ```
500
501Disabling built-in decoding libraries is useful if you use these libraries independantly of the `ma_decoder` API.
502
503A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with `ma_decoder_init_memory()`, or from data delivered via callbacks
504with `ma_decoder_init()`. Here is an example for loading a decoder from a file:
505
506 ```c
507 ma_decoder decoder;
508 ma_result result = ma_decoder_init_file("MySong.mp3", NULL, &decoder);
509 if (result != MA_SUCCESS) {
510 return false; // An error occurred.
511 }
512
513 ...
514
515 ma_decoder_uninit(&decoder);
516 ```
517
518When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object (the `NULL` argument in the example above) which allows you
519to configure the output format, channel count, sample rate and channel map:
520
521 ```c
522 ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000);
523 ```
524
525When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the same as that defined by the decoding backend.
526
527Data is read from the decoder as PCM frames. This will return the number of PCM frames actually read. If the return value is less than the requested number of
528PCM frames it means you've reached the end:
529
530 ```c
531 ma_uint64 framesRead = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead);
532 if (framesRead < framesToRead) {
533 // Reached the end.
534 }
535 ```
536
537You can also seek to a specific frame like so:
538
539 ```c
540 ma_result result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame);
541 if (result != MA_SUCCESS) {
542 return false; // An error occurred.
543 }
544 ```
545
546If you want to loop back to the start, you can simply seek back to the first PCM frame:
547
548 ```c
549 ma_decoder_seek_to_pcm_frame(pDecoder, 0);
550 ```
551
552When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding backend. This can be unnecessarily inefficient if the type
553is already known. In this case you can use `encodingFormat` variable in the device config to specify a specific encoding format you want to decode:
554
555 ```c
556 decoderConfig.encodingFormat = ma_encoding_format_wav;
557 ```
558
559See the `ma_encoding_format` enum for possible encoding formats.
560
561The `ma_decoder_init_file()` API will try using the file extension to determine which decoding backend to prefer.
562
563
564
5655. Encoding
566===========
567The `ma_encoding` API is used for writing audio files. The only supported output format is WAV which is achieved via dr_wav which is amalgamated into the
568implementation section of miniaudio. This can be disabled by specifying the following option before the implementation of miniaudio:
569
570 ```c
571 #define MA_NO_WAV
572 ```
573
574An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data delivered via callbacks with `ma_encoder_init()`. Below is an
575example for initializing an encoder to output to a file.
576
577 ```c
578 ma_encoder_config config = ma_encoder_config_init(ma_resource_format_wav, FORMAT, CHANNELS, SAMPLE_RATE);
579 ma_encoder encoder;
580 ma_result result = ma_encoder_init_file("my_file.wav", &config, &encoder);
581 if (result != MA_SUCCESS) {
582 // Error
583 }
584
585 ...
586
587 ma_encoder_uninit(&encoder);
588 ```
589
590When initializing an encoder you must specify a config which is initialized with `ma_encoder_config_init()`. Here you must specify the file type, the output
591sample format, output channel count and output sample rate. The following file types are supported:
592
593 +------------------------+-------------+
594 | Enum | Description |
595 +------------------------+-------------+
596 | ma_resource_format_wav | WAV |
597 +------------------------+-------------+
598
599If the format, channel count or sample rate is not supported by the output file type an error will be returned. The encoder will not perform data conversion so
600you will need to convert it before outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the example below:
601
602 ```c
603 framesWritten = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite);
604 ```
605
606Encoders must be uninitialized with `ma_encoder_uninit()`.
607
608
6096. Data Conversion
610==================
611A data conversion API is included with miniaudio which supports the majority of data conversion requirements. This supports conversion between sample formats,
612channel counts (with channel mapping) and sample rates.
613
614
6156.1. Sample Format Conversion
616-----------------------------
617Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and `ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()`
618to convert between two specific formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use `ma_convert_pcm_frames_format()` to convert
619PCM frames where you want to specify the frame count and channel count as a variable instead of the total sample count.
620
621
6226.1.1. Dithering
623----------------
624Dithering can be set using the ditherMode parameter.
625
626The different dithering modes include the following, in order of efficiency:
627
628 +-----------+--------------------------+
629 | Type | Enum Token |
630 +-----------+--------------------------+
631 | None | ma_dither_mode_none |
632 | Rectangle | ma_dither_mode_rectangle |
633 | Triangle | ma_dither_mode_triangle |
634 +-----------+--------------------------+
635
636Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be ignored for conversions where dithering is not needed.
637Dithering is available for the following conversions:
638
639 ```
640 s16 -> u8
641 s24 -> u8
642 s32 -> u8
643 f32 -> u8
644 s24 -> s16
645 s32 -> s16
646 f32 -> s16
647 ```
648
649Note that it is not an error to pass something other than ma_dither_mode_none for conversions where dither is not used. It will just be ignored.
650
651
652
6536.2. Channel Conversion
654-----------------------
655Channel conversion is used for channel rearrangement and conversion from one channel count to another. The `ma_channel_converter` API is used for channel
656conversion. Below is an example of initializing a simple channel converter which converts from mono to stereo.
657
658 ```c
659 ma_channel_converter_config config = ma_channel_converter_config_init(
660 ma_format, // Sample format
661 1, // Input channels
662 NULL, // Input channel map
663 2, // Output channels
664 NULL, // Output channel map
665 ma_channel_mix_mode_default); // The mixing algorithm to use when combining channels.
666
667 result = ma_channel_converter_init(&config, &converter);
668 if (result != MA_SUCCESS) {
669 // Error.
670 }
671 ```
672
673To perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so:
674
675 ```c
676 ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount);
677 if (result != MA_SUCCESS) {
678 // Error.
679 }
680 ```
681
682It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM frames.
683
684Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported.
685
686
6876.2.1. Channel Mapping
688----------------------
689In addition to converting from one channel count to another, like the example above, the channel converter can also be used to rearrange channels. When
690initializing the channel converter, you can optionally pass in channel maps for both the input and output frames. If the channel counts are the same, and each
691channel map contains the same channel positions with the exception that they're in a different order, a simple shuffling of the channels will be performed. If,
692however, there is not a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed based on a mixing mode which is
693specified when initializing the `ma_channel_converter_config` object.
694
695When converting from mono to multi-channel, the mono channel is simply copied to each output channel. When going the other way around, the audio of each output
696channel is simply averaged and copied to the mono channel.
697
698In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess channels and silence extra channels. For example, converting
699from 4 to 2 channels, the 3rd and 4th channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and 4th channels.
700
701The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a simple distribution between input and output. Imagine sitting
702in the middle of a room, with speakers on the walls representing channel positions. The MA_CHANNEL_FRONT_LEFT position can be thought of as being in the corner
703of the front and left walls.
704
705Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined weights. Custom weights can be passed in as the last parameter of
706`ma_channel_converter_config_init()`.
707
708Predefined channel maps can be retrieved with `ma_get_standard_channel_map()`. This takes a `ma_standard_channel_map` enum as it's first parameter, which can
709be one of the following:
710
711 +-----------------------------------+-----------------------------------------------------------+
712 | Name | Description |
713 +-----------------------------------+-----------------------------------------------------------+
714 | ma_standard_channel_map_default | Default channel map used by miniaudio. See below. |
715 | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps. |
716 | ma_standard_channel_map_alsa | Default ALSA channel map. |
717 | ma_standard_channel_map_rfc3551 | RFC 3551. Based on AIFF. |
718 | ma_standard_channel_map_flac | FLAC channel map. |
719 | ma_standard_channel_map_vorbis | Vorbis channel map. |
720 | ma_standard_channel_map_sound4 | FreeBSD's sound(4). |
721 | ma_standard_channel_map_sndio | sndio channel map. http://www.sndio.org/tips.html. |
722 | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering |
723 +-----------------------------------+-----------------------------------------------------------+
724
725Below are the channel maps used by default in miniaudio (`ma_standard_channel_map_default`):
726
727 +---------------+---------------------------------+
728 | Channel Count | Mapping |
729 +---------------+---------------------------------+
730 | 1 (Mono) | 0: MA_CHANNEL_MONO |
731 +---------------+---------------------------------+
732 | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT <br> |
733 | | 1: MA_CHANNEL_FRONT_RIGHT |
734 +---------------+---------------------------------+
735 | 3 | 0: MA_CHANNEL_FRONT_LEFT <br> |
736 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
737 | | 2: MA_CHANNEL_FRONT_CENTER |
738 +---------------+---------------------------------+
739 | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT <br> |
740 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
741 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
742 | | 3: MA_CHANNEL_BACK_CENTER |
743 +---------------+---------------------------------+
744 | 5 | 0: MA_CHANNEL_FRONT_LEFT <br> |
745 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
746 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
747 | | 3: MA_CHANNEL_BACK_LEFT <br> |
748 | | 4: MA_CHANNEL_BACK_RIGHT |
749 +---------------+---------------------------------+
750 | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT <br> |
751 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
752 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
753 | | 3: MA_CHANNEL_LFE <br> |
754 | | 4: MA_CHANNEL_SIDE_LEFT <br> |
755 | | 5: MA_CHANNEL_SIDE_RIGHT |
756 +---------------+---------------------------------+
757 | 7 | 0: MA_CHANNEL_FRONT_LEFT <br> |
758 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
759 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
760 | | 3: MA_CHANNEL_LFE <br> |
761 | | 4: MA_CHANNEL_BACK_CENTER <br> |
762 | | 4: MA_CHANNEL_SIDE_LEFT <br> |
763 | | 5: MA_CHANNEL_SIDE_RIGHT |
764 +---------------+---------------------------------+
765 | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT <br> |
766 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
767 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
768 | | 3: MA_CHANNEL_LFE <br> |
769 | | 4: MA_CHANNEL_BACK_LEFT <br> |
770 | | 5: MA_CHANNEL_BACK_RIGHT <br> |
771 | | 6: MA_CHANNEL_SIDE_LEFT <br> |
772 | | 7: MA_CHANNEL_SIDE_RIGHT |
773 +---------------+---------------------------------+
774 | Other | All channels set to 0. This |
775 | | is equivalent to the same |
776 | | mapping as the device. |
777 +---------------+---------------------------------+
778
779
780
7816.3. Resampling
782---------------
783Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something like the following:
784
785 ```c
786 ma_resampler_config config = ma_resampler_config_init(
787 ma_format_s16,
788 channels,
789 sampleRateIn,
790 sampleRateOut,
791 ma_resample_algorithm_linear);
792
793 ma_resampler resampler;
794 ma_result result = ma_resampler_init(&config, &resampler);
795 if (result != MA_SUCCESS) {
796 // An error occurred...
797 }
798 ```
799
800Do the following to uninitialize the resampler:
801
802 ```c
803 ma_resampler_uninit(&resampler);
804 ```
805
806The following example shows how data can be processed
807
808 ```c
809 ma_uint64 frameCountIn = 1000;
810 ma_uint64 frameCountOut = 2000;
811 ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
812 if (result != MA_SUCCESS) {
813 // An error occurred...
814 }
815
816 // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the
817 // number of output frames written.
818 ```
819
820To initialize the resampler you first need to set up a config (`ma_resampler_config`) with `ma_resampler_config_init()`. You need to specify the sample format
821you want to use, the number of channels, the input and output sample rate, and the algorithm.
822
823The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format you will need to perform pre- and post-conversions yourself
824where necessary. Note that the format is the same for both input and output. The format cannot be changed after initialization.
825
826The resampler supports multiple channels and is always interleaved (both input and output). The channel count cannot be changed after initialization.
827
828The sample rates can be anything other than zero, and are always specified in hertz. They should be set to something like 44100, etc. The sample rate is the
829only configuration property that can be changed after initialization.
830
831The miniaudio resampler supports multiple algorithms:
832
833 +-----------+------------------------------+
834 | Algorithm | Enum Token |
835 +-----------+------------------------------+
836 | Linear | ma_resample_algorithm_linear |
837 | Speex | ma_resample_algorithm_speex |
838 +-----------+------------------------------+
839
840Because Speex is not public domain it is strictly opt-in and the code is stored in separate files. if you opt-in to the Speex backend you will need to consider
841it's license, the text of which can be found in it's source files in "extras/speex_resampler". Details on how to opt-in to the Speex resampler is explained in
842the Speex Resampler section below.
843
844The algorithm cannot be changed after initialization.
845
846Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process
847frames, use `ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you can fit in the output buffer and the number of
848input frames contained in the input buffer. On output these variables contain the number of output frames that were written to the output buffer and the
849number of input frames that were consumed in the process. You can pass in NULL for the input buffer in which case it will be treated as an infinitely large
850buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated as seek.
851
852The sample rate can be changed dynamically on the fly. You can change this with explicit sample rates with `ma_resampler_set_rate()` and also with a decimal
853ratio with `ma_resampler_set_rate_ratio()`. The ratio is in/out.
854
855Sometimes it's useful to know exactly how many input frames will be required to output a specific number of frames. You can calculate this with
856`ma_resampler_get_required_input_frame_count()`. Likewise, it's sometimes useful to know exactly how many frames would be output given a certain number of
857input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`.
858
859Due to the nature of how resampling works, the resampler introduces some latency. This can be retrieved in terms of both the input rate and the output rate
860with `ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`.
861
862
8636.3.1. Resampling Algorithms
864----------------------------
865The choice of resampling algorithm depends on your situation and requirements. The linear resampler is the most efficient and has the least amount of latency,
866but at the expense of poorer quality. The Speex resampler is higher quality, but slower with more latency. It also performs several heap allocations internally
867for memory management.
868
869
8706.3.1.1. Linear Resampling
871--------------------------
872The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however, some control over the quality of the linear resampler which
873may make it a suitable option depending on your requirements.
874
875The linear resampler performs low-pass filtering before or after downsampling or upsampling, depending on the sample rates you're converting between. When
876decreasing the sample rate, the low-pass filter will be applied before downsampling. When increasing the rate it will be performed after upsampling. By default
877a fourth order low-pass filter will be applied. This can be configured via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering.
878
879The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of the input and output sample rates (Nyquist Frequency). This
880can be controlled with the `lpfNyquistFactor` config variable. This defaults to 1, and should be in the range of 0..1, although a value of 0 does not make
881sense and should be avoided. A value of 1 will use the Nyquist Frequency as the cutoff. A value of 0.5 will use half the Nyquist Frequency as the cutoff, etc.
882Values less than 1 will result in more washed out sound due to more of the higher frequencies being removed. This config variable has no impact on performance
883and is a purely perceptual configuration.
884
885The API for the linear resampler is the same as the main resampler API, only it's called `ma_linear_resampler`.
886
887
8886.3.1.2. Speex Resampling
889-------------------------
890The Speex resampler is made up of third party code which is released under the BSD license. Because it is licensed differently to miniaudio, which is public
891domain, it is strictly opt-in and all of it's code is stored in separate files. If you opt-in to the Speex resampler you must consider the license text in it's
892source files. To opt-in, you must first `#include` the following file before the implementation of miniaudio.h:
893
894 ```c
895 #include "extras/speex_resampler/ma_speex_resampler.h"
896 ```
897
898Both the header and implementation is contained within the same file. The implementation can be included in your program like so:
899
900 ```c
901 #define MINIAUDIO_SPEEX_RESAMPLER_IMPLEMENTATION
902 #include "extras/speex_resampler/ma_speex_resampler.h"
903 ```
904
905Note that even if you opt-in to the Speex backend, miniaudio won't use it unless you explicitly ask for it in the respective config of the object you are
906initializing. If you try to use the Speex resampler without opting in, initialization of the `ma_resampler` object will fail with `MA_NO_BACKEND`.
907
908The only configuration option to consider with the Speex resampler is the `speex.quality` config variable. This is a value between 0 and 10, with 0 being
909the fastest with the poorest quality and 10 being the slowest with the highest quality. The default value is 3.
910
911
912
9136.4. General Data Conversion
914----------------------------
915The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and resampling into one operation. This is what miniaudio uses
916internally to convert between the format requested when the device was initialized and the format of the backend's native device. The API for general data
917conversion is very similar to the resampling API. Create a `ma_data_converter` object like this:
918
919 ```c
920 ma_data_converter_config config = ma_data_converter_config_init(
921 inputFormat,
922 outputFormat,
923 inputChannels,
924 outputChannels,
925 inputSampleRate,
926 outputSampleRate
927 );
928
929 ma_data_converter converter;
930 ma_result result = ma_data_converter_init(&config, &converter);
931 if (result != MA_SUCCESS) {
932 // An error occurred...
933 }
934 ```
935
936In the example above we use `ma_data_converter_config_init()` to initialize the config, however there's many more properties that can be configured, such as
937channel maps and resampling quality. Something like the following may be more suitable depending on your requirements:
938
939 ```c
940 ma_data_converter_config config = ma_data_converter_config_init_default();
941 config.formatIn = inputFormat;
942 config.formatOut = outputFormat;
943 config.channelsIn = inputChannels;
944 config.channelsOut = outputChannels;
945 config.sampleRateIn = inputSampleRate;
946 config.sampleRateOut = outputSampleRate;
947 ma_get_standard_channel_map(ma_standard_channel_map_flac, config.channelCountIn, config.channelMapIn);
948 config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER;
949 ```
950
951Do the following to uninitialize the data converter:
952
953 ```c
954 ma_data_converter_uninit(&converter);
955 ```
956
957The following example shows how data can be processed
958
959 ```c
960 ma_uint64 frameCountIn = 1000;
961 ma_uint64 frameCountOut = 2000;
962 ma_result result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
963 if (result != MA_SUCCESS) {
964 // An error occurred...
965 }
966
967 // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number
968 // of output frames written.
969 ```
970
971The data converter supports multiple channels and is always interleaved (both input and output). The channel count cannot be changed after initialization.
972
973Sample rates can be anything other than zero, and are always specified in hertz. They should be set to something like 44100, etc. The sample rate is the only
974configuration property that can be changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of `ma_data_converter_config` is
975set to `MA_TRUE`. To change the sample rate, use `ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out. The
976resampling algorithm cannot be changed after initialization.
977
978Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process
979frames, use `ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames you can fit in the output buffer and the number
980of input frames contained in the input buffer. On output these variables contain the number of output frames that were written to the output buffer and the
981number of input frames that were consumed in the process. You can pass in NULL for the input buffer in which case it will be treated as an infinitely large
982buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated as seek.
983
984Sometimes it's useful to know exactly how many input frames will be required to output a specific number of frames. You can calculate this with
985`ma_data_converter_get_required_input_frame_count()`. Likewise, it's sometimes useful to know exactly how many frames would be output given a certain number of
986input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`.
987
988Due to the nature of how resampling works, the data converter introduces some latency if resampling is required. This can be retrieved in terms of both the
989input rate and the output rate with `ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`.
990
991
992
9937. Filtering
994============
995
9967.1. Biquad Filtering
997---------------------
998Biquad filtering is achieved with the `ma_biquad` API. Example:
999
1000 ```c
1001 ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);
1002 ma_result result = ma_biquad_init(&config, &biquad);
1003 if (result != MA_SUCCESS) {
1004 // Error.
1005 }
1006
1007 ...
1008
1009 ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount);
1010 ```
1011
1012Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0, b1 and b2, and the denominator coefficients are a0, a1 and
1013a2. The a0 coefficient is required and coefficients must not be pre-normalized.
1014
1015Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format you need to convert it yourself beforehand. When using
1016`ma_format_s16` the biquad filter will use fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used.
1017
1018Input and output frames are always interleaved.
1019
1020Filtering can be applied in-place by passing in the same pointer for both the input and output buffers, like so:
1021
1022 ```c
1023 ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount);
1024 ```
1025
1026If you need to change the values of the coefficients, but maintain the values in the registers you can do so with `ma_biquad_reinit()`. This is useful if you
1027need to change the properties of the filter while keeping the values of registers valid to avoid glitching. Do not use `ma_biquad_init()` for this as it will
1028do a full initialization which involves clearing the registers to 0. Note that changing the format or channel count after initialization is invalid and will
1029result in an error.
1030
1031
10327.2. Low-Pass Filtering
1033-----------------------
1034Low-pass filtering is achieved with the following APIs:
1035
1036 +---------+------------------------------------------+
1037 | API | Description |
1038 +---------+------------------------------------------+
1039 | ma_lpf1 | First order low-pass filter |
1040 | ma_lpf2 | Second order low-pass filter |
1041 | ma_lpf | High order low-pass filter (Butterworth) |
1042 +---------+------------------------------------------+
1043
1044Low-pass filter example:
1045
1046 ```c
1047 ma_lpf_config config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
1048 ma_result result = ma_lpf_init(&config, &lpf);
1049 if (result != MA_SUCCESS) {
1050 // Error.
1051 }
1052
1053 ...
1054
1055 ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount);
1056 ```
1057
1058Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format you need to convert it yourself beforehand. Input and output
1059frames are always interleaved.
1060
1061Filtering can be applied in-place by passing in the same pointer for both the input and output buffers, like so:
1062
1063 ```c
1064 ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount);
1065 ```
1066
1067The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more, you can chain first and second order filters together.
1068
1069 ```c
1070 for (iFilter = 0; iFilter < filterCount; iFilter += 1) {
1071 ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount);
1072 }
1073 ```
1074
1075If you need to change the configuration of the filter, but need to maintain the state of internal registers you can do so with `ma_lpf_reinit()`. This may be
1076useful if you need to change the sample rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the format or channel
1077count after initialization is invalid and will result in an error.
1078
1079The `ma_lpf` object supports a configurable order, but if you only need a first order filter you may want to consider using `ma_lpf1`. Likewise, if you only
1080need a second order filter you can use `ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient.
1081
1082If an even filter order is specified, a series of second order filters will be processed in a chain. If an odd filter order is specified, a first order filter
1083will be applied, followed by a series of second order filters in a chain.
1084
1085
10867.3. High-Pass Filtering
1087------------------------
1088High-pass filtering is achieved with the following APIs:
1089
1090 +---------+-------------------------------------------+
1091 | API | Description |
1092 +---------+-------------------------------------------+
1093 | ma_hpf1 | First order high-pass filter |
1094 | ma_hpf2 | Second order high-pass filter |
1095 | ma_hpf | High order high-pass filter (Butterworth) |
1096 +---------+-------------------------------------------+
1097
1098High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, `ma_hpf2` and `ma_hpf`. See example code for low-pass filters
1099for example usage.
1100
1101
11027.4. Band-Pass Filtering
1103------------------------
1104Band-pass filtering is achieved with the following APIs:
1105
1106 +---------+-------------------------------+
1107 | API | Description |
1108 +---------+-------------------------------+
1109 | ma_bpf2 | Second order band-pass filter |
1110 | ma_bpf | High order band-pass filter |
1111 +---------+-------------------------------+
1112
1113Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and `ma_hpf`. See example code for low-pass filters for example
1114usage. Note that the order for band-pass filters must be an even number which means there is no first order band-pass filter, unlike low-pass and high-pass
1115filters.
1116
1117
11187.5. Notch Filtering
1119--------------------
1120Notch filtering is achieved with the following APIs:
1121
1122 +-----------+------------------------------------------+
1123 | API | Description |
1124 +-----------+------------------------------------------+
1125 | ma_notch2 | Second order notching filter |
1126 +-----------+------------------------------------------+
1127
1128
11297.6. Peaking EQ Filtering
1130-------------------------
1131Peaking filtering is achieved with the following APIs:
1132
1133 +----------+------------------------------------------+
1134 | API | Description |
1135 +----------+------------------------------------------+
1136 | ma_peak2 | Second order peaking filter |
1137 +----------+------------------------------------------+
1138
1139
11407.7. Low Shelf Filtering
1141------------------------
1142Low shelf filtering is achieved with the following APIs:
1143
1144 +-------------+------------------------------------------+
1145 | API | Description |
1146 +-------------+------------------------------------------+
1147 | ma_loshelf2 | Second order low shelf filter |
1148 +-------------+------------------------------------------+
1149
1150Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to just turn them down rather than eliminate them entirely.
1151
1152
11537.8. High Shelf Filtering
1154-------------------------
1155High shelf filtering is achieved with the following APIs:
1156
1157 +-------------+------------------------------------------+
1158 | API | Description |
1159 +-------------+------------------------------------------+
1160 | ma_hishelf2 | Second order high shelf filter |
1161 +-------------+------------------------------------------+
1162
1163The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf` instead of `ma_loshelf`. Where a low shelf filter is used to
1164adjust the volume of low frequencies, the high shelf filter does the same thing for high frequencies.
1165
1166
1167
1168
11698. Waveform and Noise Generation
1170================================
1171
11728.1. Waveforms
1173--------------
1174miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved with the `ma_waveform` API. Example:
1175
1176 ```c
1177 ma_waveform_config config = ma_waveform_config_init(
1178 FORMAT,
1179 CHANNELS,
1180 SAMPLE_RATE,
1181 ma_waveform_type_sine,
1182 amplitude,
1183 frequency);
1184
1185 ma_waveform waveform;
1186 ma_result result = ma_waveform_init(&config, &waveform);
1187 if (result != MA_SUCCESS) {
1188 // Error.
1189 }
1190
1191 ...
1192
1193 ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount);
1194 ```
1195
1196The amplitude, frequency, type, and sample rate can be changed dynamically with `ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`,
1197`ma_waveform_set_type()`, and `ma_waveform_set_sample_rate()` respectively.
1198
1199You can invert the waveform by setting the amplitude to a negative value. You can use this to control whether or not a sawtooth has a positive or negative
1200ramp, for example.
1201
1202Below are the supported waveform types:
1203
1204 +---------------------------+
1205 | Enum Name |
1206 +---------------------------+
1207 | ma_waveform_type_sine |
1208 | ma_waveform_type_square |
1209 | ma_waveform_type_triangle |
1210 | ma_waveform_type_sawtooth |
1211 +---------------------------+
1212
1213
1214
12158.2. Noise
1216----------
1217miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example:
1218
1219 ```c
1220 ma_noise_config config = ma_noise_config_init(
1221 FORMAT,
1222 CHANNELS,
1223 ma_noise_type_white,
1224 SEED,
1225 amplitude);
1226
1227 ma_noise noise;
1228 ma_result result = ma_noise_init(&config, &noise);
1229 if (result != MA_SUCCESS) {
1230 // Error.
1231 }
1232
1233 ...
1234
1235 ma_noise_read_pcm_frames(&noise, pOutput, frameCount);
1236 ```
1237
1238The noise API uses simple LCG random number generation. It supports a custom seed which is useful for things like automated testing requiring reproducibility.
1239Setting the seed to zero will default to `MA_DEFAULT_LCG_SEED`.
1240
1241The amplitude, seed, and type can be changed dynamically with `ma_noise_set_amplitude()`, `ma_noise_set_seed()`, and `ma_noise_set_type()` respectively.
1242
1243By default, the noise API will use different values for different channels. So, for example, the left side in a stereo stream will be different to the right
1244side. To instead have each channel use the same random value, set the `duplicateChannels` member of the noise config to true, like so:
1245
1246 ```c
1247 config.duplicateChannels = MA_TRUE;
1248 ```
1249
1250Below are the supported noise types.
1251
1252 +------------------------+
1253 | Enum Name |
1254 +------------------------+
1255 | ma_noise_type_white |
1256 | ma_noise_type_pink |
1257 | ma_noise_type_brownian |
1258 +------------------------+
1259
1260
1261
12629. Audio Buffers
1263================
1264miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can read from memory that's managed by the application, but
1265can also handle the memory management for you internally. Memory management is flexible and should support most use cases.
1266
1267Audio buffers are initialised using the standard configuration system used everywhere in miniaudio:
1268
1269 ```c
1270 ma_audio_buffer_config config = ma_audio_buffer_config_init(
1271 format,
1272 channels,
1273 sizeInFrames,
1274 pExistingData,
1275 &allocationCallbacks);
1276
1277 ma_audio_buffer buffer;
1278 result = ma_audio_buffer_init(&config, &buffer);
1279 if (result != MA_SUCCESS) {
1280 // Error.
1281 }
1282
1283 ...
1284
1285 ma_audio_buffer_uninit(&buffer);
1286 ```
1287
1288In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an application can do self-managed memory allocation. If you
1289would rather make a copy of the data, use `ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`.
1290
1291Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the raw audio data in a contiguous block of memory. That is,
1292the raw audio data will be located immediately after the `ma_audio_buffer` structure. To do this, use `ma_audio_buffer_alloc_and_init()`:
1293
1294 ```c
1295 ma_audio_buffer_config config = ma_audio_buffer_config_init(
1296 format,
1297 channels,
1298 sizeInFrames,
1299 pExistingData,
1300 &allocationCallbacks);
1301
1302 ma_audio_buffer* pBuffer
1303 result = ma_audio_buffer_alloc_and_init(&config, &pBuffer);
1304 if (result != MA_SUCCESS) {
1305 // Error
1306 }
1307
1308 ...
1309
1310 ma_audio_buffer_uninit_and_free(&buffer);
1311 ```
1312
1313If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it with `ma_audio_buffer_uninit_and_free()`. In the example above,
1314the memory pointed to by `pExistingData` will be copied into the buffer, which is contrary to the behavior of `ma_audio_buffer_init()`.
1315
1316An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the cursor moves forward. The last parameter (`loop`) can be
1317used to determine if the buffer should loop. The return value is the number of frames actually read. If this is less than the number of frames requested it
1318means the end has been reached. This should never happen if the `loop` parameter is set to true. If you want to manually loop back to the start, you can do so
1319with with `ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an audio buffer.
1320
1321 ```c
1322 ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping);
1323 if (framesRead < desiredFrameCount) {
1324 // If not looping, this means the end has been reached. This should never happen in looping mode with valid input.
1325 }
1326 ```
1327
1328Sometimes you may want to avoid the cost of data movement between the internal buffer and the output buffer. Instead you can use memory mapping to retrieve a
1329pointer to a segment of data:
1330
1331 ```c
1332 void* pMappedFrames;
1333 ma_uint64 frameCount = frameCountToTryMapping;
1334 ma_result result = ma_audio_buffer_map(pAudioBuffer, &pMappedFrames, &frameCount);
1335 if (result == MA_SUCCESS) {
1336 // Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be
1337 // less due to the end of the buffer being reached.
1338 ma_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels);
1339
1340 // You must unmap the buffer.
1341 ma_audio_buffer_unmap(pAudioBuffer, frameCount);
1342 }
1343 ```
1344
1345When you use memory mapping, the read cursor is increment by the frame count passed in to `ma_audio_buffer_unmap()`. If you decide not to process every frame
1346you can pass in a value smaller than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is that it does not handle looping
1347for you. You can determine if the buffer is at the end for the purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of
1348`ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END` as an error when returned by `ma_audio_buffer_unmap()`.
1349
1350
1351
135210. Ring Buffers
1353================
1354miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates
1355on bytes, whereas the `ma_pcm_rb` operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around `ma_rb`.
1356
1357Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved streams. The caller can also allocate their own backing memory for
1358the ring buffer to use internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for you.
1359
1360The examples below use the PCM frame variant of the ring buffer since that's most likely the one you will want to use. To initialize a ring buffer, do
1361something like the following:
1362
1363 ```c
1364 ma_pcm_rb rb;
1365 ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb);
1366 if (result != MA_SUCCESS) {
1367 // Error
1368 }
1369 ```
1370
1371The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because it's the PCM varient of the ring buffer API. For the regular
1372ring buffer that operates on bytes you would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes instead of frames. The
1373fourth parameter is an optional pre-allocated buffer and the fifth parameter is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation
1374routines. Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used.
1375
1376Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is offset from each other based on the stride. To manage your
1377sub-buffers you can use `ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and `ma_pcm_rb_get_subbuffer_ptr()`.
1378
1379Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section of the ring buffer. You specify the number of frames you
1380need, and on output it will set to what was actually acquired. If the read or write pointer is positioned such that the number of frames requested will require
1381a loop, it will be clamped to the end of the buffer. Therefore, the number of frames you're given may be less than the number you requested.
1382
1383After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the buffer and then "commit" it with `ma_pcm_rb_commit_read()` or
1384`ma_pcm_rb_commit_write()`. This is where the read/write pointers are updated. When you commit you need to pass in the buffer that was returned by the earlier
1385call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and
1386`ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was originally requested.
1387
1388If you want to correct for drift between the write pointer and the read pointer you can use a combination of `ma_pcm_rb_pointer_distance()`,
1389`ma_pcm_rb_seek_read()` and `ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only move the read pointer forward via
1390the consumer thread, and the write pointer forward by the producer thread. If there is too much space between the pointers, move the read pointer forward. If
1391there is too little space between the pointers, move the write pointer forward.
1392
1393You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb` API. This is exactly the same, only you will use the `ma_rb`
1394functions instead of `ma_pcm_rb` and instead of frame counts you will pass around byte counts.
1395
1396The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most significant bit being used to encode a loop flag and the internally
1397managed buffers always being aligned to MA_SIMD_ALIGNMENT.
1398
1399Note that the ring buffer is only thread safe when used by a single consumer thread and single producer thread.
1400
1401
1402
140311. Backends
1404============
1405The following backends are supported by miniaudio.
1406
1407 +-------------+-----------------------+--------------------------------------------------------+
1408 | Name | Enum Name | Supported Operating Systems |
1409 +-------------+-----------------------+--------------------------------------------------------+
1410 | WASAPI | ma_backend_wasapi | Windows Vista+ |
1411 | DirectSound | ma_backend_dsound | Windows XP+ |
1412 | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) |
1413 | Core Audio | ma_backend_coreaudio | macOS, iOS |
1414 | ALSA | ma_backend_alsa | Linux |
1415 | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
1416 | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
1417 | sndio | ma_backend_sndio | OpenBSD |
1418 | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
1419 | OSS | ma_backend_oss | FreeBSD |
1420 | AAudio | ma_backend_aaudio | Android 8+ |
1421 | OpenSL ES | ma_backend_opensl | Android (API level 16+) |
1422 | Web Audio | ma_backend_webaudio | Web (via Emscripten) |
1423 | Custom | ma_backend_custom | Cross Platform |
1424 | Null | ma_backend_null | Cross Platform (not used on Web) |
1425 +-------------+-----------------------+--------------------------------------------------------+
1426
1427Some backends have some nuance details you may want to be aware of.
1428
142911.1. WASAPI
1430------------
1431- Low-latency shared mode will be disabled when using an application-defined sample rate which is different to the device's native sample rate. To work around
1432 this, set `wasapi.noAutoConvertSRC` to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing when the
1433 `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC will result in miniaudio's internal resampler being used instead
1434 which will in turn enable the use of low-latency shared mode.
1435
143611.2. PulseAudio
1437----------------
1438- If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki:
1439 https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling. Alternatively, consider using a different backend such as ALSA.
1440
144111.3. Android
1442-------------
1443- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: `<uses-permission android:name="android.permission.RECORD_AUDIO" />`
1444- With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a limitation with OpenSL|ES.
1445- With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration API (devices are enumerated through Java). You can however
1446 perform your own device enumeration through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it to ma_device_init().
1447- The backend API will perform resampling where possible. The reason for this as opposed to using miniaudio's built-in resampler is to take advantage of any
1448 potential device-specific optimizations the driver may implement.
1449
145011.4. UWP
1451---------
1452- UWP only supports default playback and capture devices.
1453- UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest):
1454
1455 ```
1456 <Package ...>
1457 ...
1458 <Capabilities>
1459 <DeviceCapability Name="microphone" />
1460 </Capabilities>
1461 </Package>
1462 ```
1463
146411.5. Web Audio / Emscripten
1465----------------------------
1466- You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build.
1467- The first time a context is initialized it will create a global object called "miniaudio" whose primary purpose is to act as a factory for device objects.
1468- Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as they've been deprecated.
1469- Google has implemented a policy in their browsers that prevent automatic media output without first receiving some kind of user input. The following web page
1470 has additional details: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device may fail if you try to start playback
1471 without first handling some kind of user input.
1472
1473
1474
147512. Miscellaneous Notes
1476=======================
1477- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for WASAPI and Core Audio, however other backends such as
1478 PulseAudio may naturally support it, though not all have been tested.
1479- The contents of the output buffer passed into the data callback will always be pre-initialized to silence unless the `noPreZeroedOutputBuffer` config variable
1480 in `ma_device_config` is set to true, in which case it'll be undefined which will require you to write something to the entire buffer.
1481- By default miniaudio will automatically clip samples. This only applies when the playback sample format is configured as `ma_format_f32`. If you are doing
1482 clipping yourself, you can disable this overhead by setting `noClip` to true in the device config.
1483- The sndio backend is currently only enabled on OpenBSD builds.
1484- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can use it.
1485- Note that GCC and Clang requires `-msse2`, `-mavx2`, etc. for SIMD optimizations.
1486- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This is due to 64-bit file APIs not being available.
1487*/
1488
1489#ifndef miniaudio_h
1490#define miniaudio_h
1491
1492#ifdef __cplusplus
1493extern "C" {
1494#endif
1495
1496#define MA_STRINGIFY(x) #x
1497#define MA_XSTRINGIFY(x) MA_STRINGIFY(x)
1498
1499#define MA_VERSION_MAJOR 0
1500#define MA_VERSION_MINOR 10
1501#define MA_VERSION_REVISION 40
1502#define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION)
1503
1504#if defined(_MSC_VER) && !defined(__clang__)
1505 #pragma warning(push)
1506 #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
1507 #pragma warning(disable:4214) /* nonstandard extension used: bit field types other than int */
1508 #pragma warning(disable:4324) /* structure was padded due to alignment specifier */
1509#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
1510 #pragma GCC diagnostic push
1511 #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
1512 #if defined(__clang__)
1513 #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */
1514 #endif
1515#endif
1516
1517/* Platform/backend detection. */
1518#ifdef _WIN32
1519 #define MA_WIN32
1520 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
1521 #define MA_WIN32_UWP
1522 #else
1523 #define MA_WIN32_DESKTOP
1524 #endif
1525#else
1526 #define MA_POSIX
1527 #include <pthread.h> /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */
1528
1529 #ifdef __unix__
1530 #define MA_UNIX
1531 #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
1532 #define MA_BSD
1533 #endif
1534 #endif
1535 #ifdef __linux__
1536 #define MA_LINUX
1537 #endif
1538 #ifdef __APPLE__
1539 #define MA_APPLE
1540 #endif
1541 #ifdef __ANDROID__
1542 #define MA_ANDROID
1543 #endif
1544 #ifdef __EMSCRIPTEN__
1545 #define MA_EMSCRIPTEN
1546 #endif
1547#endif
1548
1549#include <stddef.h> /* For size_t. */
1550
1551/* Sized types. */
1552typedef signed char ma_int8;
1553typedef unsigned char ma_uint8;
1554typedef signed short ma_int16;
1555typedef unsigned short ma_uint16;
1556typedef signed int ma_int32;
1557typedef unsigned int ma_uint32;
1558#if defined(_MSC_VER)
1559 typedef signed __int64 ma_int64;
1560 typedef unsigned __int64 ma_uint64;
1561#else
1562 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
1563 #pragma GCC diagnostic push
1564 #pragma GCC diagnostic ignored "-Wlong-long"
1565 #if defined(__clang__)
1566 #pragma GCC diagnostic ignored "-Wc++11-long-long"
1567 #endif
1568 #endif
1569 typedef signed long long ma_int64;
1570 typedef unsigned long long ma_uint64;
1571 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
1572 #pragma GCC diagnostic pop
1573 #endif
1574#endif
1575#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
1576 typedef ma_uint64 ma_uintptr;
1577#else
1578 typedef ma_uint32 ma_uintptr;
1579#endif
1580
1581typedef ma_uint8 ma_bool8;
1582typedef ma_uint32 ma_bool32;
1583#define MA_TRUE 1
1584#define MA_FALSE 0
1585
1586typedef void* ma_handle;
1587typedef void* ma_ptr;
1588typedef void (* ma_proc)(void);
1589
1590#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED)
1591typedef ma_uint16 wchar_t;
1592#endif
1593
1594/* Define NULL for some compilers. */
1595#ifndef NULL
1596#define NULL 0
1597#endif
1598
1599#if defined(SIZE_MAX)
1600 #define MA_SIZE_MAX SIZE_MAX
1601#else
1602 #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */
1603#endif
1604
1605
1606#ifdef _MSC_VER
1607 #define MA_INLINE __forceinline
1608#elif defined(__GNUC__)
1609 /*
1610 I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
1611 the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
1612 case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
1613 command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
1614 I am using "__inline__" only when we're compiling in strict ANSI mode.
1615 */
1616 #if defined(__STRICT_ANSI__)
1617 #define MA_INLINE __inline__ __attribute__((always_inline))
1618 #else
1619 #define MA_INLINE inline __attribute__((always_inline))
1620 #endif
1621#elif defined(__WATCOMC__)
1622 #define MA_INLINE __inline
1623#else
1624 #define MA_INLINE
1625#endif
1626
1627#if !defined(MA_API)
1628 #if defined(MA_DLL)
1629 #if defined(_WIN32)
1630 #define MA_DLL_IMPORT __declspec(dllimport)
1631 #define MA_DLL_EXPORT __declspec(dllexport)
1632 #define MA_DLL_PRIVATE static
1633 #else
1634 #if defined(__GNUC__) && __GNUC__ >= 4
1635 #define MA_DLL_IMPORT __attribute__((visibility("default")))
1636 #define MA_DLL_EXPORT __attribute__((visibility("default")))
1637 #define MA_DLL_PRIVATE __attribute__((visibility("hidden")))
1638 #else
1639 #define MA_DLL_IMPORT
1640 #define MA_DLL_EXPORT
1641 #define MA_DLL_PRIVATE static
1642 #endif
1643 #endif
1644
1645 #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
1646 #define MA_API MA_DLL_EXPORT
1647 #else
1648 #define MA_API MA_DLL_IMPORT
1649 #endif
1650 #define MA_PRIVATE MA_DLL_PRIVATE
1651 #else
1652 #define MA_API extern
1653 #define MA_PRIVATE static
1654 #endif
1655#endif
1656
1657/* SIMD alignment in bytes. Currently set to 64 bytes in preparation for future AVX-512 optimizations. */
1658#define MA_SIMD_ALIGNMENT 64
1659
1660
1661/*
1662Logging Levels
1663==============
1664Log levels are only used to give logging callbacks some context as to the severity of a log message
1665so they can do filtering. All log levels will be posted to registered logging callbacks, except for
1666MA_LOG_LEVEL_DEBUG which will only get processed if MA_DEBUG_OUTPUT is enabled.
1667
1668MA_LOG_LEVEL_DEBUG
1669 Used for debugging. These log messages are only posted when `MA_DEBUG_OUTPUT` is enabled.
1670
1671MA_LOG_LEVEL_INFO
1672 Informational logging. Useful for debugging. This will also enable warning and error logs. This
1673 will never be called from within the data callback.
1674
1675MA_LOG_LEVEL_WARNING
1676 Warnings. You should enable this in you development builds and action them when encounted. This
1677 will also enable error logs. These logs usually indicate a potential problem or
1678 misconfiguration, but still allow you to keep running. This will never be called from within
1679 the data callback.
1680
1681MA_LOG_LEVEL_ERROR
1682 Error logging. This will be fired when an operation fails and is subsequently aborted. This can
1683 be fired from within the data callback, in which case the device will be stopped. You should
1684 always have this log level enabled.
1685*/
1686#define MA_LOG_LEVEL_DEBUG 4
1687#define MA_LOG_LEVEL_INFO 3
1688#define MA_LOG_LEVEL_WARNING 2
1689#define MA_LOG_LEVEL_ERROR 1
1690
1691/* Deprecated. */
1692#define MA_LOG_LEVEL_VERBOSE MA_LOG_LEVEL_DEBUG
1693
1694/* Deprecated. */
1695#ifndef MA_LOG_LEVEL
1696#define MA_LOG_LEVEL MA_LOG_LEVEL_ERROR
1697#endif
1698
1699/*
1700An annotation for variables which must be used atomically. This doesn't actually do anything - it's
1701just used as a way for humans to identify variables that should be used atomically.
1702*/
1703#define MA_ATOMIC
1704
1705typedef struct ma_context ma_context;
1706typedef struct ma_device ma_device;
1707
1708typedef ma_uint8 ma_channel;
1709#define MA_CHANNEL_NONE 0
1710#define MA_CHANNEL_MONO 1
1711#define MA_CHANNEL_FRONT_LEFT 2
1712#define MA_CHANNEL_FRONT_RIGHT 3
1713#define MA_CHANNEL_FRONT_CENTER 4
1714#define MA_CHANNEL_LFE 5
1715#define MA_CHANNEL_BACK_LEFT 6
1716#define MA_CHANNEL_BACK_RIGHT 7
1717#define MA_CHANNEL_FRONT_LEFT_CENTER 8
1718#define MA_CHANNEL_FRONT_RIGHT_CENTER 9
1719#define MA_CHANNEL_BACK_CENTER 10
1720#define MA_CHANNEL_SIDE_LEFT 11
1721#define MA_CHANNEL_SIDE_RIGHT 12
1722#define MA_CHANNEL_TOP_CENTER 13
1723#define MA_CHANNEL_TOP_FRONT_LEFT 14
1724#define MA_CHANNEL_TOP_FRONT_CENTER 15
1725#define MA_CHANNEL_TOP_FRONT_RIGHT 16
1726#define MA_CHANNEL_TOP_BACK_LEFT 17
1727#define MA_CHANNEL_TOP_BACK_CENTER 18
1728#define MA_CHANNEL_TOP_BACK_RIGHT 19
1729#define MA_CHANNEL_AUX_0 20
1730#define MA_CHANNEL_AUX_1 21
1731#define MA_CHANNEL_AUX_2 22
1732#define MA_CHANNEL_AUX_3 23
1733#define MA_CHANNEL_AUX_4 24
1734#define MA_CHANNEL_AUX_5 25
1735#define MA_CHANNEL_AUX_6 26
1736#define MA_CHANNEL_AUX_7 27
1737#define MA_CHANNEL_AUX_8 28
1738#define MA_CHANNEL_AUX_9 29
1739#define MA_CHANNEL_AUX_10 30
1740#define MA_CHANNEL_AUX_11 31
1741#define MA_CHANNEL_AUX_12 32
1742#define MA_CHANNEL_AUX_13 33
1743#define MA_CHANNEL_AUX_14 34
1744#define MA_CHANNEL_AUX_15 35
1745#define MA_CHANNEL_AUX_16 36
1746#define MA_CHANNEL_AUX_17 37
1747#define MA_CHANNEL_AUX_18 38
1748#define MA_CHANNEL_AUX_19 39
1749#define MA_CHANNEL_AUX_20 40
1750#define MA_CHANNEL_AUX_21 41
1751#define MA_CHANNEL_AUX_22 42
1752#define MA_CHANNEL_AUX_23 43
1753#define MA_CHANNEL_AUX_24 44
1754#define MA_CHANNEL_AUX_25 45
1755#define MA_CHANNEL_AUX_26 46
1756#define MA_CHANNEL_AUX_27 47
1757#define MA_CHANNEL_AUX_28 48
1758#define MA_CHANNEL_AUX_29 49
1759#define MA_CHANNEL_AUX_30 50
1760#define MA_CHANNEL_AUX_31 51
1761#define MA_CHANNEL_LEFT MA_CHANNEL_FRONT_LEFT
1762#define MA_CHANNEL_RIGHT MA_CHANNEL_FRONT_RIGHT
1763#define MA_CHANNEL_POSITION_COUNT (MA_CHANNEL_AUX_31 + 1)
1764
1765
1766typedef int ma_result;
1767#define MA_SUCCESS 0
1768#define MA_ERROR -1 /* A generic error. */
1769#define MA_INVALID_ARGS -2
1770#define MA_INVALID_OPERATION -3
1771#define MA_OUT_OF_MEMORY -4
1772#define MA_OUT_OF_RANGE -5
1773#define MA_ACCESS_DENIED -6
1774#define MA_DOES_NOT_EXIST -7
1775#define MA_ALREADY_EXISTS -8
1776#define MA_TOO_MANY_OPEN_FILES -9
1777#define MA_INVALID_FILE -10
1778#define MA_TOO_BIG -11
1779#define MA_PATH_TOO_LONG -12
1780#define MA_NAME_TOO_LONG -13
1781#define MA_NOT_DIRECTORY -14
1782#define MA_IS_DIRECTORY -15
1783#define MA_DIRECTORY_NOT_EMPTY -16
1784#define MA_AT_END -17
1785#define MA_NO_SPACE -18
1786#define MA_BUSY -19
1787#define MA_IO_ERROR -20
1788#define MA_INTERRUPT -21
1789#define MA_UNAVAILABLE -22
1790#define MA_ALREADY_IN_USE -23
1791#define MA_BAD_ADDRESS -24
1792#define MA_BAD_SEEK -25
1793#define MA_BAD_PIPE -26
1794#define MA_DEADLOCK -27
1795#define MA_TOO_MANY_LINKS -28
1796#define MA_NOT_IMPLEMENTED -29
1797#define MA_NO_MESSAGE -30
1798#define MA_BAD_MESSAGE -31
1799#define MA_NO_DATA_AVAILABLE -32
1800#define MA_INVALID_DATA -33
1801#define MA_TIMEOUT -34
1802#define MA_NO_NETWORK -35
1803#define MA_NOT_UNIQUE -36
1804#define MA_NOT_SOCKET -37
1805#define MA_NO_ADDRESS -38
1806#define MA_BAD_PROTOCOL -39
1807#define MA_PROTOCOL_UNAVAILABLE -40
1808#define MA_PROTOCOL_NOT_SUPPORTED -41
1809#define MA_PROTOCOL_FAMILY_NOT_SUPPORTED -42
1810#define MA_ADDRESS_FAMILY_NOT_SUPPORTED -43
1811#define MA_SOCKET_NOT_SUPPORTED -44
1812#define MA_CONNECTION_RESET -45
1813#define MA_ALREADY_CONNECTED -46
1814#define MA_NOT_CONNECTED -47
1815#define MA_CONNECTION_REFUSED -48
1816#define MA_NO_HOST -49
1817#define MA_IN_PROGRESS -50
1818#define MA_CANCELLED -51
1819#define MA_MEMORY_ALREADY_MAPPED -52
1820
1821/* General miniaudio-specific errors. */
1822#define MA_FORMAT_NOT_SUPPORTED -100
1823#define MA_DEVICE_TYPE_NOT_SUPPORTED -101
1824#define MA_SHARE_MODE_NOT_SUPPORTED -102
1825#define MA_NO_BACKEND -103
1826#define MA_NO_DEVICE -104
1827#define MA_API_NOT_FOUND -105
1828#define MA_INVALID_DEVICE_CONFIG -106
1829#define MA_LOOP -107
1830
1831/* State errors. */
1832#define MA_DEVICE_NOT_INITIALIZED -200
1833#define MA_DEVICE_ALREADY_INITIALIZED -201
1834#define MA_DEVICE_NOT_STARTED -202
1835#define MA_DEVICE_NOT_STOPPED -203
1836
1837/* Operation errors. */
1838#define MA_FAILED_TO_INIT_BACKEND -300
1839#define MA_FAILED_TO_OPEN_BACKEND_DEVICE -301
1840#define MA_FAILED_TO_START_BACKEND_DEVICE -302
1841#define MA_FAILED_TO_STOP_BACKEND_DEVICE -303
1842
1843
1844#define MA_MIN_CHANNELS 1
1845#ifndef MA_MAX_CHANNELS
1846#define MA_MAX_CHANNELS 32
1847#endif
1848
1849
1850#ifndef MA_MAX_FILTER_ORDER
1851#define MA_MAX_FILTER_ORDER 8
1852#endif
1853
1854typedef enum
1855{
1856 ma_stream_format_pcm = 0
1857} ma_stream_format;
1858
1859typedef enum
1860{
1861 ma_stream_layout_interleaved = 0,
1862 ma_stream_layout_deinterleaved
1863} ma_stream_layout;
1864
1865typedef enum
1866{
1867 ma_dither_mode_none = 0,
1868 ma_dither_mode_rectangle,
1869 ma_dither_mode_triangle
1870} ma_dither_mode;
1871
1872typedef enum
1873{
1874 /*
1875 I like to keep these explicitly defined because they're used as a key into a lookup table. When items are
1876 added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample().
1877 */
1878 ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */
1879 ma_format_u8 = 1,
1880 ma_format_s16 = 2, /* Seems to be the most widely supported format. */
1881 ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */
1882 ma_format_s32 = 4,
1883 ma_format_f32 = 5,
1884 ma_format_count
1885} ma_format;
1886
1887typedef enum
1888{
1889 /* Standard rates need to be in priority order. */
1890 ma_standard_sample_rate_48000 = 48000, /* Most common */
1891 ma_standard_sample_rate_44100 = 44100,
1892
1893 ma_standard_sample_rate_32000 = 32000, /* Lows */
1894 ma_standard_sample_rate_24000 = 24000,
1895 ma_standard_sample_rate_22050 = 22050,
1896
1897 ma_standard_sample_rate_88200 = 88200, /* Highs */
1898 ma_standard_sample_rate_96000 = 96000,
1899 ma_standard_sample_rate_176400 = 176400,
1900 ma_standard_sample_rate_192000 = 192000,
1901
1902 ma_standard_sample_rate_16000 = 16000, /* Extreme lows */
1903 ma_standard_sample_rate_11025 = 11250,
1904 ma_standard_sample_rate_8000 = 8000,
1905
1906 ma_standard_sample_rate_352800 = 352800, /* Extreme highs */
1907 ma_standard_sample_rate_384000 = 384000,
1908
1909 ma_standard_sample_rate_min = ma_standard_sample_rate_8000,
1910 ma_standard_sample_rate_max = ma_standard_sample_rate_384000,
1911 ma_standard_sample_rate_count = 14 /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */
1912} ma_standard_sample_rate;
1913
1914/* These are deprecated. Use ma_standard_sample_rate_min and ma_standard_sample_rate_max. */
1915#define MA_MIN_SAMPLE_RATE (ma_uint32)ma_standard_sample_rate_min
1916#define MA_MAX_SAMPLE_RATE (ma_uint32)ma_standard_sample_rate_max
1917
1918
1919typedef enum
1920{
1921 ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */
1922 ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */
1923 ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_router_config. */
1924 ma_channel_mix_mode_planar_blend = ma_channel_mix_mode_rectangular,
1925 ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular
1926} ma_channel_mix_mode;
1927
1928typedef enum
1929{
1930 ma_standard_channel_map_microsoft,
1931 ma_standard_channel_map_alsa,
1932 ma_standard_channel_map_rfc3551, /* Based off AIFF. */
1933 ma_standard_channel_map_flac,
1934 ma_standard_channel_map_vorbis,
1935 ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */
1936 ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */
1937 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. */
1938 ma_standard_channel_map_default = ma_standard_channel_map_microsoft
1939} ma_standard_channel_map;
1940
1941typedef enum
1942{
1943 ma_performance_profile_low_latency = 0,
1944 ma_performance_profile_conservative
1945} ma_performance_profile;
1946
1947
1948typedef struct
1949{
1950 void* pUserData;
1951 void* (* onMalloc)(size_t sz, void* pUserData);
1952 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
1953 void (* onFree)(void* p, void* pUserData);
1954} ma_allocation_callbacks;
1955
1956typedef struct
1957{
1958 ma_int32 state;
1959} ma_lcg;
1960
1961
1962#ifndef MA_NO_THREADING
1963/* Thread priorities should be ordered such that the default priority of the worker thread is 0. */
1964typedef enum
1965{
1966 ma_thread_priority_idle = -5,
1967 ma_thread_priority_lowest = -4,
1968 ma_thread_priority_low = -3,
1969 ma_thread_priority_normal = -2,
1970 ma_thread_priority_high = -1,
1971 ma_thread_priority_highest = 0,
1972 ma_thread_priority_realtime = 1,
1973 ma_thread_priority_default = 0
1974} ma_thread_priority;
1975
1976/* Spinlocks are 32-bit for compatibility reasons. */
1977typedef ma_uint32 ma_spinlock;
1978
1979#if defined(MA_WIN32)
1980typedef ma_handle ma_thread;
1981#endif
1982#if defined(MA_POSIX)
1983typedef pthread_t ma_thread;
1984#endif
1985
1986#if defined(MA_WIN32)
1987typedef ma_handle ma_mutex;
1988#endif
1989#if defined(MA_POSIX)
1990typedef pthread_mutex_t ma_mutex;
1991#endif
1992
1993#if defined(MA_WIN32)
1994typedef ma_handle ma_event;
1995#endif
1996#if defined(MA_POSIX)
1997typedef struct
1998{
1999 ma_uint32 value;
2000 pthread_mutex_t lock;
2001 pthread_cond_t cond;
2002} ma_event;
2003#endif /* MA_POSIX */
2004
2005#if defined(MA_WIN32)
2006typedef ma_handle ma_semaphore;
2007#endif
2008#if defined(MA_POSIX)
2009typedef struct
2010{
2011 int value;
2012 pthread_mutex_t lock;
2013 pthread_cond_t cond;
2014} ma_semaphore;
2015#endif /* MA_POSIX */
2016#else
2017/* 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. */
2018#ifndef MA_NO_DEVICE_IO
2019#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO";
2020#endif
2021#endif /* MA_NO_THREADING */
2022
2023
2024/*
2025Retrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required.
2026*/
2027MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);
2028
2029/*
2030Retrieves the version of miniaudio as a string which can be useful for logging purposes.
2031*/
2032MA_API const char* ma_version_string(void);
2033
2034
2035/**************************************************************************************************************************************************************
2036
2037Logging
2038
2039**************************************************************************************************************************************************************/
2040#include <stdarg.h> /* For va_list. */
2041
2042#if defined(__has_attribute)
2043 #if __has_attribute(format)
2044 #define MA_ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va)))
2045 #endif
2046#endif
2047#ifndef MA_ATTRIBUTE_FORMAT
2048#define MA_ATTRIBUTE_FORMAT(fmt,va)
2049#endif
2050
2051#ifndef MA_MAX_LOG_CALLBACKS
2052#define MA_MAX_LOG_CALLBACKS 4
2053#endif
2054
2055typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage);
2056
2057typedef struct
2058{
2059 ma_log_callback_proc onLog;
2060 void* pUserData;
2061} ma_log_callback;
2062
2063MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData);
2064
2065
2066typedef struct
2067{
2068 ma_log_callback callbacks[MA_MAX_LOG_CALLBACKS];
2069 ma_uint32 callbackCount;
2070 ma_allocation_callbacks allocationCallbacks; /* Need to store these persistently because ma_log_postv() might need to allocate a buffer on the heap. */
2071#ifndef MA_NO_THREADING
2072 ma_mutex lock; /* For thread safety just to make it easier and safer for the logging implementation. */
2073#endif
2074} ma_log;
2075
2076MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog);
2077MA_API void ma_log_uninit(ma_log* pLog);
2078MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback);
2079MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback);
2080MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage);
2081MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args);
2082MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) MA_ATTRIBUTE_FORMAT(3, 4);
2083
2084
2085/**************************************************************************************************************************************************************
2086
2087Biquad Filtering
2088
2089**************************************************************************************************************************************************************/
2090typedef union
2091{
2092 float f32;
2093 ma_int32 s32;
2094} ma_biquad_coefficient;
2095
2096typedef struct
2097{
2098 ma_format format;
2099 ma_uint32 channels;
2100 double b0;
2101 double b1;
2102 double b2;
2103 double a0;
2104 double a1;
2105 double a2;
2106} ma_biquad_config;
2107
2108MA_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);
2109
2110typedef struct
2111{
2112 ma_format format;
2113 ma_uint32 channels;
2114 ma_biquad_coefficient b0;
2115 ma_biquad_coefficient b1;
2116 ma_biquad_coefficient b2;
2117 ma_biquad_coefficient a1;
2118 ma_biquad_coefficient a2;
2119 ma_biquad_coefficient r1[MA_MAX_CHANNELS];
2120 ma_biquad_coefficient r2[MA_MAX_CHANNELS];
2121} ma_biquad;
2122
2123MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ);
2124MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ);
2125MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2126MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ);
2127
2128
2129/**************************************************************************************************************************************************************
2130
2131Low-Pass Filtering
2132
2133**************************************************************************************************************************************************************/
2134typedef struct
2135{
2136 ma_format format;
2137 ma_uint32 channels;
2138 ma_uint32 sampleRate;
2139 double cutoffFrequency;
2140 double q;
2141} ma_lpf1_config, ma_lpf2_config;
2142
2143MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency);
2144MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);
2145
2146typedef struct
2147{
2148 ma_format format;
2149 ma_uint32 channels;
2150 ma_biquad_coefficient a;
2151 ma_biquad_coefficient r1[MA_MAX_CHANNELS];
2152} ma_lpf1;
2153
2154MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, ma_lpf1* pLPF);
2155MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF);
2156MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2157MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF);
2158
2159typedef struct
2160{
2161 ma_biquad bq; /* The second order low-pass filter is implemented as a biquad filter. */
2162} ma_lpf2;
2163
2164MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, ma_lpf2* pLPF);
2165MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF);
2166MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2167MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF);
2168
2169
2170typedef struct
2171{
2172 ma_format format;
2173 ma_uint32 channels;
2174 ma_uint32 sampleRate;
2175 double cutoffFrequency;
2176 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
2177} ma_lpf_config;
2178
2179MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
2180
2181typedef struct
2182{
2183 ma_format format;
2184 ma_uint32 channels;
2185 ma_uint32 sampleRate;
2186 ma_uint32 lpf1Count;
2187 ma_uint32 lpf2Count;
2188 ma_lpf1 lpf1[1];
2189 ma_lpf2 lpf2[MA_MAX_FILTER_ORDER/2];
2190} ma_lpf;
2191
2192MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF);
2193MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF);
2194MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2195MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF);
2196
2197
2198/**************************************************************************************************************************************************************
2199
2200High-Pass Filtering
2201
2202**************************************************************************************************************************************************************/
2203typedef struct
2204{
2205 ma_format format;
2206 ma_uint32 channels;
2207 ma_uint32 sampleRate;
2208 double cutoffFrequency;
2209 double q;
2210} ma_hpf1_config, ma_hpf2_config;
2211
2212MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency);
2213MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);
2214
2215typedef struct
2216{
2217 ma_format format;
2218 ma_uint32 channels;
2219 ma_biquad_coefficient a;
2220 ma_biquad_coefficient r1[MA_MAX_CHANNELS];
2221} ma_hpf1;
2222
2223MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, ma_hpf1* pHPF);
2224MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF);
2225MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2226MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF);
2227
2228typedef struct
2229{
2230 ma_biquad bq; /* The second order high-pass filter is implemented as a biquad filter. */
2231} ma_hpf2;
2232
2233MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, ma_hpf2* pHPF);
2234MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF);
2235MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2236MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF);
2237
2238
2239typedef struct
2240{
2241 ma_format format;
2242 ma_uint32 channels;
2243 ma_uint32 sampleRate;
2244 double cutoffFrequency;
2245 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
2246} ma_hpf_config;
2247
2248MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
2249
2250typedef struct
2251{
2252 ma_format format;
2253 ma_uint32 channels;
2254 ma_uint32 sampleRate;
2255 ma_uint32 hpf1Count;
2256 ma_uint32 hpf2Count;
2257 ma_hpf1 hpf1[1];
2258 ma_hpf2 hpf2[MA_MAX_FILTER_ORDER/2];
2259} ma_hpf;
2260
2261MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, ma_hpf* pHPF);
2262MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF);
2263MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2264MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF);
2265
2266
2267/**************************************************************************************************************************************************************
2268
2269Band-Pass Filtering
2270
2271**************************************************************************************************************************************************************/
2272typedef struct
2273{
2274 ma_format format;
2275 ma_uint32 channels;
2276 ma_uint32 sampleRate;
2277 double cutoffFrequency;
2278 double q;
2279} ma_bpf2_config;
2280
2281MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);
2282
2283typedef struct
2284{
2285 ma_biquad bq; /* The second order band-pass filter is implemented as a biquad filter. */
2286} ma_bpf2;
2287
2288MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, ma_bpf2* pBPF);
2289MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF);
2290MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2291MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF);
2292
2293
2294typedef struct
2295{
2296 ma_format format;
2297 ma_uint32 channels;
2298 ma_uint32 sampleRate;
2299 double cutoffFrequency;
2300 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
2301} ma_bpf_config;
2302
2303MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
2304
2305typedef struct
2306{
2307 ma_format format;
2308 ma_uint32 channels;
2309 ma_uint32 bpf2Count;
2310 ma_bpf2 bpf2[MA_MAX_FILTER_ORDER/2];
2311} ma_bpf;
2312
2313MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, ma_bpf* pBPF);
2314MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF);
2315MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2316MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF);
2317
2318
2319/**************************************************************************************************************************************************************
2320
2321Notching Filter
2322
2323**************************************************************************************************************************************************************/
2324typedef struct
2325{
2326 ma_format format;
2327 ma_uint32 channels;
2328 ma_uint32 sampleRate;
2329 double q;
2330 double frequency;
2331} ma_notch2_config, ma_notch_config;
2332
2333MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency);
2334
2335typedef struct
2336{
2337 ma_biquad bq;
2338} ma_notch2;
2339
2340MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, ma_notch2* pFilter);
2341MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter);
2342MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2343MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter);
2344
2345
2346/**************************************************************************************************************************************************************
2347
2348Peaking EQ Filter
2349
2350**************************************************************************************************************************************************************/
2351typedef struct
2352{
2353 ma_format format;
2354 ma_uint32 channels;
2355 ma_uint32 sampleRate;
2356 double gainDB;
2357 double q;
2358 double frequency;
2359} ma_peak2_config, ma_peak_config;
2360
2361MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
2362
2363typedef struct
2364{
2365 ma_biquad bq;
2366} ma_peak2;
2367
2368MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, ma_peak2* pFilter);
2369MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter);
2370MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2371MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter);
2372
2373
2374/**************************************************************************************************************************************************************
2375
2376Low Shelf Filter
2377
2378**************************************************************************************************************************************************************/
2379typedef struct
2380{
2381 ma_format format;
2382 ma_uint32 channels;
2383 ma_uint32 sampleRate;
2384 double gainDB;
2385 double shelfSlope;
2386 double frequency;
2387} ma_loshelf2_config, ma_loshelf_config;
2388
2389MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency);
2390
2391typedef struct
2392{
2393 ma_biquad bq;
2394} ma_loshelf2;
2395
2396MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter);
2397MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter);
2398MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2399MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter);
2400
2401
2402/**************************************************************************************************************************************************************
2403
2404High Shelf Filter
2405
2406**************************************************************************************************************************************************************/
2407typedef struct
2408{
2409 ma_format format;
2410 ma_uint32 channels;
2411 ma_uint32 sampleRate;
2412 double gainDB;
2413 double shelfSlope;
2414 double frequency;
2415} ma_hishelf2_config, ma_hishelf_config;
2416
2417MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency);
2418
2419typedef struct
2420{
2421 ma_biquad bq;
2422} ma_hishelf2;
2423
2424MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter);
2425MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter);
2426MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2427MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter);
2428
2429
2430
2431/************************************************************************************************************************************************************
2432*************************************************************************************************************************************************************
2433
2434DATA CONVERSION
2435===============
2436
2437This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc.
2438
2439*************************************************************************************************************************************************************
2440************************************************************************************************************************************************************/
2441
2442/**************************************************************************************************************************************************************
2443
2444Resampling
2445
2446**************************************************************************************************************************************************************/
2447typedef struct
2448{
2449 ma_format format;
2450 ma_uint32 channels;
2451 ma_uint32 sampleRateIn;
2452 ma_uint32 sampleRateOut;
2453 ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */
2454 double lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */
2455} ma_linear_resampler_config;
2456
2457MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
2458
2459typedef struct
2460{
2461 ma_linear_resampler_config config;
2462 ma_uint32 inAdvanceInt;
2463 ma_uint32 inAdvanceFrac;
2464 ma_uint32 inTimeInt;
2465 ma_uint32 inTimeFrac;
2466 union
2467 {
2468 float f32[MA_MAX_CHANNELS];
2469 ma_int16 s16[MA_MAX_CHANNELS];
2470 } x0; /* The previous input frame. */
2471 union
2472 {
2473 float f32[MA_MAX_CHANNELS];
2474 ma_int16 s16[MA_MAX_CHANNELS];
2475 } x1; /* The next input frame. */
2476 ma_lpf lpf;
2477} ma_linear_resampler;
2478
2479MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, ma_linear_resampler* pResampler);
2480MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler);
2481MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
2482MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
2483MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut);
2484MA_API ma_uint64 ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount);
2485MA_API ma_uint64 ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount);
2486MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler);
2487MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler);
2488
2489typedef enum
2490{
2491 ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */
2492 ma_resample_algorithm_speex
2493} ma_resample_algorithm;
2494
2495typedef struct
2496{
2497 ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */
2498 ma_uint32 channels;
2499 ma_uint32 sampleRateIn;
2500 ma_uint32 sampleRateOut;
2501 ma_resample_algorithm algorithm;
2502 struct
2503 {
2504 ma_uint32 lpfOrder;
2505 double lpfNyquistFactor;
2506 } linear;
2507 struct
2508 {
2509 int quality; /* 0 to 10. Defaults to 3. */
2510 } speex;
2511} ma_resampler_config;
2512
2513MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm);
2514
2515typedef struct
2516{
2517 ma_resampler_config config;
2518 union
2519 {
2520 ma_linear_resampler linear;
2521 struct
2522 {
2523 void* pSpeexResamplerState; /* SpeexResamplerState* */
2524 } speex;
2525 } state;
2526} ma_resampler;
2527
2528/*
2529Initializes a new resampler object from a config.
2530*/
2531MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pResampler);
2532
2533/*
2534Uninitializes a resampler.
2535*/
2536MA_API void ma_resampler_uninit(ma_resampler* pResampler);
2537
2538/*
2539Converts the given input data.
2540
2541Both the input and output frames must be in the format specified in the config when the resampler was initilized.
2542
2543On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that
2544were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use
2545ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames.
2546
2547On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole
2548input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames
2549you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead.
2550
2551If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of
2552output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input
2553frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be
2554processed. In this case, any internal filter state will be updated as if zeroes were passed in.
2555
2556It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL.
2557
2558It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL.
2559*/
2560MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
2561
2562
2563/*
2564Sets the input and output sample sample rate.
2565*/
2566MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
2567
2568/*
2569Sets the input and output sample rate as a ratio.
2570
2571The ration is in/out.
2572*/
2573MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio);
2574
2575
2576/*
2577Calculates the number of whole input frames that would need to be read from the client in order to output the specified
2578number of output frames.
2579
2580The returned value does not include cached input frames. It only returns the number of extra frames that would need to be
2581read from the input buffer in order to output the specified number of output frames.
2582*/
2583MA_API ma_uint64 ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount);
2584
2585/*
2586Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of
2587input frames.
2588*/
2589MA_API ma_uint64 ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount);
2590
2591
2592/*
2593Retrieves the latency introduced by the resampler in input frames.
2594*/
2595MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler);
2596
2597/*
2598Retrieves the latency introduced by the resampler in output frames.
2599*/
2600MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler);
2601
2602
2603
2604/**************************************************************************************************************************************************************
2605
2606Channel Conversion
2607
2608**************************************************************************************************************************************************************/
2609typedef struct
2610{
2611 ma_format format;
2612 ma_uint32 channelsIn;
2613 ma_uint32 channelsOut;
2614 ma_channel channelMapIn[MA_MAX_CHANNELS];
2615 ma_channel channelMapOut[MA_MAX_CHANNELS];
2616 ma_channel_mix_mode mixingMode;
2617 float weights[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
2618} ma_channel_converter_config;
2619
2620MA_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);
2621
2622typedef struct
2623{
2624 ma_format format;
2625 ma_uint32 channelsIn;
2626 ma_uint32 channelsOut;
2627 ma_channel channelMapIn[MA_MAX_CHANNELS];
2628 ma_channel channelMapOut[MA_MAX_CHANNELS];
2629 ma_channel_mix_mode mixingMode;
2630 union
2631 {
2632 float f32[MA_MAX_CHANNELS][MA_MAX_CHANNELS];
2633 ma_int32 s16[MA_MAX_CHANNELS][MA_MAX_CHANNELS];
2634 } weights;
2635 ma_bool8 isPassthrough;
2636 ma_bool8 isSimpleShuffle;
2637 ma_bool8 isSimpleMonoExpansion;
2638 ma_bool8 isStereoToMono;
2639 ma_uint8 shuffleTable[MA_MAX_CHANNELS];
2640} ma_channel_converter;
2641
2642MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, ma_channel_converter* pConverter);
2643MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter);
2644MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
2645
2646
2647/**************************************************************************************************************************************************************
2648
2649Data Conversion
2650
2651**************************************************************************************************************************************************************/
2652typedef struct
2653{
2654 ma_format formatIn;
2655 ma_format formatOut;
2656 ma_uint32 channelsIn;
2657 ma_uint32 channelsOut;
2658 ma_uint32 sampleRateIn;
2659 ma_uint32 sampleRateOut;
2660 ma_channel channelMapIn[MA_MAX_CHANNELS];
2661 ma_channel channelMapOut[MA_MAX_CHANNELS];
2662 ma_dither_mode ditherMode;
2663 ma_channel_mix_mode channelMixMode;
2664 float channelWeights[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; /* [in][out]. Only used when channelMixMode is set to ma_channel_mix_mode_custom_weights. */
2665 struct
2666 {
2667 ma_resample_algorithm algorithm;
2668 ma_bool32 allowDynamicSampleRate;
2669 struct
2670 {
2671 ma_uint32 lpfOrder;
2672 double lpfNyquistFactor;
2673 } linear;
2674 struct
2675 {
2676 int quality;
2677 } speex;
2678 } resampling;
2679} ma_data_converter_config;
2680
2681MA_API ma_data_converter_config ma_data_converter_config_init_default(void);
2682MA_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);
2683
2684typedef struct
2685{
2686 ma_data_converter_config config;
2687 ma_channel_converter channelConverter;
2688 ma_resampler resampler;
2689 ma_bool8 hasPreFormatConversion;
2690 ma_bool8 hasPostFormatConversion;
2691 ma_bool8 hasChannelConverter;
2692 ma_bool8 hasResampler;
2693 ma_bool8 isPassthrough;
2694} ma_data_converter;
2695
2696MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, ma_data_converter* pConverter);
2697MA_API void ma_data_converter_uninit(ma_data_converter* pConverter);
2698MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
2699MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
2700MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut);
2701MA_API ma_uint64 ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount);
2702MA_API ma_uint64 ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount);
2703MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter);
2704MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter);
2705
2706
2707/************************************************************************************************************************************************************
2708
2709Format Conversion
2710
2711************************************************************************************************************************************************************/
2712MA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2713MA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2714MA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2715MA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2716MA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2717MA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2718MA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2719MA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2720MA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2721MA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2722MA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2723MA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2724MA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2725MA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2726MA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2727MA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2728MA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2729MA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2730MA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2731MA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
2732MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode);
2733MA_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);
2734
2735/*
2736Deinterleaves an interleaved buffer.
2737*/
2738MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);
2739
2740/*
2741Interleaves a group of deinterleaved buffers.
2742*/
2743MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);
2744
2745
2746/************************************************************************************************************************************************************
2747
2748Channel Maps
2749
2750************************************************************************************************************************************************************/
2751/*
2752This is used in the shuffle table to indicate that the channel index is undefined and should be ignored.
2753*/
2754#define MA_CHANNEL_INDEX_NULL 255
2755
2756/* Retrieves the channel position of the specified channel based on miniaudio's default channel map. */
2757MA_API ma_channel ma_channel_map_get_default_channel(ma_uint32 channelCount, ma_uint32 channelIndex);
2758
2759/*
2760Retrieves the channel position of the specified channel in the given channel map.
2761
2762The pChannelMap parameter can be null, in which case miniaudio's default channel map will be assumed.
2763*/
2764MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex);
2765
2766/*
2767Initializes a blank channel map.
2768
2769When a blank channel map is specified anywhere it indicates that the native channel map should be used.
2770*/
2771MA_API void ma_channel_map_init_blank(ma_uint32 channels, ma_channel* pChannelMap);
2772
2773/*
2774Helper for retrieving a standard channel map.
2775
2776The output channel map buffer must have a capacity of at least `channels`.
2777*/
2778MA_API void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel* pChannelMap);
2779
2780/*
2781Copies a channel map.
2782
2783Both input and output channel map buffers must have a capacity of at at least `channels`.
2784*/
2785MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels);
2786
2787/*
2788Copies a channel map if one is specified, otherwise copies the default channel map.
2789
2790The 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`.
2791*/
2792MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels);
2793
2794
2795/*
2796Determines whether or not a channel map is valid.
2797
2798A blank channel map is valid (all channels set to MA_CHANNEL_NONE). The way a blank channel map is handled is context specific, but
2799is usually treated as a passthrough.
2800
2801Invalid channel maps:
2802 - A channel map with no channels
2803 - A channel map with more than one channel and a mono channel
2804
2805The channel map buffer must have a capacity of at least `channels`.
2806*/
2807MA_API ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel* pChannelMap);
2808
2809/*
2810Helper for comparing two channel maps for equality.
2811
2812This assumes the channel count is the same between the two.
2813
2814Both channels map buffers must have a capacity of at least `channels`.
2815*/
2816MA_API ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel* pChannelMapA, const ma_channel* pChannelMapB);
2817
2818/*
2819Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE).
2820
2821The channel map buffer must have a capacity of at least `channels`.
2822*/
2823MA_API ma_bool32 ma_channel_map_blank(ma_uint32 channels, const ma_channel* pChannelMap);
2824
2825/*
2826Helper for determining whether or not a channel is present in the given channel map.
2827
2828The channel map buffer must have a capacity of at least `channels`.
2829*/
2830MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition);
2831
2832
2833/************************************************************************************************************************************************************
2834
2835Conversion Helpers
2836
2837************************************************************************************************************************************************************/
2838
2839/*
2840High-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
2841determine the required size of the output buffer. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is
2842ignored.
2843
2844A return value of 0 indicates an error.
2845
2846This function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_data_converter APIs instead.
2847*/
2848MA_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);
2849MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig);
2850
2851
2852/************************************************************************************************************************************************************
2853
2854Ring Buffer
2855
2856************************************************************************************************************************************************************/
2857typedef struct
2858{
2859 void* pBuffer;
2860 ma_uint32 subbufferSizeInBytes;
2861 ma_uint32 subbufferCount;
2862 ma_uint32 subbufferStrideInBytes;
2863 MA_ATOMIC ma_uint32 encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */
2864 MA_ATOMIC ma_uint32 encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */
2865 ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */
2866 ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */
2867 ma_allocation_callbacks allocationCallbacks;
2868} ma_rb;
2869
2870MA_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);
2871MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB);
2872MA_API void ma_rb_uninit(ma_rb* pRB);
2873MA_API void ma_rb_reset(ma_rb* pRB);
2874MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
2875MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut);
2876MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
2877MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut);
2878MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes);
2879MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes);
2880MA_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. */
2881MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB);
2882MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB);
2883MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB);
2884MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB);
2885MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex);
2886MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer);
2887
2888
2889typedef struct
2890{
2891 ma_rb rb;
2892 ma_format format;
2893 ma_uint32 channels;
2894} ma_pcm_rb;
2895
2896MA_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);
2897MA_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);
2898MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB);
2899MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB);
2900MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
2901MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut);
2902MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
2903MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut);
2904MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames);
2905MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames);
2906MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */
2907MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB);
2908MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB);
2909MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB);
2910MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB);
2911MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex);
2912MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer);
2913
2914
2915/*
2916The idea of the duplex ring buffer is to act as the intermediary buffer when running two asynchronous devices in a duplex set up. The
2917capture device writes to it, and then a playback device reads from it.
2918
2919At the moment this is just a simple naive implementation, but in the future I want to implement some dynamic resampling to seamlessly
2920handle desyncs. Note that the API is work in progress and may change at any time in any version.
2921
2922The 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
2923in frames. The internal sample rate of the capture device is also needed in order to calculate the size.
2924*/
2925typedef struct
2926{
2927 ma_pcm_rb rb;
2928} ma_duplex_rb;
2929
2930MA_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);
2931MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB);
2932
2933
2934/************************************************************************************************************************************************************
2935
2936Miscellaneous Helpers
2937
2938************************************************************************************************************************************************************/
2939/*
2940Retrieves a human readable description of the given result code.
2941*/
2942MA_API const char* ma_result_description(ma_result result);
2943
2944/*
2945malloc(). Calls MA_MALLOC().
2946*/
2947MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
2948
2949/*
2950realloc(). Calls MA_REALLOC().
2951*/
2952MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
2953
2954/*
2955free(). Calls MA_FREE().
2956*/
2957MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
2958
2959/*
2960Performs an aligned malloc, with the assumption that the alignment is a power of 2.
2961*/
2962MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks);
2963
2964/*
2965Free's an aligned malloc'd buffer.
2966*/
2967MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
2968
2969/*
2970Retrieves a friendly name for a format.
2971*/
2972MA_API const char* ma_get_format_name(ma_format format);
2973
2974/*
2975Blends two frames in floating point format.
2976*/
2977MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels);
2978
2979/*
2980Retrieves the size of a sample in bytes for the given format.
2981
2982This API is efficient and is implemented using a lookup table.
2983
2984Thread Safety: SAFE
2985 This API is pure.
2986*/
2987MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format);
2988static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; }
2989
2990/*
2991Converts a log level to a string.
2992*/
2993MA_API const char* ma_log_level_to_string(ma_uint32 logLevel);
2994
2995
2996
2997/************************************************************************************************************************************************************
2998*************************************************************************************************************************************************************
2999
3000DEVICE I/O
3001==========
3002
3003This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc.
3004
3005*************************************************************************************************************************************************************
3006************************************************************************************************************************************************************/
3007#ifndef MA_NO_DEVICE_IO
3008/* Some backends are only supported on certain platforms. */
3009#if defined(MA_WIN32)
3010 #define MA_SUPPORT_WASAPI
3011 #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */
3012 #define MA_SUPPORT_DSOUND
3013 #define MA_SUPPORT_WINMM
3014 #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */
3015 #endif
3016#endif
3017#if defined(MA_UNIX)
3018 #if defined(MA_LINUX)
3019 #if !defined(MA_ANDROID) /* ALSA is not supported on Android. */
3020 #define MA_SUPPORT_ALSA
3021 #endif
3022 #endif
3023 #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN)
3024 #define MA_SUPPORT_PULSEAUDIO
3025 #define MA_SUPPORT_JACK
3026 #endif
3027 #if defined(MA_ANDROID)
3028 #define MA_SUPPORT_AAUDIO
3029 #define MA_SUPPORT_OPENSL
3030 #endif
3031 #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */
3032 #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */
3033 #endif
3034 #if defined(__NetBSD__) || defined(__OpenBSD__)
3035 #define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */
3036 #endif
3037 #if defined(__FreeBSD__) || defined(__DragonFly__)
3038 #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */
3039 #endif
3040#endif
3041#if defined(MA_APPLE)
3042 #define MA_SUPPORT_COREAUDIO
3043#endif
3044#if defined(MA_EMSCRIPTEN)
3045 #define MA_SUPPORT_WEBAUDIO
3046#endif
3047
3048/* All platforms should support custom backends. */
3049#define MA_SUPPORT_CUSTOM
3050
3051/* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */
3052#if !defined(MA_EMSCRIPTEN)
3053#define MA_SUPPORT_NULL
3054#endif
3055
3056
3057#if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI))
3058 #define MA_HAS_WASAPI
3059#endif
3060#if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND))
3061 #define MA_HAS_DSOUND
3062#endif
3063#if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM))
3064 #define MA_HAS_WINMM
3065#endif
3066#if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA))
3067 #define MA_HAS_ALSA
3068#endif
3069#if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO))
3070 #define MA_HAS_PULSEAUDIO
3071#endif
3072#if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK))
3073 #define MA_HAS_JACK
3074#endif
3075#if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO))
3076 #define MA_HAS_COREAUDIO
3077#endif
3078#if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO))
3079 #define MA_HAS_SNDIO
3080#endif
3081#if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4))
3082 #define MA_HAS_AUDIO4
3083#endif
3084#if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS))
3085 #define MA_HAS_OSS
3086#endif
3087#if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO))
3088 #define MA_HAS_AAUDIO
3089#endif
3090#if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL))
3091 #define MA_HAS_OPENSL
3092#endif
3093#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO))
3094 #define MA_HAS_WEBAUDIO
3095#endif
3096#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM))
3097 #define MA_HAS_CUSTOM
3098#endif
3099#if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL))
3100 #define MA_HAS_NULL
3101#endif
3102
3103#define MA_STATE_UNINITIALIZED 0
3104#define MA_STATE_STOPPED 1 /* The device's default state after initialization. */
3105#define MA_STATE_STARTED 2 /* The device is started and is requesting and/or delivering audio data. */
3106#define MA_STATE_STARTING 3 /* Transitioning from a stopped state to started. */
3107#define MA_STATE_STOPPING 4 /* Transitioning from a started state to stopped. */
3108
3109#ifdef MA_SUPPORT_WASAPI
3110/* We need a IMMNotificationClient object for WASAPI. */
3111typedef struct
3112{
3113 void* lpVtbl;
3114 ma_uint32 counter;
3115 ma_device* pDevice;
3116} ma_IMMNotificationClient;
3117#endif
3118
3119/* Backend enums must be in priority order. */
3120typedef enum
3121{
3122 ma_backend_wasapi,
3123 ma_backend_dsound,
3124 ma_backend_winmm,
3125 ma_backend_coreaudio,
3126 ma_backend_sndio,
3127 ma_backend_audio4,
3128 ma_backend_oss,
3129 ma_backend_pulseaudio,
3130 ma_backend_alsa,
3131 ma_backend_jack,
3132 ma_backend_aaudio,
3133 ma_backend_opensl,
3134 ma_backend_webaudio,
3135 ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */
3136 ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */
3137} ma_backend;
3138
3139#define MA_BACKEND_COUNT (ma_backend_null+1)
3140
3141
3142/*
3143The callback for processing audio data from the device.
3144
3145The 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
3146available. 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
3147callback will be fired with a consistent frame count.
3148
3149
3150Parameters
3151----------
3152pDevice (in)
3153 A pointer to the relevant device.
3154
3155pOutput (out)
3156 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
3157 full-duplex device and null for a capture and loopback device.
3158
3159pInput (in)
3160 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
3161 playback device.
3162
3163frameCount (in)
3164 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
3165 `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must
3166 not assume this will always be the same value each time the callback is fired.
3167
3168
3169Remarks
3170-------
3171You 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
3172callback. The following APIs cannot be called from inside the callback:
3173
3174 ma_device_init()
3175 ma_device_init_ex()
3176 ma_device_uninit()
3177 ma_device_start()
3178 ma_device_stop()
3179
3180The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread.
3181*/
3182typedef void (* ma_device_callback_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
3183
3184/*
3185The callback for when the device has been stopped.
3186
3187This 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
3188such as being unplugged or an internal error occuring.
3189
3190
3191Parameters
3192----------
3193pDevice (in)
3194 A pointer to the device that has just stopped.
3195
3196
3197Remarks
3198-------
3199Do not restart or uninitialize the device from the callback.
3200*/
3201typedef void (* ma_stop_proc)(ma_device* pDevice);
3202
3203/*
3204The callback for handling log messages.
3205
3206
3207Parameters
3208----------
3209pContext (in)
3210 A pointer to the context the log message originated from.
3211
3212pDevice (in)
3213 A pointer to the device the log message originate from, if any. This can be null, in which case the message came from the context.
3214
3215logLevel (in)
3216 The log level. This can be one of the following:
3217
3218 +----------------------+
3219 | Log Level |
3220 +----------------------+
3221 | MA_LOG_LEVEL_DEBUG |
3222 | MA_LOG_LEVEL_INFO |
3223 | MA_LOG_LEVEL_WARNING |
3224 | MA_LOG_LEVEL_ERROR |
3225 +----------------------+
3226
3227message (in)
3228 The log message.
3229
3230
3231Remarks
3232-------
3233Do not modify the state of the device from inside the callback.
3234*/
3235typedef void (* ma_log_proc)(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message);
3236
3237typedef enum
3238{
3239 ma_device_type_playback = 1,
3240 ma_device_type_capture = 2,
3241 ma_device_type_duplex = ma_device_type_playback | ma_device_type_capture, /* 3 */
3242 ma_device_type_loopback = 4
3243} ma_device_type;
3244
3245typedef enum
3246{
3247 ma_share_mode_shared = 0,
3248 ma_share_mode_exclusive
3249} ma_share_mode;
3250
3251/* iOS/tvOS/watchOS session categories. */
3252typedef enum
3253{
3254 ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord with AVAudioSessionCategoryOptionDefaultToSpeaker. */
3255 ma_ios_session_category_none, /* Leave the session category unchanged. */
3256 ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */
3257 ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */
3258 ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */
3259 ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */
3260 ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */
3261 ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */
3262} ma_ios_session_category;
3263
3264/* iOS/tvOS/watchOS session category options */
3265typedef enum
3266{
3267 ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */
3268 ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */
3269 ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */
3270 ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */
3271 ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */
3272 ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */
3273 ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */
3274} ma_ios_session_category_option;
3275
3276/* OpenSL stream types. */
3277typedef enum
3278{
3279 ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */
3280 ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */
3281 ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */
3282 ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */
3283 ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */
3284 ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */
3285 ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */
3286} ma_opensl_stream_type;
3287
3288/* OpenSL recording presets. */
3289typedef enum
3290{
3291 ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */
3292 ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */
3293 ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */
3294 ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */
3295 ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */
3296 ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */
3297} ma_opensl_recording_preset;
3298
3299/* AAudio usage types. */
3300typedef enum
3301{
3302 ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */
3303 ma_aaudio_usage_announcement, /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */
3304 ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */
3305 ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */
3306 ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */
3307 ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */
3308 ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */
3309 ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */
3310 ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */
3311 ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */
3312 ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */
3313 ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */
3314 ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */
3315 ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */
3316 ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */
3317 ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */
3318 ma_aaudio_usage_voice_communication_signalling /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */
3319} ma_aaudio_usage;
3320
3321/* AAudio content types. */
3322typedef enum
3323{
3324 ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */
3325 ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */
3326 ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */
3327 ma_aaudio_content_type_sonification, /* AAUDIO_CONTENT_TYPE_SONIFICATION */
3328 ma_aaudio_content_type_speech /* AAUDIO_CONTENT_TYPE_SPEECH */
3329} ma_aaudio_content_type;
3330
3331/* AAudio input presets. */
3332typedef enum
3333{
3334 ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */
3335 ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */
3336 ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */
3337 ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */
3338 ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */
3339 ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */
3340 ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */
3341} ma_aaudio_input_preset;
3342
3343
3344typedef union
3345{
3346 ma_int64 counter;
3347 double counterD;
3348} ma_timer;
3349
3350typedef union
3351{
3352 wchar_t wasapi[64]; /* WASAPI uses a wchar_t string for identification. */
3353 ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */
3354 /*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. */
3355 char alsa[256]; /* ALSA uses a name string for identification. */
3356 char pulse[256]; /* PulseAudio uses a name string for identification. */
3357 int jack; /* JACK always uses default devices. */
3358 char coreaudio[256]; /* Core Audio uses a string for identification. */
3359 char sndio[256]; /* "snd/0", etc. */
3360 char audio4[256]; /* "/dev/audio", etc. */
3361 char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */
3362 ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */
3363 ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */
3364 char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */
3365 union
3366 {
3367 int i;
3368 char s[256];
3369 void* p;
3370 } custom; /* The custom backend could be anything. Give them a few options. */
3371 int nullbackend; /* The null backend uses an integer for device IDs. */
3372} ma_device_id;
3373
3374
3375typedef struct ma_context_config ma_context_config;
3376typedef struct ma_device_config ma_device_config;
3377typedef struct ma_backend_callbacks ma_backend_callbacks;
3378
3379#define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */
3380
3381typedef struct
3382{
3383 /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */
3384 ma_device_id id;
3385 char name[256];
3386 ma_bool32 isDefault;
3387
3388 /*
3389 Detailed info. As much of this is filled as possible with ma_context_get_device_info(). Note that you are allowed to initialize
3390 a device with settings outside of this range, but it just means the data will be converted using miniaudio's data conversion
3391 pipeline before sending the data to/from the device. Most programs will need to not worry about these values, but it's provided
3392 here mainly for informational purposes or in the rare case that someone might find it useful.
3393
3394 These will be set to 0 when returned by ma_context_enumerate_devices() or ma_context_get_devices().
3395 */
3396 ma_uint32 formatCount;
3397 ma_format formats[ma_format_count];
3398 ma_uint32 minChannels;
3399 ma_uint32 maxChannels;
3400 ma_uint32 minSampleRate;
3401 ma_uint32 maxSampleRate;
3402
3403
3404 /* Experimental. Don't use these right now. */
3405 ma_uint32 nativeDataFormatCount;
3406 struct
3407 {
3408 ma_format format; /* Sample format. If set to ma_format_unknown, all sample formats are supported. */
3409 ma_uint32 channels; /* If set to 0, all channels are supported. */
3410 ma_uint32 sampleRate; /* If set to 0, all sample rates are supported. */
3411 ma_uint32 flags; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */
3412 } 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. */
3413} ma_device_info;
3414
3415struct ma_device_config
3416{
3417 ma_device_type deviceType;
3418 ma_uint32 sampleRate;
3419 ma_uint32 periodSizeInFrames;
3420 ma_uint32 periodSizeInMilliseconds;
3421 ma_uint32 periods;
3422 ma_performance_profile performanceProfile;
3423 ma_bool8 noPreZeroedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to zero. */
3424 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. */
3425 ma_device_callback_proc dataCallback;
3426 ma_stop_proc stopCallback;
3427 void* pUserData;
3428 struct
3429 {
3430 ma_resample_algorithm algorithm;
3431 struct
3432 {
3433 ma_uint32 lpfOrder;
3434 } linear;
3435 struct
3436 {
3437 int quality;
3438 } speex;
3439 } resampling;
3440 struct
3441 {
3442 const ma_device_id* pDeviceID;
3443 ma_format format;
3444 ma_uint32 channels;
3445 ma_channel channelMap[MA_MAX_CHANNELS];
3446 ma_channel_mix_mode channelMixMode;
3447 ma_share_mode shareMode;
3448 } playback;
3449 struct
3450 {
3451 const ma_device_id* pDeviceID;
3452 ma_format format;
3453 ma_uint32 channels;
3454 ma_channel channelMap[MA_MAX_CHANNELS];
3455 ma_channel_mix_mode channelMixMode;
3456 ma_share_mode shareMode;
3457 } capture;
3458
3459 struct
3460 {
3461 ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
3462 ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
3463 ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */
3464 ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */
3465 } wasapi;
3466 struct
3467 {
3468 ma_bool32 noMMap; /* Disables MMap mode. */
3469 ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */
3470 ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */
3471 ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */
3472 } alsa;
3473 struct
3474 {
3475 const char* pStreamNamePlayback;
3476 const char* pStreamNameCapture;
3477 } pulse;
3478 struct
3479 {
3480 ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */
3481 } coreaudio;
3482 struct
3483 {
3484 ma_opensl_stream_type streamType;
3485 ma_opensl_recording_preset recordingPreset;
3486 } opensl;
3487 struct
3488 {
3489 ma_aaudio_usage usage;
3490 ma_aaudio_content_type contentType;
3491 ma_aaudio_input_preset inputPreset;
3492 } aaudio;
3493};
3494
3495
3496/*
3497The callback for handling device enumeration. This is fired from `ma_context_enumerated_devices()`.
3498
3499
3500Parameters
3501----------
3502pContext (in)
3503 A pointer to the context performing the enumeration.
3504
3505deviceType (in)
3506 The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`.
3507
3508pInfo (in)
3509 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,
3510 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
3511 is too inefficient.
3512
3513pUserData (in)
3514 The user data pointer passed into `ma_context_enumerate_devices()`.
3515*/
3516typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData);
3517
3518
3519/*
3520Describes some basic details about a playback or capture device.
3521*/
3522typedef struct
3523{
3524 const ma_device_id* pDeviceID;
3525 ma_share_mode shareMode;
3526 ma_format format;
3527 ma_uint32 channels;
3528 ma_uint32 sampleRate;
3529 ma_channel channelMap[MA_MAX_CHANNELS];
3530 ma_uint32 periodSizeInFrames;
3531 ma_uint32 periodSizeInMilliseconds;
3532 ma_uint32 periodCount;
3533} ma_device_descriptor;
3534
3535/*
3536These are the callbacks required to be implemented for a backend. These callbacks are grouped into two parts: context and device. There is one context
3537to many devices. A device is created from a context.
3538
3539The general flow goes like this:
3540
3541 1) A context is created with `onContextInit()`
3542 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required.
3543 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required.
3544 2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was
3545 selected from device enumeration via `onContextEnumerateDevices()`.
3546 3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()`
3547 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
3548 to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by
3549 miniaudio internally.
3550
3551Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the
3552callbacks defined in this structure.
3553
3554Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which
3555physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the
3556given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration
3557needs to stop and the `onContextEnumerateDevices()` function return with a success code.
3558
3559Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID,
3560and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the
3561case when the device ID is NULL, in which case information about the default device needs to be retrieved.
3562
3563Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created.
3564This is a little bit more complicated than initialization of the context due to it's more complicated configuration. When initializing a
3565device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input,
3566the 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
3567the requested format. The conversion between the format requested by the application and the device's native format will be handled
3568internally by miniaudio.
3569
3570On 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
3571supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for
3572sample rate. For the channel map, the default should be used when `ma_channel_map_blank()` returns true (all channels set to
3573`MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should
3574inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period
3575size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the
3576sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_data_format`
3577object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set).
3578
3579Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses
3580asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented.
3581
3582The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit
3583easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and
3584`onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the
3585backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback.
3586This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback.
3587
3588If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback
3589which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional.
3590
3591The audio thread should run data delivery logic in a loop while `ma_device_get_state() == MA_STATE_STARTED` and no errors have been
3592encounted. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback.
3593
3594The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this
3595callback. When the device is stopped, the `ma_device_get_state() == MA_STATE_STARTED` condition will fail and the loop will be terminated
3596which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback,
3597look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to
3598wake up the audio thread.
3599*/
3600struct ma_backend_callbacks
3601{
3602 ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks);
3603 ma_result (* onContextUninit)(ma_context* pContext);
3604 ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData);
3605 ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo);
3606 ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture);
3607 ma_result (* onDeviceUninit)(ma_device* pDevice);
3608 ma_result (* onDeviceStart)(ma_device* pDevice);
3609 ma_result (* onDeviceStop)(ma_device* pDevice);
3610 ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead);
3611 ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten);
3612 ma_result (* onDeviceDataLoop)(ma_device* pDevice);
3613 ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice);
3614};
3615
3616struct ma_context_config
3617{
3618 ma_log_proc logCallback; /* Legacy logging callback. Will be removed in version 0.11. */
3619 ma_log* pLog;
3620 ma_thread_priority threadPriority;
3621 size_t threadStackSize;
3622 void* pUserData;
3623 ma_allocation_callbacks allocationCallbacks;
3624 struct
3625 {
3626 ma_bool32 useVerboseDeviceEnumeration;
3627 } alsa;
3628 struct
3629 {
3630 const char* pApplicationName;
3631 const char* pServerName;
3632 ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */
3633 } pulse;
3634 struct
3635 {
3636 ma_ios_session_category sessionCategory;
3637 ma_uint32 sessionCategoryOptions;
3638 ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */
3639 ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */
3640 } coreaudio;
3641 struct
3642 {
3643 const char* pClientName;
3644 ma_bool32 tryStartServer;
3645 } jack;
3646 ma_backend_callbacks custom;
3647};
3648
3649/* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */
3650typedef struct
3651{
3652 int code;
3653 ma_event* pEvent; /* This will be signalled when the event is complete. */
3654 union
3655 {
3656 struct
3657 {
3658 int _unused;
3659 } quit;
3660 struct
3661 {
3662 ma_device_type deviceType;
3663 void* pAudioClient;
3664 void** ppAudioClientService;
3665 ma_result* pResult; /* The result from creating the audio client service. */
3666 } createAudioClient;
3667 struct
3668 {
3669 ma_device* pDevice;
3670 ma_device_type deviceType;
3671 } releaseAudioClient;
3672 } data;
3673} ma_context_command__wasapi;
3674
3675struct ma_context
3676{
3677 ma_backend_callbacks callbacks;
3678 ma_backend backend; /* DirectSound, ALSA, etc. */
3679 ma_log* pLog;
3680 ma_log log; /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */
3681 ma_log_proc logCallback; /* Legacy callback. Will be removed in version 0.11. */
3682 ma_thread_priority threadPriority;
3683 size_t threadStackSize;
3684 void* pUserData;
3685 ma_allocation_callbacks allocationCallbacks;
3686 ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */
3687 ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */
3688 ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */
3689 ma_uint32 playbackDeviceInfoCount;
3690 ma_uint32 captureDeviceInfoCount;
3691 ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */
3692
3693 union
3694 {
3695#ifdef MA_SUPPORT_WASAPI
3696 struct
3697 {
3698 ma_thread commandThread;
3699 ma_mutex commandLock;
3700 ma_semaphore commandSem;
3701 ma_uint32 commandIndex;
3702 ma_uint32 commandCount;
3703 ma_context_command__wasapi commands[4];
3704 } wasapi;
3705#endif
3706#ifdef MA_SUPPORT_DSOUND
3707 struct
3708 {
3709 ma_handle hDSoundDLL;
3710 ma_proc DirectSoundCreate;
3711 ma_proc DirectSoundEnumerateA;
3712 ma_proc DirectSoundCaptureCreate;
3713 ma_proc DirectSoundCaptureEnumerateA;
3714 } dsound;
3715#endif
3716#ifdef MA_SUPPORT_WINMM
3717 struct
3718 {
3719 ma_handle hWinMM;
3720 ma_proc waveOutGetNumDevs;
3721 ma_proc waveOutGetDevCapsA;
3722 ma_proc waveOutOpen;
3723 ma_proc waveOutClose;
3724 ma_proc waveOutPrepareHeader;
3725 ma_proc waveOutUnprepareHeader;
3726 ma_proc waveOutWrite;
3727 ma_proc waveOutReset;
3728 ma_proc waveInGetNumDevs;
3729 ma_proc waveInGetDevCapsA;
3730 ma_proc waveInOpen;
3731 ma_proc waveInClose;
3732 ma_proc waveInPrepareHeader;
3733 ma_proc waveInUnprepareHeader;
3734 ma_proc waveInAddBuffer;
3735 ma_proc waveInStart;
3736 ma_proc waveInReset;
3737 } winmm;
3738#endif
3739#ifdef MA_SUPPORT_ALSA
3740 struct
3741 {
3742 ma_handle asoundSO;
3743 ma_proc snd_pcm_open;
3744 ma_proc snd_pcm_close;
3745 ma_proc snd_pcm_hw_params_sizeof;
3746 ma_proc snd_pcm_hw_params_any;
3747 ma_proc snd_pcm_hw_params_set_format;
3748 ma_proc snd_pcm_hw_params_set_format_first;
3749 ma_proc snd_pcm_hw_params_get_format_mask;
3750 ma_proc snd_pcm_hw_params_set_channels;
3751 ma_proc snd_pcm_hw_params_set_channels_near;
3752 ma_proc snd_pcm_hw_params_set_channels_minmax;
3753 ma_proc snd_pcm_hw_params_set_rate_resample;
3754 ma_proc snd_pcm_hw_params_set_rate;
3755 ma_proc snd_pcm_hw_params_set_rate_near;
3756 ma_proc snd_pcm_hw_params_set_buffer_size_near;
3757 ma_proc snd_pcm_hw_params_set_periods_near;
3758 ma_proc snd_pcm_hw_params_set_access;
3759 ma_proc snd_pcm_hw_params_get_format;
3760 ma_proc snd_pcm_hw_params_get_channels;
3761 ma_proc snd_pcm_hw_params_get_channels_min;
3762 ma_proc snd_pcm_hw_params_get_channels_max;
3763 ma_proc snd_pcm_hw_params_get_rate;
3764 ma_proc snd_pcm_hw_params_get_rate_min;
3765 ma_proc snd_pcm_hw_params_get_rate_max;
3766 ma_proc snd_pcm_hw_params_get_buffer_size;
3767 ma_proc snd_pcm_hw_params_get_periods;
3768 ma_proc snd_pcm_hw_params_get_access;
3769 ma_proc snd_pcm_hw_params_test_format;
3770 ma_proc snd_pcm_hw_params_test_channels;
3771 ma_proc snd_pcm_hw_params_test_rate;
3772 ma_proc snd_pcm_hw_params;
3773 ma_proc snd_pcm_sw_params_sizeof;
3774 ma_proc snd_pcm_sw_params_current;
3775 ma_proc snd_pcm_sw_params_get_boundary;
3776 ma_proc snd_pcm_sw_params_set_avail_min;
3777 ma_proc snd_pcm_sw_params_set_start_threshold;
3778 ma_proc snd_pcm_sw_params_set_stop_threshold;
3779 ma_proc snd_pcm_sw_params;
3780 ma_proc snd_pcm_format_mask_sizeof;
3781 ma_proc snd_pcm_format_mask_test;
3782 ma_proc snd_pcm_get_chmap;
3783 ma_proc snd_pcm_state;
3784 ma_proc snd_pcm_prepare;
3785 ma_proc snd_pcm_start;
3786 ma_proc snd_pcm_drop;
3787 ma_proc snd_pcm_drain;
3788 ma_proc snd_pcm_reset;
3789 ma_proc snd_device_name_hint;
3790 ma_proc snd_device_name_get_hint;
3791 ma_proc snd_card_get_index;
3792 ma_proc snd_device_name_free_hint;
3793 ma_proc snd_pcm_mmap_begin;
3794 ma_proc snd_pcm_mmap_commit;
3795 ma_proc snd_pcm_recover;
3796 ma_proc snd_pcm_readi;
3797 ma_proc snd_pcm_writei;
3798 ma_proc snd_pcm_avail;
3799 ma_proc snd_pcm_avail_update;
3800 ma_proc snd_pcm_wait;
3801 ma_proc snd_pcm_nonblock;
3802 ma_proc snd_pcm_info;
3803 ma_proc snd_pcm_info_sizeof;
3804 ma_proc snd_pcm_info_get_name;
3805 ma_proc snd_pcm_poll_descriptors;
3806 ma_proc snd_pcm_poll_descriptors_count;
3807 ma_proc snd_pcm_poll_descriptors_revents;
3808 ma_proc snd_config_update_free_global;
3809
3810 ma_mutex internalDeviceEnumLock;
3811 ma_bool32 useVerboseDeviceEnumeration;
3812 } alsa;
3813#endif
3814#ifdef MA_SUPPORT_PULSEAUDIO
3815 struct
3816 {
3817 ma_handle pulseSO;
3818 ma_proc pa_mainloop_new;
3819 ma_proc pa_mainloop_free;
3820 ma_proc pa_mainloop_quit;
3821 ma_proc pa_mainloop_get_api;
3822 ma_proc pa_mainloop_iterate;
3823 ma_proc pa_mainloop_wakeup;
3824 ma_proc pa_threaded_mainloop_new;
3825 ma_proc pa_threaded_mainloop_free;
3826 ma_proc pa_threaded_mainloop_start;
3827 ma_proc pa_threaded_mainloop_stop;
3828 ma_proc pa_threaded_mainloop_lock;
3829 ma_proc pa_threaded_mainloop_unlock;
3830 ma_proc pa_threaded_mainloop_wait;
3831 ma_proc pa_threaded_mainloop_signal;
3832 ma_proc pa_threaded_mainloop_accept;
3833 ma_proc pa_threaded_mainloop_get_retval;
3834 ma_proc pa_threaded_mainloop_get_api;
3835 ma_proc pa_threaded_mainloop_in_thread;
3836 ma_proc pa_threaded_mainloop_set_name;
3837 ma_proc pa_context_new;
3838 ma_proc pa_context_unref;
3839 ma_proc pa_context_connect;
3840 ma_proc pa_context_disconnect;
3841 ma_proc pa_context_set_state_callback;
3842 ma_proc pa_context_get_state;
3843 ma_proc pa_context_get_sink_info_list;
3844 ma_proc pa_context_get_source_info_list;
3845 ma_proc pa_context_get_sink_info_by_name;
3846 ma_proc pa_context_get_source_info_by_name;
3847 ma_proc pa_operation_unref;
3848 ma_proc pa_operation_get_state;
3849 ma_proc pa_channel_map_init_extend;
3850 ma_proc pa_channel_map_valid;
3851 ma_proc pa_channel_map_compatible;
3852 ma_proc pa_stream_new;
3853 ma_proc pa_stream_unref;
3854 ma_proc pa_stream_connect_playback;
3855 ma_proc pa_stream_connect_record;
3856 ma_proc pa_stream_disconnect;
3857 ma_proc pa_stream_get_state;
3858 ma_proc pa_stream_get_sample_spec;
3859 ma_proc pa_stream_get_channel_map;
3860 ma_proc pa_stream_get_buffer_attr;
3861 ma_proc pa_stream_set_buffer_attr;
3862 ma_proc pa_stream_get_device_name;
3863 ma_proc pa_stream_set_write_callback;
3864 ma_proc pa_stream_set_read_callback;
3865 ma_proc pa_stream_set_suspended_callback;
3866 ma_proc pa_stream_is_suspended;
3867 ma_proc pa_stream_flush;
3868 ma_proc pa_stream_drain;
3869 ma_proc pa_stream_is_corked;
3870 ma_proc pa_stream_cork;
3871 ma_proc pa_stream_trigger;
3872 ma_proc pa_stream_begin_write;
3873 ma_proc pa_stream_write;
3874 ma_proc pa_stream_peek;
3875 ma_proc pa_stream_drop;
3876 ma_proc pa_stream_writable_size;
3877 ma_proc pa_stream_readable_size;
3878
3879 /*pa_mainloop**/ ma_ptr pMainLoop;
3880 /*pa_context**/ ma_ptr pPulseContext;
3881 } pulse;
3882#endif
3883#ifdef MA_SUPPORT_JACK
3884 struct
3885 {
3886 ma_handle jackSO;
3887 ma_proc jack_client_open;
3888 ma_proc jack_client_close;
3889 ma_proc jack_client_name_size;
3890 ma_proc jack_set_process_callback;
3891 ma_proc jack_set_buffer_size_callback;
3892 ma_proc jack_on_shutdown;
3893 ma_proc jack_get_sample_rate;
3894 ma_proc jack_get_buffer_size;
3895 ma_proc jack_get_ports;
3896 ma_proc jack_activate;
3897 ma_proc jack_deactivate;
3898 ma_proc jack_connect;
3899 ma_proc jack_port_register;
3900 ma_proc jack_port_name;
3901 ma_proc jack_port_get_buffer;
3902 ma_proc jack_free;
3903
3904 char* pClientName;
3905 ma_bool32 tryStartServer;
3906 } jack;
3907#endif
3908#ifdef MA_SUPPORT_COREAUDIO
3909 struct
3910 {
3911 ma_handle hCoreFoundation;
3912 ma_proc CFStringGetCString;
3913 ma_proc CFRelease;
3914
3915 ma_handle hCoreAudio;
3916 ma_proc AudioObjectGetPropertyData;
3917 ma_proc AudioObjectGetPropertyDataSize;
3918 ma_proc AudioObjectSetPropertyData;
3919 ma_proc AudioObjectAddPropertyListener;
3920 ma_proc AudioObjectRemovePropertyListener;
3921
3922 ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */
3923 ma_proc AudioComponentFindNext;
3924 ma_proc AudioComponentInstanceDispose;
3925 ma_proc AudioComponentInstanceNew;
3926 ma_proc AudioOutputUnitStart;
3927 ma_proc AudioOutputUnitStop;
3928 ma_proc AudioUnitAddPropertyListener;
3929 ma_proc AudioUnitGetPropertyInfo;
3930 ma_proc AudioUnitGetProperty;
3931 ma_proc AudioUnitSetProperty;
3932 ma_proc AudioUnitInitialize;
3933 ma_proc AudioUnitRender;
3934
3935 /*AudioComponent*/ ma_ptr component;
3936 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(). */
3937 } coreaudio;
3938#endif
3939#ifdef MA_SUPPORT_SNDIO
3940 struct
3941 {
3942 ma_handle sndioSO;
3943 ma_proc sio_open;
3944 ma_proc sio_close;
3945 ma_proc sio_setpar;
3946 ma_proc sio_getpar;
3947 ma_proc sio_getcap;
3948 ma_proc sio_start;
3949 ma_proc sio_stop;
3950 ma_proc sio_read;
3951 ma_proc sio_write;
3952 ma_proc sio_onmove;
3953 ma_proc sio_nfds;
3954 ma_proc sio_pollfd;
3955 ma_proc sio_revents;
3956 ma_proc sio_eof;
3957 ma_proc sio_setvol;
3958 ma_proc sio_onvol;
3959 ma_proc sio_initpar;
3960 } sndio;
3961#endif
3962#ifdef MA_SUPPORT_AUDIO4
3963 struct
3964 {
3965 int _unused;
3966 } audio4;
3967#endif
3968#ifdef MA_SUPPORT_OSS
3969 struct
3970 {
3971 int versionMajor;
3972 int versionMinor;
3973 } oss;
3974#endif
3975#ifdef MA_SUPPORT_AAUDIO
3976 struct
3977 {
3978 ma_handle hAAudio; /* libaaudio.so */
3979 ma_proc AAudio_createStreamBuilder;
3980 ma_proc AAudioStreamBuilder_delete;
3981 ma_proc AAudioStreamBuilder_setDeviceId;
3982 ma_proc AAudioStreamBuilder_setDirection;
3983 ma_proc AAudioStreamBuilder_setSharingMode;
3984 ma_proc AAudioStreamBuilder_setFormat;
3985 ma_proc AAudioStreamBuilder_setChannelCount;
3986 ma_proc AAudioStreamBuilder_setSampleRate;
3987 ma_proc AAudioStreamBuilder_setBufferCapacityInFrames;
3988 ma_proc AAudioStreamBuilder_setFramesPerDataCallback;
3989 ma_proc AAudioStreamBuilder_setDataCallback;
3990 ma_proc AAudioStreamBuilder_setErrorCallback;
3991 ma_proc AAudioStreamBuilder_setPerformanceMode;
3992 ma_proc AAudioStreamBuilder_setUsage;
3993 ma_proc AAudioStreamBuilder_setContentType;
3994 ma_proc AAudioStreamBuilder_setInputPreset;
3995 ma_proc AAudioStreamBuilder_openStream;
3996 ma_proc AAudioStream_close;
3997 ma_proc AAudioStream_getState;
3998 ma_proc AAudioStream_waitForStateChange;
3999 ma_proc AAudioStream_getFormat;
4000 ma_proc AAudioStream_getChannelCount;
4001 ma_proc AAudioStream_getSampleRate;
4002 ma_proc AAudioStream_getBufferCapacityInFrames;
4003 ma_proc AAudioStream_getFramesPerDataCallback;
4004 ma_proc AAudioStream_getFramesPerBurst;
4005 ma_proc AAudioStream_requestStart;
4006 ma_proc AAudioStream_requestStop;
4007 } aaudio;
4008#endif
4009#ifdef MA_SUPPORT_OPENSL
4010 struct
4011 {
4012 ma_handle libOpenSLES;
4013 ma_handle SL_IID_ENGINE;
4014 ma_handle SL_IID_AUDIOIODEVICECAPABILITIES;
4015 ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
4016 ma_handle SL_IID_RECORD;
4017 ma_handle SL_IID_PLAY;
4018 ma_handle SL_IID_OUTPUTMIX;
4019 ma_handle SL_IID_ANDROIDCONFIGURATION;
4020 ma_proc slCreateEngine;
4021 } opensl;
4022#endif
4023#ifdef MA_SUPPORT_WEBAUDIO
4024 struct
4025 {
4026 int _unused;
4027 } webaudio;
4028#endif
4029#ifdef MA_SUPPORT_NULL
4030 struct
4031 {
4032 int _unused;
4033 } null_backend;
4034#endif
4035 };
4036
4037 union
4038 {
4039#ifdef MA_WIN32
4040 struct
4041 {
4042 /*HMODULE*/ ma_handle hOle32DLL;
4043 ma_proc CoInitializeEx;
4044 ma_proc CoUninitialize;
4045 ma_proc CoCreateInstance;
4046 ma_proc CoTaskMemFree;
4047 ma_proc PropVariantClear;
4048 ma_proc StringFromGUID2;
4049
4050 /*HMODULE*/ ma_handle hUser32DLL;
4051 ma_proc GetForegroundWindow;
4052 ma_proc GetDesktopWindow;
4053
4054 /*HMODULE*/ ma_handle hAdvapi32DLL;
4055 ma_proc RegOpenKeyExA;
4056 ma_proc RegCloseKey;
4057 ma_proc RegQueryValueExA;
4058 } win32;
4059#endif
4060#ifdef MA_POSIX
4061 struct
4062 {
4063 ma_handle pthreadSO;
4064 ma_proc pthread_create;
4065 ma_proc pthread_join;
4066 ma_proc pthread_mutex_init;
4067 ma_proc pthread_mutex_destroy;
4068 ma_proc pthread_mutex_lock;
4069 ma_proc pthread_mutex_unlock;
4070 ma_proc pthread_cond_init;
4071 ma_proc pthread_cond_destroy;
4072 ma_proc pthread_cond_wait;
4073 ma_proc pthread_cond_signal;
4074 ma_proc pthread_attr_init;
4075 ma_proc pthread_attr_destroy;
4076 ma_proc pthread_attr_setschedpolicy;
4077 ma_proc pthread_attr_getschedparam;
4078 ma_proc pthread_attr_setschedparam;
4079 } posix;
4080#endif
4081 int _unused;
4082 };
4083};
4084
4085struct ma_device
4086{
4087 ma_context* pContext;
4088 ma_device_type type;
4089 ma_uint32 sampleRate;
4090 MA_ATOMIC ma_uint32 state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */
4091 ma_device_callback_proc onData; /* Set once at initialization time and should not be changed after. */
4092 ma_stop_proc onStop; /* Set once at initialization time and should not be changed after. */
4093 void* pUserData; /* Application defined data. */
4094 ma_mutex startStopLock;
4095 ma_event wakeupEvent;
4096 ma_event startEvent;
4097 ma_event stopEvent;
4098 ma_thread thread;
4099 ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */
4100 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(). */
4101 ma_bool8 noPreZeroedOutputBuffer;
4102 ma_bool8 noClip;
4103 MA_ATOMIC float masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */
4104 ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */
4105 struct
4106 {
4107 ma_resample_algorithm algorithm;
4108 struct
4109 {
4110 ma_uint32 lpfOrder;
4111 } linear;
4112 struct
4113 {
4114 int quality;
4115 } speex;
4116 } resampling;
4117 struct
4118 {
4119 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. */
4120 char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */
4121 ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
4122 ma_format format;
4123 ma_uint32 channels;
4124 ma_channel channelMap[MA_MAX_CHANNELS];
4125 ma_format internalFormat;
4126 ma_uint32 internalChannels;
4127 ma_uint32 internalSampleRate;
4128 ma_channel internalChannelMap[MA_MAX_CHANNELS];
4129 ma_uint32 internalPeriodSizeInFrames;
4130 ma_uint32 internalPeriods;
4131 ma_channel_mix_mode channelMixMode;
4132 ma_data_converter converter;
4133 } playback;
4134 struct
4135 {
4136 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. */
4137 char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */
4138 ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
4139 ma_format format;
4140 ma_uint32 channels;
4141 ma_channel channelMap[MA_MAX_CHANNELS];
4142 ma_format internalFormat;
4143 ma_uint32 internalChannels;
4144 ma_uint32 internalSampleRate;
4145 ma_channel internalChannelMap[MA_MAX_CHANNELS];
4146 ma_uint32 internalPeriodSizeInFrames;
4147 ma_uint32 internalPeriods;
4148 ma_channel_mix_mode channelMixMode;
4149 ma_data_converter converter;
4150 } capture;
4151
4152 union
4153 {
4154#ifdef MA_SUPPORT_WASAPI
4155 struct
4156 {
4157 /*IAudioClient**/ ma_ptr pAudioClientPlayback;
4158 /*IAudioClient**/ ma_ptr pAudioClientCapture;
4159 /*IAudioRenderClient**/ ma_ptr pRenderClient;
4160 /*IAudioCaptureClient**/ ma_ptr pCaptureClient;
4161 /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */
4162 ma_IMMNotificationClient notificationClient;
4163 /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */
4164 /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */
4165 ma_uint32 actualPeriodSizeInFramesPlayback; /* 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. */
4166 ma_uint32 actualPeriodSizeInFramesCapture;
4167 ma_uint32 originalPeriodSizeInFrames;
4168 ma_uint32 originalPeriodSizeInMilliseconds;
4169 ma_uint32 originalPeriods;
4170 ma_performance_profile originalPerformanceProfile;
4171 ma_uint32 periodSizeInFramesPlayback;
4172 ma_uint32 periodSizeInFramesCapture;
4173 MA_ATOMIC ma_bool32 isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
4174 MA_ATOMIC ma_bool32 isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
4175 ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
4176 ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
4177 ma_bool8 noHardwareOffloading;
4178 ma_bool8 allowCaptureAutoStreamRouting;
4179 ma_bool8 allowPlaybackAutoStreamRouting;
4180 ma_bool8 isDetachedPlayback;
4181 ma_bool8 isDetachedCapture;
4182 } wasapi;
4183#endif
4184#ifdef MA_SUPPORT_DSOUND
4185 struct
4186 {
4187 /*LPDIRECTSOUND*/ ma_ptr pPlayback;
4188 /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer;
4189 /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer;
4190 /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture;
4191 /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer;
4192 } dsound;
4193#endif
4194#ifdef MA_SUPPORT_WINMM
4195 struct
4196 {
4197 /*HWAVEOUT*/ ma_handle hDevicePlayback;
4198 /*HWAVEIN*/ ma_handle hDeviceCapture;
4199 /*HANDLE*/ ma_handle hEventPlayback;
4200 /*HANDLE*/ ma_handle hEventCapture;
4201 ma_uint32 fragmentSizeInFrames;
4202 ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */
4203 ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */
4204 ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */
4205 ma_uint32 headerFramesConsumedCapture; /* ^^^ */
4206 /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */
4207 /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */
4208 ma_uint8* pIntermediaryBufferPlayback;
4209 ma_uint8* pIntermediaryBufferCapture;
4210 ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */
4211 } winmm;
4212#endif
4213#ifdef MA_SUPPORT_ALSA
4214 struct
4215 {
4216 /*snd_pcm_t**/ ma_ptr pPCMPlayback;
4217 /*snd_pcm_t**/ ma_ptr pPCMCapture;
4218 /*struct pollfd**/ void* pPollDescriptorsPlayback;
4219 /*struct pollfd**/ void* pPollDescriptorsCapture;
4220 int pollDescriptorCountPlayback;
4221 int pollDescriptorCountCapture;
4222 int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */
4223 int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */
4224 ma_bool8 isUsingMMapPlayback;
4225 ma_bool8 isUsingMMapCapture;
4226 } alsa;
4227#endif
4228#ifdef MA_SUPPORT_PULSEAUDIO
4229 struct
4230 {
4231 /*pa_stream**/ ma_ptr pStreamPlayback;
4232 /*pa_stream**/ ma_ptr pStreamCapture;
4233 } pulse;
4234#endif
4235#ifdef MA_SUPPORT_JACK
4236 struct
4237 {
4238 /*jack_client_t**/ ma_ptr pClient;
4239 /*jack_port_t**/ ma_ptr pPortsPlayback[MA_MAX_CHANNELS];
4240 /*jack_port_t**/ ma_ptr pPortsCapture[MA_MAX_CHANNELS];
4241 float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */
4242 float* pIntermediaryBufferCapture;
4243 } jack;
4244#endif
4245#ifdef MA_SUPPORT_COREAUDIO
4246 struct
4247 {
4248 ma_uint32 deviceObjectIDPlayback;
4249 ma_uint32 deviceObjectIDCapture;
4250 /*AudioUnit*/ ma_ptr audioUnitPlayback;
4251 /*AudioUnit*/ ma_ptr audioUnitCapture;
4252 /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */
4253 ma_uint32 audioBufferCapInFrames; /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */
4254 ma_event stopEvent;
4255 ma_uint32 originalPeriodSizeInFrames;
4256 ma_uint32 originalPeriodSizeInMilliseconds;
4257 ma_uint32 originalPeriods;
4258 ma_performance_profile originalPerformanceProfile;
4259 ma_bool32 isDefaultPlaybackDevice;
4260 ma_bool32 isDefaultCaptureDevice;
4261 ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
4262 ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
4263 void* pRouteChangeHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */
4264 } coreaudio;
4265#endif
4266#ifdef MA_SUPPORT_SNDIO
4267 struct
4268 {
4269 ma_ptr handlePlayback;
4270 ma_ptr handleCapture;
4271 ma_bool32 isStartedPlayback;
4272 ma_bool32 isStartedCapture;
4273 } sndio;
4274#endif
4275#ifdef MA_SUPPORT_AUDIO4
4276 struct
4277 {
4278 int fdPlayback;
4279 int fdCapture;
4280 } audio4;
4281#endif
4282#ifdef MA_SUPPORT_OSS
4283 struct
4284 {
4285 int fdPlayback;
4286 int fdCapture;
4287 } oss;
4288#endif
4289#ifdef MA_SUPPORT_AAUDIO
4290 struct
4291 {
4292 /*AAudioStream**/ ma_ptr pStreamPlayback;
4293 /*AAudioStream**/ ma_ptr pStreamCapture;
4294 } aaudio;
4295#endif
4296#ifdef MA_SUPPORT_OPENSL
4297 struct
4298 {
4299 /*SLObjectItf*/ ma_ptr pOutputMixObj;
4300 /*SLOutputMixItf*/ ma_ptr pOutputMix;
4301 /*SLObjectItf*/ ma_ptr pAudioPlayerObj;
4302 /*SLPlayItf*/ ma_ptr pAudioPlayer;
4303 /*SLObjectItf*/ ma_ptr pAudioRecorderObj;
4304 /*SLRecordItf*/ ma_ptr pAudioRecorder;
4305 /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback;
4306 /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture;
4307 ma_bool32 isDrainingCapture;
4308 ma_bool32 isDrainingPlayback;
4309 ma_uint32 currentBufferIndexPlayback;
4310 ma_uint32 currentBufferIndexCapture;
4311 ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */
4312 ma_uint8* pBufferCapture;
4313 } opensl;
4314#endif
4315#ifdef MA_SUPPORT_WEBAUDIO
4316 struct
4317 {
4318 int indexPlayback; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */
4319 int indexCapture;
4320 } webaudio;
4321#endif
4322#ifdef MA_SUPPORT_NULL
4323 struct
4324 {
4325 ma_thread deviceThread;
4326 ma_event operationEvent;
4327 ma_event operationCompletionEvent;
4328 ma_semaphore operationSemaphore;
4329 ma_uint32 operation;
4330 ma_result operationResult;
4331 ma_timer timer;
4332 double priorRunTime;
4333 ma_uint32 currentPeriodFramesRemainingPlayback;
4334 ma_uint32 currentPeriodFramesRemainingCapture;
4335 ma_uint64 lastProcessedFramePlayback;
4336 ma_uint64 lastProcessedFrameCapture;
4337 MA_ATOMIC ma_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */
4338 } null_device;
4339#endif
4340 };
4341};
4342#if defined(_MSC_VER) && !defined(__clang__)
4343 #pragma warning(pop)
4344#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
4345 #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
4346#endif
4347
4348/*
4349Initializes a `ma_context_config` object.
4350
4351
4352Return Value
4353------------
4354A `ma_context_config` initialized to defaults.
4355
4356
4357Remarks
4358-------
4359You 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
4360is updated and new members are added to `ma_context_config`. It also sets logical defaults.
4361
4362You can override members of the returned object by changing it's members directly.
4363
4364
4365See Also
4366--------
4367ma_context_init()
4368*/
4369MA_API ma_context_config ma_context_config_init(void);
4370
4371/*
4372Initializes a context.
4373
4374The 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
4375device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices.
4376
4377
4378Parameters
4379----------
4380backends (in, optional)
4381 A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.
4382
4383backendCount (in, optional)
4384 The number of items in `backend`. Ignored if `backend` is NULL.
4385
4386pConfig (in, optional)
4387 The context configuration.
4388
4389pContext (in)
4390 A pointer to the context object being initialized.
4391
4392
4393Return Value
4394------------
4395MA_SUCCESS if successful; any other error code otherwise.
4396
4397
4398Thread Safety
4399-------------
4400Unsafe. Do not call this function across multiple threads as some backends read and write to global state.
4401
4402
4403Remarks
4404-------
4405When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order:
4406
4407 |-------------|-----------------------|--------------------------------------------------------|
4408 | Name | Enum Name | Supported Operating Systems |
4409 |-------------|-----------------------|--------------------------------------------------------|
4410 | WASAPI | ma_backend_wasapi | Windows Vista+ |
4411 | DirectSound | ma_backend_dsound | Windows XP+ |
4412 | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) |
4413 | Core Audio | ma_backend_coreaudio | macOS, iOS |
4414 | ALSA | ma_backend_alsa | Linux |
4415 | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
4416 | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
4417 | sndio | ma_backend_sndio | OpenBSD |
4418 | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
4419 | OSS | ma_backend_oss | FreeBSD |
4420 | AAudio | ma_backend_aaudio | Android 8+ |
4421 | OpenSL|ES | ma_backend_opensl | Android (API level 16+) |
4422 | Web Audio | ma_backend_webaudio | Web (via Emscripten) |
4423 | Null | ma_backend_null | Cross Platform (not used on Web) |
4424 |-------------|-----------------------|--------------------------------------------------------|
4425
4426The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings
4427can then be set directly on the structure. Below are the members of the `ma_context_config` object.
4428
4429 pLog
4430 A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not
4431 require logging. See the `ma_log` API for details on how to use the logging system.
4432
4433 threadPriority
4434 The desired priority to use for the audio thread. Allowable values include the following:
4435
4436 |--------------------------------------|
4437 | Thread Priority |
4438 |--------------------------------------|
4439 | ma_thread_priority_idle |
4440 | ma_thread_priority_lowest |
4441 | ma_thread_priority_low |
4442 | ma_thread_priority_normal |
4443 | ma_thread_priority_high |
4444 | ma_thread_priority_highest (default) |
4445 | ma_thread_priority_realtime |
4446 | ma_thread_priority_default |
4447 |--------------------------------------|
4448
4449 pUserData
4450 A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`.
4451
4452 allocationCallbacks
4453 Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation
4454 callbacks will be used for anything tied to the context, including devices.
4455
4456 alsa.useVerboseDeviceEnumeration
4457 ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique
4458 card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes
4459 it so the ALSA backend includes all devices. Defaults to false.
4460
4461 pulse.pApplicationName
4462 PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`.
4463
4464 pulse.pServerName
4465 PulseAudio only. The name of the server to connect to with `pa_context_connect()`.
4466
4467 pulse.tryAutoSpawn
4468 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
4469 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
4470 intrusive for the end user.
4471
4472 coreaudio.sessionCategory
4473 iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.
4474
4475 |-----------------------------------------|-------------------------------------|
4476 | miniaudio Token | Core Audio Token |
4477 |-----------------------------------------|-------------------------------------|
4478 | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient |
4479 | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient |
4480 | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback |
4481 | ma_ios_session_category_record | AVAudioSessionCategoryRecord |
4482 | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord |
4483 | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute |
4484 | ma_ios_session_category_none | AVAudioSessionCategoryAmbient |
4485 | ma_ios_session_category_default | AVAudioSessionCategoryAmbient |
4486 |-----------------------------------------|-------------------------------------|
4487
4488 coreaudio.sessionCategoryOptions
4489 iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.
4490
4491 |---------------------------------------------------------------------------|------------------------------------------------------------------|
4492 | miniaudio Token | Core Audio Token |
4493 |---------------------------------------------------------------------------|------------------------------------------------------------------|
4494 | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers |
4495 | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers |
4496 | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth |
4497 | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker |
4498 | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers |
4499 | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP |
4500 | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay |
4501 |---------------------------------------------------------------------------|------------------------------------------------------------------|
4502
4503 jack.pClientName
4504 The name of the client to pass to `jack_client_open()`.
4505
4506 jack.tryStartServer
4507 Whether or not to try auto-starting the JACK server. Defaults to false.
4508
4509
4510It 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
4511relevant backends every time it's initialized.
4512
4513The location of the context cannot change throughout it's lifetime. Consider allocating the `ma_context` object with `malloc()` if this is an issue. The
4514reason for this is that a pointer to the context is stored in the `ma_device` structure.
4515
4516
4517Example 1 - Default Initialization
4518----------------------------------
4519The example below shows how to initialize the context using the default configuration.
4520
4521```c
4522ma_context context;
4523ma_result result = ma_context_init(NULL, 0, NULL, &context);
4524if (result != MA_SUCCESS) {
4525 // Error.
4526}
4527```
4528
4529
4530Example 2 - Custom Configuration
4531--------------------------------
4532The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program
4533wants 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
4534want an error to be returned if no valid backend is available which they achieve by excluding the Null backend.
4535
4536For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface.
4537
4538```c
4539ma_backend backends[] = {
4540 ma_backend_alsa,
4541 ma_backend_pulseaudio,
4542 ma_backend_wasapi,
4543 ma_backend_dsound
4544};
4545
4546ma_context_config config = ma_context_config_init();
4547config.logCallback = my_log_callback;
4548config.pUserData = pMyUserData;
4549
4550ma_context context;
4551ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context);
4552if (result != MA_SUCCESS) {
4553 // Error.
4554 if (result == MA_NO_BACKEND) {
4555 // Couldn't find an appropriate backend.
4556 }
4557}
4558```
4559
4560
4561See Also
4562--------
4563ma_context_config_init()
4564ma_context_uninit()
4565*/
4566MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext);
4567
4568/*
4569Uninitializes a context.
4570
4571
4572Return Value
4573------------
4574MA_SUCCESS if successful; any other error code otherwise.
4575
4576
4577Thread Safety
4578-------------
4579Unsafe. Do not call this function across multiple threads as some backends read and write to global state.
4580
4581
4582Remarks
4583-------
4584Results are undefined if you call this while any device created by this context is still active.
4585
4586
4587See Also
4588--------
4589ma_context_init()
4590*/
4591MA_API ma_result ma_context_uninit(ma_context* pContext);
4592
4593/*
4594Retrieves the size of the ma_context object.
4595
4596This is mainly for the purpose of bindings to know how much memory to allocate.
4597*/
4598MA_API size_t ma_context_sizeof(void);
4599
4600/*
4601Retrieves a pointer to the log object associated with this context.
4602
4603
4604Remarks
4605-------
4606Pass the returned pointer to `ma_log_post()`, `ma_log_postv()` or `ma_log_postf()` to post a log
4607message.
4608
4609
4610Return Value
4611------------
4612A pointer to the `ma_log` object that the context uses to post log messages. If some error occurs,
4613NULL will be returned.
4614*/
4615MA_API ma_log* ma_context_get_log(ma_context* pContext);
4616
4617/*
4618Enumerates over every device (both playback and capture).
4619
4620This 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
4621an internal heap allocation, or it simply suits your code better.
4622
4623Note 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
4624opening 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,
4625but don't call it from within the enumeration callback.
4626
4627Returning false from the callback will stop enumeration. Returning true will continue enumeration.
4628
4629
4630Parameters
4631----------
4632pContext (in)
4633 A pointer to the context performing the enumeration.
4634
4635callback (in)
4636 The callback to fire for each enumerated device.
4637
4638pUserData (in)
4639 A pointer to application-defined data passed to the callback.
4640
4641
4642Return Value
4643------------
4644MA_SUCCESS if successful; any other error code otherwise.
4645
4646
4647Thread Safety
4648-------------
4649Safe. This is guarded using a simple mutex lock.
4650
4651
4652Remarks
4653-------
4654Do _not_ assume the first enumerated device of a given type is the default device.
4655
4656Some backends and platforms may only support default playback and capture devices.
4657
4658In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also,
4659do not try to call `ma_context_get_device_info()` from within the callback.
4660
4661Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation.
4662
4663
4664Example 1 - Simple Enumeration
4665------------------------------
4666ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
4667{
4668 printf("Device Name: %s\n", pInfo->name);
4669 return MA_TRUE;
4670}
4671
4672ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData);
4673if (result != MA_SUCCESS) {
4674 // Error.
4675}
4676
4677
4678See Also
4679--------
4680ma_context_get_devices()
4681*/
4682MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData);
4683
4684/*
4685Retrieves basic information about every active playback and/or capture device.
4686
4687This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos`
4688parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback.
4689
4690
4691Parameters
4692----------
4693pContext (in)
4694 A pointer to the context performing the enumeration.
4695
4696ppPlaybackDeviceInfos (out)
4697 A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices.
4698
4699pPlaybackDeviceCount (out)
4700 A pointer to an unsigned integer that will receive the number of playback devices.
4701
4702ppCaptureDeviceInfos (out)
4703 A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices.
4704
4705pCaptureDeviceCount (out)
4706 A pointer to an unsigned integer that will receive the number of capture devices.
4707
4708
4709Return Value
4710------------
4711MA_SUCCESS if successful; any other error code otherwise.
4712
4713
4714Thread Safety
4715-------------
4716Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple
4717threads. Instead, you need to make a copy of the returned data with your own higher level synchronization.
4718
4719
4720Remarks
4721-------
4722It is _not_ safe to assume the first device in the list is the default device.
4723
4724You can pass in NULL for the playback or capture lists in which case they'll be ignored.
4725
4726The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers.
4727
4728
4729See Also
4730--------
4731ma_context_get_devices()
4732*/
4733MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount);
4734
4735/*
4736Retrieves information about a device of the given type, with the specified ID and share mode.
4737
4738
4739Parameters
4740----------
4741pContext (in)
4742 A pointer to the context performing the query.
4743
4744deviceType (in)
4745 The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`.
4746
4747pDeviceID (in)
4748 The ID of the device being queried.
4749
4750shareMode (in)
4751 The share mode to query for device capabilities. This should be set to whatever you're intending on using when initializing the device. If you're unsure,
4752 set this to `ma_share_mode_shared`.
4753
4754pDeviceInfo (out)
4755 A pointer to the `ma_device_info` structure that will receive the device information.
4756
4757
4758Return Value
4759------------
4760MA_SUCCESS if successful; any other error code otherwise.
4761
4762
4763Thread Safety
4764-------------
4765Safe. This is guarded using a simple mutex lock.
4766
4767
4768Remarks
4769-------
4770Do _not_ call this from within the `ma_context_enumerate_devices()` callback.
4771
4772It'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
4773shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify
4774which 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
4775the requested share mode is unsupported.
4776
4777This leaves pDeviceInfo unmodified in the result of an error.
4778*/
4779MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo);
4780
4781/*
4782Determines if the given context supports loopback mode.
4783
4784
4785Parameters
4786----------
4787pContext (in)
4788 A pointer to the context getting queried.
4789
4790
4791Return Value
4792------------
4793MA_TRUE if the context supports loopback mode; MA_FALSE otherwise.
4794*/
4795MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext);
4796
4797
4798
4799/*
4800Initializes a device config with default settings.
4801
4802
4803Parameters
4804----------
4805deviceType (in)
4806 The type of the device this config is being initialized for. This must set to one of the following:
4807
4808 |-------------------------|
4809 | Device Type |
4810 |-------------------------|
4811 | ma_device_type_playback |
4812 | ma_device_type_capture |
4813 | ma_device_type_duplex |
4814 | ma_device_type_loopback |
4815 |-------------------------|
4816
4817
4818Return Value
4819------------
4820A new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks.
4821
4822
4823Thread Safety
4824-------------
4825Safe.
4826
4827
4828Callback Safety
4829---------------
4830Safe, but don't try initializing a device in a callback.
4831
4832
4833Remarks
4834-------
4835The 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
4836typical configuration which sets the sample format, channel count, sample rate, data callback and user data. These are usually things you will want to change
4837before initializing the device.
4838
4839See `ma_device_init()` for details on specific configuration options.
4840
4841
4842Example 1 - Simple Configuration
4843--------------------------------
4844The 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
4845then the returned object is modified directly. This is important because it ensures that your program continues to work as new configuration options are added
4846to the `ma_device_config` structure.
4847
4848```c
4849ma_device_config config = ma_device_config_init(ma_device_type_playback);
4850config.playback.format = ma_format_f32;
4851config.playback.channels = 2;
4852config.sampleRate = 48000;
4853config.dataCallback = ma_data_callback;
4854config.pUserData = pMyUserData;
4855```
4856
4857
4858See Also
4859--------
4860ma_device_init()
4861ma_device_init_ex()
4862*/
4863MA_API ma_device_config ma_device_config_init(ma_device_type deviceType);
4864
4865
4866/*
4867Initializes a device.
4868
4869A 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
4870from 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
4871playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the
4872device is done via a callback which is fired by miniaudio at periodic time intervals.
4873
4874The 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
4875or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and
4876increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but
4877miniaudio'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
4878media 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
4879backend is ultimately responsible for what it gives you. You cannot assume you will get exactly what you ask for.
4880
4881When 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
4882format 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
4883can 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.
4884
4885
4886Parameters
4887----------
4888pContext (in, optional)
4889 A pointer to the context that owns the device. This can be null, in which case it creates a default context internally.
4890
4891pConfig (in)
4892 A pointer to the device configuration. Cannot be null. See remarks for details.
4893
4894pDevice (out)
4895 A pointer to the device object being initialized.
4896
4897
4898Return Value
4899------------
4900MA_SUCCESS if successful; any other error code otherwise.
4901
4902
4903Thread Safety
4904-------------
4905Unsafe. 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
4906calling this at the same time as `ma_device_uninit()`.
4907
4908
4909Callback Safety
4910---------------
4911Unsafe. It is not safe to call this inside any callback.
4912
4913
4914Remarks
4915-------
4916Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so:
4917
4918 ```c
4919 ma_context_init(NULL, 0, NULL, &context);
4920 ```
4921
4922Do 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
4923device.pContext for the initialization of other devices.
4924
4925The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can
4926then be set directly on the structure. Below are the members of the `ma_device_config` object.
4927
4928 deviceType
4929 Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`.
4930
4931 sampleRate
4932 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.
4933
4934 periodSizeInFrames
4935 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
4936 be used depending on the selected performance profile. This value affects latency. See below for details.
4937
4938 periodSizeInMilliseconds
4939 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
4940 used depending on the selected performance profile. The value affects latency. See below for details.
4941
4942 periods
4943 The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by
4944 this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured.
4945
4946 performanceProfile
4947 A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or
4948 `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value.
4949
4950 noPreZeroedOutputBuffer
4951 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
4952 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
4953 callback will write to every sample in the output buffer, or if you are doing your own clearing.
4954
4955 noClip
4956 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
4957 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
4958 applies when the playback sample format is f32.
4959
4960 dataCallback
4961 The callback to fire whenever data is ready to be delivered to or from the device.
4962
4963 stopCallback
4964 The callback to fire whenever the device has stopped, either explicitly via `ma_device_stop()`, or implicitly due to things like the device being
4965 disconnected.
4966
4967 pUserData
4968 The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`.
4969
4970 resampling.algorithm
4971 The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The
4972 default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`.
4973
4974 resampling.linear.lpfOrder
4975 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
4976 the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is
4977 `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`.
4978
4979 playback.pDeviceID
4980 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
4981 default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration.
4982
4983 playback.format
4984 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
4985 initialization from the device object directly with `device.playback.format`.
4986
4987 playback.channels
4988 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
4989 from the device object directly with `device.playback.channels`.
4990
4991 playback.channelMap
4992 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
4993 device object direct with `device.playback.channelMap`.
4994
4995 playback.shareMode
4996 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
4997 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
4998 ma_share_mode_shared and reinitializing.
4999
5000 capture.pDeviceID
5001 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
5002 default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration.
5003
5004 capture.format
5005 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
5006 initialization from the device object directly with `device.capture.format`.
5007
5008 capture.channels
5009 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
5010 from the device object directly with `device.capture.channels`.
5011
5012 capture.channelMap
5013 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
5014 device object direct with `device.capture.channelMap`.
5015
5016 capture.shareMode
5017 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
5018 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
5019 ma_share_mode_shared and reinitializing.
5020
5021 wasapi.noAutoConvertSRC
5022 WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false.
5023
5024 wasapi.noDefaultQualitySRC
5025 WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`.
5026 You should usually leave this set to false, which is the default.
5027
5028 wasapi.noAutoStreamRouting
5029 WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false.
5030
5031 wasapi.noHardwareOffloading
5032 WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false.
5033
5034 alsa.noMMap
5035 ALSA only. When set to true, disables MMap mode. Defaults to false.
5036
5037 alsa.noAutoFormat
5038 ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false.
5039
5040 alsa.noAutoChannels
5041 ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false.
5042
5043 alsa.noAutoResample
5044 ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false.
5045
5046 pulse.pStreamNamePlayback
5047 PulseAudio only. Sets the stream name for playback.
5048
5049 pulse.pStreamNameCapture
5050 PulseAudio only. Sets the stream name for capture.
5051
5052 coreaudio.allowNominalSampleRateChange
5053 Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This
5054 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
5055 that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will
5056 find the closest match between the sample rate requested in the device config and the sample rates natively supported by the
5057 hardware. When set to false, the sample rate currently set by the operating system will always be used.
5058
5059
5060Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device.
5061
5062After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`.
5063
5064If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or
5065`MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or
5066`ma_performance_profile_conservative`.
5067
5068If 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
5069in 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
5070config) 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,
5071for 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.
5072Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary.
5073
5074When sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by the config
5075and 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
5076on an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`,
5077`playback/capture.channels` and `sampleRate` members of the device object.
5078
5079When 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
5080asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information.
5081
5082ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture.
5083If these fail it will try falling back to the "hw" device.
5084
5085
5086Example 1 - Simple Initialization
5087---------------------------------
5088This 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
5089playback device this is usually all you need.
5090
5091```c
5092ma_device_config config = ma_device_config_init(ma_device_type_playback);
5093config.playback.format = ma_format_f32;
5094config.playback.channels = 2;
5095config.sampleRate = 48000;
5096config.dataCallback = ma_data_callback;
5097config.pMyUserData = pMyUserData;
5098
5099ma_device device;
5100ma_result result = ma_device_init(NULL, &config, &device);
5101if (result != MA_SUCCESS) {
5102 // Error
5103}
5104```
5105
5106
5107Example 2 - Advanced Initialization
5108-----------------------------------
5109This 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
5110and 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
5111enumeration.
5112
5113```c
5114ma_context context;
5115ma_result result = ma_context_init(NULL, 0, NULL, &context);
5116if (result != MA_SUCCESS) {
5117 // Error
5118}
5119
5120ma_device_info* pPlaybackDeviceInfos;
5121ma_uint32 playbackDeviceCount;
5122result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL);
5123if (result != MA_SUCCESS) {
5124 // Error
5125}
5126
5127// ... choose a device from pPlaybackDeviceInfos ...
5128
5129ma_device_config config = ma_device_config_init(ma_device_type_playback);
5130config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices().
5131config.playback.format = ma_format_f32;
5132config.playback.channels = 2;
5133config.sampleRate = 48000;
5134config.dataCallback = ma_data_callback;
5135config.pUserData = pMyUserData;
5136config.periodSizeInMilliseconds = 10;
5137config.periods = 3;
5138
5139ma_device device;
5140result = ma_device_init(&context, &config, &device);
5141if (result != MA_SUCCESS) {
5142 // Error
5143}
5144```
5145
5146
5147See Also
5148--------
5149ma_device_config_init()
5150ma_device_uninit()
5151ma_device_start()
5152ma_context_init()
5153ma_context_get_devices()
5154ma_context_enumerate_devices()
5155*/
5156MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice);
5157
5158/*
5159Initializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context.
5160
5161This 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
5162allows you to configure the internally created context.
5163
5164
5165Parameters
5166----------
5167backends (in, optional)
5168 A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.
5169
5170backendCount (in, optional)
5171 The number of items in `backend`. Ignored if `backend` is NULL.
5172
5173pContextConfig (in, optional)
5174 The context configuration.
5175
5176pConfig (in)
5177 A pointer to the device configuration. Cannot be null. See remarks for details.
5178
5179pDevice (out)
5180 A pointer to the device object being initialized.
5181
5182
5183Return Value
5184------------
5185MA_SUCCESS if successful; any other error code otherwise.
5186
5187
5188Thread Safety
5189-------------
5190Unsafe. 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
5191calling this at the same time as `ma_device_uninit()`.
5192
5193
5194Callback Safety
5195---------------
5196Unsafe. It is not safe to call this inside any callback.
5197
5198
5199Remarks
5200-------
5201You 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
5202your own context.
5203
5204See the documentation for `ma_context_init()` for information on the different context configuration options.
5205
5206
5207See Also
5208--------
5209ma_device_init()
5210ma_device_uninit()
5211ma_device_config_init()
5212ma_context_init()
5213*/
5214MA_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);
5215
5216/*
5217Uninitializes a device.
5218
5219This will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do.
5220
5221
5222Parameters
5223----------
5224pDevice (in)
5225 A pointer to the device to stop.
5226
5227
5228Return Value
5229------------
5230Nothing
5231
5232
5233Thread Safety
5234-------------
5235Unsafe. As soon as this API is called the device should be considered undefined.
5236
5237
5238Callback Safety
5239---------------
5240Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.
5241
5242
5243See Also
5244--------
5245ma_device_init()
5246ma_device_stop()
5247*/
5248MA_API void ma_device_uninit(ma_device* pDevice);
5249
5250
5251/*
5252Retrieves a pointer to the context that owns the given device.
5253*/
5254MA_API ma_context* ma_device_get_context(ma_device* pDevice);
5255
5256/*
5257Helper function for retrieving the log object associated with the context that owns this device.
5258*/
5259MA_API ma_log* ma_device_get_log(ma_device* pDevice);
5260
5261
5262/*
5263Starts the device. For playback devices this begins playback. For capture devices it begins recording.
5264
5265Use `ma_device_stop()` to stop the device.
5266
5267
5268Parameters
5269----------
5270pDevice (in)
5271 A pointer to the device to start.
5272
5273
5274Return Value
5275------------
5276MA_SUCCESS if successful; any other error code otherwise.
5277
5278
5279Thread Safety
5280-------------
5281Safe. It's safe to call this from any thread with the exception of the callback thread.
5282
5283
5284Callback Safety
5285---------------
5286Unsafe. It is not safe to call this inside any callback.
5287
5288
5289Remarks
5290-------
5291For 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
5292audio data in the buffer, which needs to be done before the device begins playback.
5293
5294This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety.
5295
5296Do not call this in any callback.
5297
5298
5299See Also
5300--------
5301ma_device_stop()
5302*/
5303MA_API ma_result ma_device_start(ma_device* pDevice);
5304
5305/*
5306Stops the device. For playback devices this stops playback. For capture devices it stops recording.
5307
5308Use `ma_device_start()` to start the device again.
5309
5310
5311Parameters
5312----------
5313pDevice (in)
5314 A pointer to the device to stop.
5315
5316
5317Return Value
5318------------
5319MA_SUCCESS if successful; any other error code otherwise.
5320
5321
5322Thread Safety
5323-------------
5324Safe. It's safe to call this from any thread with the exception of the callback thread.
5325
5326
5327Callback Safety
5328---------------
5329Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.
5330
5331
5332Remarks
5333-------
5334This 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
5335backends 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
5336that was specified at initialization time).
5337
5338Backends 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
5339the 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
5340speakers or received from the microphone which can in turn result in de-syncs.
5341
5342Do not call this in any callback.
5343
5344This will be called implicitly by `ma_device_uninit()`.
5345
5346
5347See Also
5348--------
5349ma_device_start()
5350*/
5351MA_API ma_result ma_device_stop(ma_device* pDevice);
5352
5353/*
5354Determines whether or not the device is started.
5355
5356
5357Parameters
5358----------
5359pDevice (in)
5360 A pointer to the device whose start state is being retrieved.
5361
5362
5363Return Value
5364------------
5365True if the device is started, false otherwise.
5366
5367
5368Thread Safety
5369-------------
5370Safe. 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
5371value will be out of sync.
5372
5373
5374Callback Safety
5375---------------
5376Safe. This is implemented as a simple accessor.
5377
5378
5379See Also
5380--------
5381ma_device_start()
5382ma_device_stop()
5383*/
5384MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice);
5385
5386
5387/*
5388Retrieves the state of the device.
5389
5390
5391Parameters
5392----------
5393pDevice (in)
5394 A pointer to the device whose state is being retrieved.
5395
5396
5397Return Value
5398------------
5399The current state of the device. The return value will be one of the following:
5400
5401 +------------------------+------------------------------------------------------------------------------+
5402 | MA_STATE_UNINITIALIZED | Will only be returned if the device is in the middle of initialization. |
5403 +------------------------+------------------------------------------------------------------------------+
5404 | MA_STATE_STOPPED | The device is stopped. The initial state of the device after initialization. |
5405 +------------------------+------------------------------------------------------------------------------+
5406 | MA_STATE_STARTED | The device started and requesting and/or delivering audio data. |
5407 +------------------------+------------------------------------------------------------------------------+
5408 | MA_STATE_STARTING | The device is in the process of starting. |
5409 +------------------------+------------------------------------------------------------------------------+
5410 | MA_STATE_STOPPING | The device is in the process of stopping. |
5411 +------------------------+------------------------------------------------------------------------------+
5412
5413
5414Thread Safety
5415-------------
5416Safe. 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,
5417there's a possibility the return value could be out of sync. See remarks.
5418
5419
5420Callback Safety
5421---------------
5422Safe. This is implemented as a simple accessor.
5423
5424
5425Remarks
5426-------
5427The general flow of a devices state goes like this:
5428
5429 ```
5430 ma_device_init() -> MA_STATE_UNINITIALIZED -> MA_STATE_STOPPED
5431 ma_device_start() -> MA_STATE_STARTING -> MA_STATE_STARTED
5432 ma_device_stop() -> MA_STATE_STOPPING -> MA_STATE_STOPPED
5433 ```
5434
5435When 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
5436value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own
5437synchronization.
5438*/
5439MA_API ma_uint32 ma_device_get_state(const ma_device* pDevice);
5440
5441
5442/*
5443Sets the master volume factor for the device.
5444
5445The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_gain_db()` to use decibel notation, where 0 is full volume and
5446values less than 0 decreases the volume.
5447
5448
5449Parameters
5450----------
5451pDevice (in)
5452 A pointer to the device whose volume is being set.
5453
5454volume (in)
5455 The new volume factor. Must be within the range of [0, 1].
5456
5457
5458Return Value
5459------------
5460MA_SUCCESS if the volume was set successfully.
5461MA_INVALID_ARGS if pDevice is NULL.
5462MA_INVALID_ARGS if the volume factor is not within the range of [0, 1].
5463
5464
5465Thread Safety
5466-------------
5467Safe. This just sets a local member of the device object.
5468
5469
5470Callback Safety
5471---------------
5472Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.
5473
5474
5475Remarks
5476-------
5477This applies the volume factor across all channels.
5478
5479This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.
5480
5481
5482See Also
5483--------
5484ma_device_get_master_volume()
5485ma_device_set_master_volume_gain_db()
5486ma_device_get_master_volume_gain_db()
5487*/
5488MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume);
5489
5490/*
5491Retrieves the master volume factor for the device.
5492
5493
5494Parameters
5495----------
5496pDevice (in)
5497 A pointer to the device whose volume factor is being retrieved.
5498
5499pVolume (in)
5500 A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1].
5501
5502
5503Return Value
5504------------
5505MA_SUCCESS if successful.
5506MA_INVALID_ARGS if pDevice is NULL.
5507MA_INVALID_ARGS if pVolume is NULL.
5508
5509
5510Thread Safety
5511-------------
5512Safe. This just a simple member retrieval.
5513
5514
5515Callback Safety
5516---------------
5517Safe.
5518
5519
5520Remarks
5521-------
5522If an error occurs, `*pVolume` will be set to 0.
5523
5524
5525See Also
5526--------
5527ma_device_set_master_volume()
5528ma_device_set_master_volume_gain_db()
5529ma_device_get_master_volume_gain_db()
5530*/
5531MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume);
5532
5533/*
5534Sets the master volume for the device as gain in decibels.
5535
5536A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume.
5537
5538
5539Parameters
5540----------
5541pDevice (in)
5542 A pointer to the device whose gain is being set.
5543
5544gainDB (in)
5545 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.
5546
5547
5548Return Value
5549------------
5550MA_SUCCESS if the volume was set successfully.
5551MA_INVALID_ARGS if pDevice is NULL.
5552MA_INVALID_ARGS if the gain is > 0.
5553
5554
5555Thread Safety
5556-------------
5557Safe. This just sets a local member of the device object.
5558
5559
5560Callback Safety
5561---------------
5562Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.
5563
5564
5565Remarks
5566-------
5567This applies the gain across all channels.
5568
5569This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.
5570
5571
5572See Also
5573--------
5574ma_device_get_master_volume_gain_db()
5575ma_device_set_master_volume()
5576ma_device_get_master_volume()
5577*/
5578MA_API ma_result ma_device_set_master_gain_db(ma_device* pDevice, float gainDB);
5579
5580/*
5581Retrieves the master gain in decibels.
5582
5583
5584Parameters
5585----------
5586pDevice (in)
5587 A pointer to the device whose gain is being retrieved.
5588
5589pGainDB (in)
5590 A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0.
5591
5592
5593Return Value
5594------------
5595MA_SUCCESS if successful.
5596MA_INVALID_ARGS if pDevice is NULL.
5597MA_INVALID_ARGS if pGainDB is NULL.
5598
5599
5600Thread Safety
5601-------------
5602Safe. This just a simple member retrieval.
5603
5604
5605Callback Safety
5606---------------
5607Safe.
5608
5609
5610Remarks
5611-------
5612If an error occurs, `*pGainDB` will be set to 0.
5613
5614
5615See Also
5616--------
5617ma_device_set_master_volume_gain_db()
5618ma_device_set_master_volume()
5619ma_device_get_master_volume()
5620*/
5621MA_API ma_result ma_device_get_master_gain_db(ma_device* pDevice, float* pGainDB);
5622
5623
5624/*
5625Called from the data callback of asynchronous backends to allow miniaudio to process the data and fire the miniaudio data callback.
5626
5627
5628Parameters
5629----------
5630pDevice (in)
5631 A pointer to device whose processing the data callback.
5632
5633pOutput (out)
5634 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
5635 this can be NULL, in which case pInput must not be NULL.
5636
5637pInput (in)
5638 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
5639 NULL, in which case `pOutput` must not be NULL.
5640
5641frameCount (in)
5642 The number of frames being processed.
5643
5644
5645Return Value
5646------------
5647MA_SUCCESS if successful; any other result code otherwise.
5648
5649
5650Thread Safety
5651-------------
5652This function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a
5653playback and capture device in duplex setups.
5654
5655
5656Callback Safety
5657---------------
5658Do not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend.
5659
5660
5661Remarks
5662-------
5663If both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in
5664which case `pInput` will be processed first, followed by `pOutput`.
5665
5666If you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that
5667callback.
5668*/
5669MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
5670
5671
5672/*
5673Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile.
5674
5675This function is used by backends for helping determine an appropriately sized buffer to use with
5676the device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the
5677`pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a
5678best guess at the device's native sample rate is also required which is where `nativeSampleRate`
5679comes in. In addition, the performance profile is also needed for cases where both the period size
5680in frames and milliseconds are both zero.
5681
5682
5683Parameters
5684----------
5685pDescriptor (in)
5686 A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members
5687 will be used for the calculation of the buffer size.
5688
5689nativeSampleRate (in)
5690 The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of
5691 `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which
5692 case a sample rate is required to convert to a size in frames.
5693
5694performanceProfile (in)
5695 When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are
5696 zero, miniaudio will fall back to a buffer size based on the performance profile. The profile
5697 to use for this calculation is determine by this parameter.
5698
5699
5700Return Value
5701------------
5702The calculated buffer size in frames.
5703
5704
5705Thread Safety
5706-------------
5707This is safe so long as nothing modifies `pDescriptor` at the same time. However, this function
5708should only ever be called from within the backend's device initialization routine and therefore
5709shouldn't have any multithreading concerns.
5710
5711
5712Callback Safety
5713---------------
5714This is safe to call within the data callback, but there is no reason to ever do this.
5715
5716
5717Remarks
5718-------
5719If `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that
5720is also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead.
5721*/
5722MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile);
5723
5724
5725
5726/*
5727Retrieves a friendly name for a backend.
5728*/
5729MA_API const char* ma_get_backend_name(ma_backend backend);
5730
5731/*
5732Determines whether or not the given backend is available by the compilation environment.
5733*/
5734MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend);
5735
5736/*
5737Retrieves compile-time enabled backends.
5738
5739
5740Parameters
5741----------
5742pBackends (out, optional)
5743 A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting
5744 the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends.
5745
5746backendCap (in)
5747 The capacity of the `pBackends` buffer.
5748
5749pBackendCount (out)
5750 A pointer to the variable that will receive the enabled backend count.
5751
5752
5753Return Value
5754------------
5755MA_SUCCESS if successful.
5756MA_INVALID_ARGS if `pBackendCount` is NULL.
5757MA_NO_SPACE if the capacity of `pBackends` is not large enough.
5758
5759If `MA_NO_SPACE` is returned, the `pBackends` buffer will be filled with `*pBackendCount` values.
5760
5761
5762Thread Safety
5763-------------
5764Safe.
5765
5766
5767Callback Safety
5768---------------
5769Safe.
5770
5771
5772Remarks
5773-------
5774If you want to retrieve the number of backends so you can determine the capacity of `pBackends` buffer, you can call
5775this function with `pBackends` set to NULL.
5776
5777This will also enumerate the null backend. If you don't want to include this you need to check for `ma_backend_null`
5778when you enumerate over the returned backends and handle it appropriately. Alternatively, you can disable it at
5779compile time with `MA_NO_NULL`.
5780
5781The returned backends are determined based on compile time settings, not the platform it's currently running on. For
5782example, PulseAudio will be returned if it was enabled at compile time, even when the user doesn't actually have
5783PulseAudio installed.
5784
5785
5786Example 1
5787---------
5788The example below retrieves the enabled backend count using a fixed sized buffer allocated on the stack. The buffer is
5789given a capacity of `MA_BACKEND_COUNT` which will guarantee it'll be large enough to store all available backends.
5790Since `MA_BACKEND_COUNT` is always a relatively small value, this should be suitable for most scenarios.
5791
5792```
5793ma_backend enabledBackends[MA_BACKEND_COUNT];
5794size_t enabledBackendCount;
5795
5796result = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount);
5797if (result != MA_SUCCESS) {
5798 // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid.
5799}
5800```
5801
5802
5803See Also
5804--------
5805ma_is_backend_enabled()
5806*/
5807MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount);
5808
5809/*
5810Determines whether or not loopback mode is support by a backend.
5811*/
5812MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend);
5813
5814#endif /* MA_NO_DEVICE_IO */
5815
5816
5817#ifndef MA_NO_THREADING
5818
5819/*
5820Locks a spinlock.
5821*/
5822MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock);
5823
5824/*
5825Locks a spinlock, but does not yield() when looping.
5826*/
5827MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock);
5828
5829/*
5830Unlocks a spinlock.
5831*/
5832MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock);
5833
5834
5835/*
5836Creates a mutex.
5837
5838A mutex must be created from a valid context. A mutex is initially unlocked.
5839*/
5840MA_API ma_result ma_mutex_init(ma_mutex* pMutex);
5841
5842/*
5843Deletes a mutex.
5844*/
5845MA_API void ma_mutex_uninit(ma_mutex* pMutex);
5846
5847/*
5848Locks a mutex with an infinite timeout.
5849*/
5850MA_API void ma_mutex_lock(ma_mutex* pMutex);
5851
5852/*
5853Unlocks a mutex.
5854*/
5855MA_API void ma_mutex_unlock(ma_mutex* pMutex);
5856
5857
5858/*
5859Initializes an auto-reset event.
5860*/
5861MA_API ma_result ma_event_init(ma_event* pEvent);
5862
5863/*
5864Uninitializes an auto-reset event.
5865*/
5866MA_API void ma_event_uninit(ma_event* pEvent);
5867
5868/*
5869Waits for the specified auto-reset event to become signalled.
5870*/
5871MA_API ma_result ma_event_wait(ma_event* pEvent);
5872
5873/*
5874Signals the specified auto-reset event.
5875*/
5876MA_API ma_result ma_event_signal(ma_event* pEvent);
5877#endif /* MA_NO_THREADING */
5878
5879
5880/************************************************************************************************************************************************************
5881
5882Utiltities
5883
5884************************************************************************************************************************************************************/
5885
5886/*
5887Adjust buffer size based on a scaling factor.
5888
5889This just multiplies the base size by the scaling factor, making sure it's a size of at least 1.
5890*/
5891MA_API ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale);
5892
5893/*
5894Calculates a buffer size in milliseconds from the specified number of frames and sample rate.
5895*/
5896MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate);
5897
5898/*
5899Calculates a buffer size in frames from the specified number of milliseconds and sample rate.
5900*/
5901MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate);
5902
5903/*
5904Copies PCM frames from one buffer to another.
5905*/
5906MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
5907
5908/*
5909Copies silent frames into the given buffer.
5910
5911Remarks
5912-------
5913For 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
5914makes more sense for the purpose of mixing to initialize it to the center point.
5915*/
5916MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
5917static MA_INLINE void ma_zero_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels) { ma_silence_pcm_frames(p, frameCount, format, channels); }
5918
5919
5920/*
5921Offsets a pointer by the specified number of PCM frames.
5922*/
5923MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels);
5924MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels);
5925static 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); }
5926static 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); }
5927
5928
5929/*
5930Clips f32 samples.
5931*/
5932MA_API void ma_clip_samples_f32(float* p, ma_uint64 sampleCount);
5933static MA_INLINE void ma_clip_pcm_frames_f32(float* p, ma_uint64 frameCount, ma_uint32 channels) { ma_clip_samples_f32(p, frameCount*channels); }
5934
5935/*
5936Helper for applying a volume factor to samples.
5937
5938Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place.
5939*/
5940MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor);
5941MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor);
5942MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor);
5943MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor);
5944MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor);
5945
5946MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor);
5947MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor);
5948MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor);
5949MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor);
5950MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor);
5951
5952MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
5953MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
5954MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
5955MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
5956MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
5957MA_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);
5958
5959MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
5960MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
5961MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
5962MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
5963MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
5964MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor);
5965
5966
5967/*
5968Helper for converting a linear factor to gain in decibels.
5969*/
5970MA_API float ma_factor_to_gain_db(float factor);
5971
5972/*
5973Helper for converting gain in decibels to a linear factor.
5974*/
5975MA_API float ma_gain_db_to_factor(float gain);
5976
5977
5978typedef void ma_data_source;
5979
5980typedef struct
5981{
5982 ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
5983 ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex);
5984 ma_result (* onMap)(ma_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */
5985 ma_result (* onUnmap)(ma_data_source* pDataSource, ma_uint64 frameCount);
5986 ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
5987 ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor);
5988 ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength);
5989} ma_data_source_vtable, ma_data_source_callbacks; /* TODO: Remove ma_data_source_callbacks in version 0.11. */
5990
5991typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource);
5992
5993typedef struct
5994{
5995 const ma_data_source_vtable* vtable; /* Can be null, which is useful for proxies. */
5996} ma_data_source_config;
5997
5998MA_API ma_data_source_config ma_data_source_config_init(void);
5999
6000
6001typedef struct
6002{
6003 ma_data_source_callbacks cb; /* TODO: Remove this. */
6004
6005 /* Variables below are placeholder and not yet used. */
6006 const ma_data_source_vtable* vtable;
6007 ma_uint64 rangeBegInFrames;
6008 ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */
6009 ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */
6010 ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */
6011 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. */
6012 ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */
6013 ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */
6014} ma_data_source_base;
6015
6016MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource);
6017MA_API void ma_data_source_uninit(ma_data_source* pDataSource);
6018MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead, ma_bool32 loop); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */
6019MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked, ma_bool32 loop); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount); */
6020MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex);
6021MA_API ma_result ma_data_source_map(ma_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount); /* Returns MA_NOT_IMPLEMENTED if mapping is not supported. */
6022MA_API ma_result ma_data_source_unmap(ma_data_source* pDataSource, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. */
6023MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
6024MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor);
6025MA_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. */
6026#if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING)
6027MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames);
6028MA_API void ma_data_source_get_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames);
6029MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames);
6030MA_API void ma_data_source_get_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames);
6031MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource);
6032MA_API ma_data_source* ma_data_source_get_current(ma_data_source* pDataSource);
6033MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource);
6034MA_API ma_data_source* ma_data_source_get_next(ma_data_source* pDataSource);
6035MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext);
6036MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(ma_data_source* pDataSource);
6037#endif
6038
6039
6040typedef struct
6041{
6042 ma_data_source_base ds;
6043 ma_format format;
6044 ma_uint32 channels;
6045 ma_uint64 cursor;
6046 ma_uint64 sizeInFrames;
6047 const void* pData;
6048} ma_audio_buffer_ref;
6049
6050MA_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);
6051MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef);
6052MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames);
6053MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop);
6054MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex);
6055MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount);
6056MA_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. */
6057MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef);
6058MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor);
6059MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength);
6060MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames);
6061
6062
6063
6064typedef struct
6065{
6066 ma_format format;
6067 ma_uint32 channels;
6068 ma_uint64 sizeInFrames;
6069 const void* pData; /* If set to NULL, will allocate a block of memory for you. */
6070 ma_allocation_callbacks allocationCallbacks;
6071} ma_audio_buffer_config;
6072
6073MA_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);
6074
6075typedef struct
6076{
6077 ma_audio_buffer_ref ref;
6078 ma_allocation_callbacks allocationCallbacks;
6079 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(). */
6080 ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */
6081} ma_audio_buffer;
6082
6083MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer);
6084MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer);
6085MA_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. */
6086MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer);
6087MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer);
6088MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop);
6089MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex);
6090MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount);
6091MA_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. */
6092MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer);
6093MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor);
6094MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength);
6095MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames);
6096
6097
6098
6099/************************************************************************************************************************************************************
6100
6101VFS
6102===
6103
6104The VFS object (virtual file system) is what's used to customize file access. This is useful in cases where stdio FILE* based APIs may not be entirely
6105appropriate for a given situation.
6106
6107************************************************************************************************************************************************************/
6108typedef void ma_vfs;
6109typedef ma_handle ma_vfs_file;
6110
6111#define MA_OPEN_MODE_READ 0x00000001
6112#define MA_OPEN_MODE_WRITE 0x00000002
6113
6114typedef enum
6115{
6116 ma_seek_origin_start,
6117 ma_seek_origin_current,
6118 ma_seek_origin_end /* Not used by decoders. */
6119} ma_seek_origin;
6120
6121typedef struct
6122{
6123 ma_uint64 sizeInBytes;
6124} ma_file_info;
6125
6126typedef struct
6127{
6128 ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
6129 ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
6130 ma_result (* onClose)(ma_vfs* pVFS, ma_vfs_file file);
6131 ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead);
6132 ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten);
6133 ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin);
6134 ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor);
6135 ma_result (* onInfo) (ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo);
6136} ma_vfs_callbacks;
6137
6138MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
6139MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
6140MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file);
6141MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead);
6142MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten);
6143MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin);
6144MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor);
6145MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo);
6146MA_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);
6147
6148typedef struct
6149{
6150 ma_vfs_callbacks cb;
6151 ma_allocation_callbacks allocationCallbacks; /* Only used for the wchar_t version of open() on non-Windows platforms. */
6152} ma_default_vfs;
6153
6154MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks);
6155
6156
6157
6158typedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead);
6159typedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin);
6160typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor);
6161
6162
6163
6164#if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)
6165typedef enum
6166{
6167 ma_resource_format_wav
6168} ma_resource_format;
6169
6170typedef enum
6171{
6172 ma_encoding_format_unknown = 0,
6173 ma_encoding_format_wav,
6174 ma_encoding_format_flac,
6175 ma_encoding_format_mp3,
6176 ma_encoding_format_vorbis
6177} ma_encoding_format;
6178#endif
6179
6180/************************************************************************************************************************************************************
6181
6182Decoding
6183========
6184
6185Decoders are independent of the main device API. Decoding APIs can be called freely inside the device's data callback, but they are not thread safe unless
6186you do your own synchronization.
6187
6188************************************************************************************************************************************************************/
6189#ifndef MA_NO_DECODING
6190typedef struct ma_decoder ma_decoder;
6191
6192
6193typedef struct
6194{
6195 ma_format preferredFormat;
6196} ma_decoding_backend_config;
6197
6198MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat);
6199
6200
6201typedef struct
6202{
6203 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);
6204 ma_result (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */
6205 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. */
6206 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. */
6207 void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks);
6208 ma_result (* onGetChannelMap)(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap);
6209} ma_decoding_backend_vtable;
6210
6211
6212/* TODO: Convert read and seek to be consistent with the VFS API (ma_result return value, bytes read moved to an output parameter). */
6213typedef size_t (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead); /* Returns the number of bytes read. */
6214typedef ma_bool32 (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin);
6215typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor);
6216
6217typedef struct
6218{
6219 ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */
6220 ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */
6221 ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */
6222 ma_channel channelMap[MA_MAX_CHANNELS];
6223 ma_channel_mix_mode channelMixMode;
6224 ma_dither_mode ditherMode;
6225 struct
6226 {
6227 ma_resample_algorithm algorithm;
6228 struct
6229 {
6230 ma_uint32 lpfOrder;
6231 } linear;
6232 struct
6233 {
6234 int quality;
6235 } speex;
6236 } resampling;
6237 ma_allocation_callbacks allocationCallbacks;
6238 ma_encoding_format encodingFormat;
6239 ma_decoding_backend_vtable** ppCustomBackendVTables;
6240 ma_uint32 customBackendCount;
6241 void* pCustomBackendUserData;
6242} ma_decoder_config;
6243
6244struct ma_decoder
6245{
6246 ma_data_source_base ds;
6247 ma_data_source* pBackend; /* The decoding backend we'll be pulling data from. */
6248 const ma_decoding_backend_vtable* pBackendVTable; /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */
6249 void* pBackendUserData;
6250 ma_decoder_read_proc onRead;
6251 ma_decoder_seek_proc onSeek;
6252 ma_decoder_tell_proc onTell;
6253 void* pUserData;
6254 ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */
6255 ma_format outputFormat;
6256 ma_uint32 outputChannels;
6257 ma_uint32 outputSampleRate;
6258 ma_channel outputChannelMap[MA_MAX_CHANNELS];
6259 ma_data_converter converter; /* <-- Data conversion is achieved by running frames through this. */
6260 ma_allocation_callbacks allocationCallbacks;
6261 union
6262 {
6263 struct
6264 {
6265 ma_vfs* pVFS;
6266 ma_vfs_file file;
6267 } vfs;
6268 struct
6269 {
6270 const ma_uint8* pData;
6271 size_t dataSize;
6272 size_t currentReadPos;
6273 } memory; /* Only used for decoders that were opened against a block of memory. */
6274 } data;
6275};
6276
6277MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate);
6278MA_API ma_decoder_config ma_decoder_config_init_default(void);
6279
6280MA_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);
6281MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6282MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6283MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6284MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6285MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6286
6287/*
6288Uninitializes a decoder.
6289*/
6290MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder);
6291
6292/*
6293Retrieves the current position of the read cursor in PCM frames.
6294*/
6295MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor);
6296
6297/*
6298Retrieves the length of the decoder in PCM frames.
6299
6300Do not call this on streams of an undefined length, such as internet radio.
6301
6302If the length is unknown or an error occurs, 0 will be returned.
6303
6304This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio
6305uses internally.
6306
6307For MP3's, this will decode the entire file. Do not call this in time critical scenarios.
6308
6309This function is not thread safe without your own synchronization.
6310*/
6311MA_API ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder);
6312
6313/*
6314Reads PCM frames from the given decoder.
6315
6316This is not thread safe without your own synchronization.
6317*/
6318MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount);
6319
6320/*
6321Seeks to a PCM frame based on it's absolute index.
6322
6323This is not thread safe without your own synchronization.
6324*/
6325MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex);
6326
6327/*
6328Retrieves the number of frames that can be read before reaching the end.
6329
6330This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in
6331particular ensuring you do not call it on streams of an undefined length, such as internet radio.
6332
6333If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be
6334returned.
6335*/
6336MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames);
6337
6338/*
6339Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input,
6340pConfig should be set to what you want. On output it will be set to what you got.
6341*/
6342MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
6343MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
6344MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
6345
6346
6347
6348
6349/*
6350DEPRECATED
6351
6352Set the "encodingFormat" variable in the decoder config instead:
6353
6354 decoderConfig.encodingFormat = ma_encoding_format_wav;
6355
6356These functions will be removed in version 0.11.
6357*/
6358MA_API ma_result ma_decoder_init_wav(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6359MA_API ma_result ma_decoder_init_flac(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6360MA_API ma_result ma_decoder_init_mp3(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6361MA_API ma_result ma_decoder_init_vorbis(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6362MA_API ma_result ma_decoder_init_memory_wav(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6363MA_API ma_result ma_decoder_init_memory_flac(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6364MA_API ma_result ma_decoder_init_memory_mp3(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6365MA_API ma_result ma_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6366MA_API ma_result ma_decoder_init_vfs_wav(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6367MA_API ma_result ma_decoder_init_vfs_flac(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6368MA_API ma_result ma_decoder_init_vfs_mp3(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6369MA_API ma_result ma_decoder_init_vfs_vorbis(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6370MA_API ma_result ma_decoder_init_vfs_wav_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6371MA_API ma_result ma_decoder_init_vfs_flac_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6372MA_API ma_result ma_decoder_init_vfs_mp3_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6373MA_API ma_result ma_decoder_init_vfs_vorbis_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6374MA_API ma_result ma_decoder_init_file_wav(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6375MA_API ma_result ma_decoder_init_file_flac(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6376MA_API ma_result ma_decoder_init_file_mp3(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6377MA_API ma_result ma_decoder_init_file_vorbis(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6378MA_API ma_result ma_decoder_init_file_wav_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6379MA_API ma_result ma_decoder_init_file_flac_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6380MA_API ma_result ma_decoder_init_file_mp3_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6381MA_API ma_result ma_decoder_init_file_vorbis_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
6382
6383#endif /* MA_NO_DECODING */
6384
6385
6386/************************************************************************************************************************************************************
6387
6388Encoding
6389========
6390
6391Encoders do not perform any format conversion for you. If your target format does not support the format, and error will be returned.
6392
6393************************************************************************************************************************************************************/
6394#ifndef MA_NO_ENCODING
6395typedef struct ma_encoder ma_encoder;
6396
6397typedef size_t (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite); /* Returns the number of bytes written. */
6398typedef ma_bool32 (* ma_encoder_seek_proc) (ma_encoder* pEncoder, int byteOffset, ma_seek_origin origin);
6399typedef ma_result (* ma_encoder_init_proc) (ma_encoder* pEncoder);
6400typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder);
6401typedef ma_uint64 (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount);
6402
6403typedef struct
6404{
6405 ma_resource_format resourceFormat;
6406 ma_format format;
6407 ma_uint32 channels;
6408 ma_uint32 sampleRate;
6409 ma_allocation_callbacks allocationCallbacks;
6410} ma_encoder_config;
6411
6412MA_API ma_encoder_config ma_encoder_config_init(ma_resource_format resourceFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate);
6413
6414struct ma_encoder
6415{
6416 ma_encoder_config config;
6417 ma_encoder_write_proc onWrite;
6418 ma_encoder_seek_proc onSeek;
6419 ma_encoder_init_proc onInit;
6420 ma_encoder_uninit_proc onUninit;
6421 ma_encoder_write_pcm_frames_proc onWritePCMFrames;
6422 void* pUserData;
6423 void* pInternalEncoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */
6424 void* pFile; /* FILE*. Only used when initialized with ma_encoder_init_file(). */
6425};
6426
6427MA_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);
6428MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
6429MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
6430MA_API void ma_encoder_uninit(ma_encoder* pEncoder);
6431MA_API ma_uint64 ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount);
6432
6433#endif /* MA_NO_ENCODING */
6434
6435
6436/************************************************************************************************************************************************************
6437
6438Generation
6439
6440************************************************************************************************************************************************************/
6441#ifndef MA_NO_GENERATION
6442typedef enum
6443{
6444 ma_waveform_type_sine,
6445 ma_waveform_type_square,
6446 ma_waveform_type_triangle,
6447 ma_waveform_type_sawtooth
6448} ma_waveform_type;
6449
6450typedef struct
6451{
6452 ma_format format;
6453 ma_uint32 channels;
6454 ma_uint32 sampleRate;
6455 ma_waveform_type type;
6456 double amplitude;
6457 double frequency;
6458} ma_waveform_config;
6459
6460MA_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);
6461
6462typedef struct
6463{
6464 ma_data_source_base ds;
6465 ma_waveform_config config;
6466 double advance;
6467 double time;
6468} ma_waveform;
6469
6470MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform);
6471MA_API void ma_waveform_uninit(ma_waveform* pWaveform);
6472MA_API ma_uint64 ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount);
6473MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex);
6474MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude);
6475MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency);
6476MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type);
6477MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate);
6478
6479typedef enum
6480{
6481 ma_noise_type_white,
6482 ma_noise_type_pink,
6483 ma_noise_type_brownian
6484} ma_noise_type;
6485
6486typedef struct
6487{
6488 ma_format format;
6489 ma_uint32 channels;
6490 ma_noise_type type;
6491 ma_int32 seed;
6492 double amplitude;
6493 ma_bool32 duplicateChannels;
6494} ma_noise_config;
6495
6496MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude);
6497
6498typedef struct
6499{
6500 ma_data_source_vtable ds;
6501 ma_noise_config config;
6502 ma_lcg lcg;
6503 union
6504 {
6505 struct
6506 {
6507 double bin[MA_MAX_CHANNELS][16];
6508 double accumulation[MA_MAX_CHANNELS];
6509 ma_uint32 counter[MA_MAX_CHANNELS];
6510 } pink;
6511 struct
6512 {
6513 double accumulation[MA_MAX_CHANNELS];
6514 } brownian;
6515 } state;
6516} ma_noise;
6517
6518MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, ma_noise* pNoise);
6519MA_API void ma_noise_uninit(ma_noise* pNoise);
6520MA_API ma_uint64 ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount);
6521MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude);
6522MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed);
6523MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type);
6524
6525#endif /* MA_NO_GENERATION */
6526
6527#ifdef __cplusplus
6528}
6529#endif
6530#endif /* miniaudio_h */
6531
6532
6533
6534/************************************************************************************************************************************************************
6535*************************************************************************************************************************************************************
6536
6537IMPLEMENTATION
6538
6539*************************************************************************************************************************************************************
6540************************************************************************************************************************************************************/
6541#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
6542#ifndef miniaudio_c
6543#define miniaudio_c
6544
6545#include <assert.h>
6546#include <limits.h> /* For INT_MAX */
6547#include <math.h> /* sin(), etc. */
6548
6549#include <stdarg.h>
6550#include <stdio.h>
6551#if !defined(_MSC_VER) && !defined(__DMC__)
6552 #include <strings.h> /* For strcasecmp(). */
6553 #include <wchar.h> /* For wcslen(), wcsrtombs() */
6554#endif
6555
6556#ifdef MA_WIN32
6557#include <windows.h>
6558#else
6559#include <stdlib.h> /* For malloc(), free(), wcstombs(). */
6560#include <string.h> /* For memset() */
6561#include <sched.h>
6562#include <sys/time.h> /* select() (used for ma_sleep()). */
6563#endif
6564
6565#include <sys/stat.h> /* For fstat(), etc. */
6566
6567#ifdef MA_EMSCRIPTEN
6568#include <emscripten/emscripten.h>
6569#endif
6570
6571#if !defined(MA_64BIT) && !defined(MA_32BIT)
6572#ifdef _WIN32
6573#ifdef _WIN64
6574#define MA_64BIT
6575#else
6576#define MA_32BIT
6577#endif
6578#endif
6579#endif
6580
6581#if !defined(MA_64BIT) && !defined(MA_32BIT)
6582#ifdef __GNUC__
6583#ifdef __LP64__
6584#define MA_64BIT
6585#else
6586#define MA_32BIT
6587#endif
6588#endif
6589#endif
6590
6591#if !defined(MA_64BIT) && !defined(MA_32BIT)
6592#include <stdint.h>
6593#if INTPTR_MAX == INT64_MAX
6594#define MA_64BIT
6595#else
6596#define MA_32BIT
6597#endif
6598#endif
6599
6600/* Architecture Detection */
6601#if defined(__x86_64__) || defined(_M_X64)
6602#define MA_X64
6603#elif defined(__i386) || defined(_M_IX86)
6604#define MA_X86
6605#elif defined(__arm__) || defined(_M_ARM)
6606#define MA_ARM
6607#endif
6608
6609/* Cannot currently support AVX-512 if AVX is disabled. */
6610#if !defined(MA_NO_AVX512) && defined(MA_NO_AVX2)
6611#define MA_NO_AVX512
6612#endif
6613
6614/* Intrinsics Support */
6615#if defined(MA_X64) || defined(MA_X86)
6616 #if defined(_MSC_VER) && !defined(__clang__)
6617 /* MSVC. */
6618 #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */
6619 #define MA_SUPPORT_SSE2
6620 #endif
6621 /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */
6622 /* #define MA_SUPPORT_AVX*/
6623 /*#endif*/
6624 #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */
6625 #define MA_SUPPORT_AVX2
6626 #endif
6627 #if _MSC_VER >= 1910 && !defined(MA_NO_AVX512) /* 2017 */
6628 #define MA_SUPPORT_AVX512
6629 #endif
6630 #else
6631 /* Assume GNUC-style. */
6632 #if defined(__SSE2__) && !defined(MA_NO_SSE2)
6633 #define MA_SUPPORT_SSE2
6634 #endif
6635 /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/
6636 /* #define MA_SUPPORT_AVX*/
6637 /*#endif*/
6638 #if defined(__AVX2__) && !defined(MA_NO_AVX2)
6639 #define MA_SUPPORT_AVX2
6640 #endif
6641 #if defined(__AVX512F__) && !defined(MA_NO_AVX512)
6642 #define MA_SUPPORT_AVX512
6643 #endif
6644 #endif
6645
6646 /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */
6647 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
6648 #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include(<emmintrin.h>)
6649 #define MA_SUPPORT_SSE2
6650 #endif
6651 /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include(<immintrin.h>)*/
6652 /* #define MA_SUPPORT_AVX*/
6653 /*#endif*/
6654 #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include(<immintrin.h>)
6655 #define MA_SUPPORT_AVX2
6656 #endif
6657 #if !defined(MA_SUPPORT_AVX512) && !defined(MA_NO_AVX512) && __has_include(<zmmintrin.h>)
6658 #define MA_SUPPORT_AVX512
6659 #endif
6660 #endif
6661
6662 #if defined(MA_SUPPORT_AVX512)
6663 #include <immintrin.h> /* Not a mistake. Intentionally including <immintrin.h> instead of <zmmintrin.h> because otherwise the compiler will complain. */
6664 #elif defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX)
6665 #include <immintrin.h>
6666 #elif defined(MA_SUPPORT_SSE2)
6667 #include <emmintrin.h>
6668 #endif
6669#endif
6670
6671#if defined(MA_ARM)
6672 #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
6673 #define MA_SUPPORT_NEON
6674 #endif
6675
6676 /* Fall back to looking for the #include file. */
6677 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
6678 #if !defined(MA_SUPPORT_NEON) && !defined(MA_NO_NEON) && __has_include(<arm_neon.h>)
6679 #define MA_SUPPORT_NEON
6680 #endif
6681 #endif
6682
6683 #if defined(MA_SUPPORT_NEON)
6684 #include <arm_neon.h>
6685 #endif
6686#endif
6687
6688/* Begin globally disabled warnings. */
6689#if defined(_MSC_VER)
6690 #pragma warning(push)
6691 #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */
6692#endif
6693
6694#if defined(MA_X64) || defined(MA_X86)
6695 #if defined(_MSC_VER) && !defined(__clang__)
6696 #if _MSC_VER >= 1400
6697 #include <intrin.h>
6698 static MA_INLINE void ma_cpuid(int info[4], int fid)
6699 {
6700 __cpuid(info, fid);
6701 }
6702 #else
6703 #define MA_NO_CPUID
6704 #endif
6705
6706 #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219)
6707 static MA_INLINE unsigned __int64 ma_xgetbv(int reg)
6708 {
6709 return _xgetbv(reg);
6710 }
6711 #else
6712 #define MA_NO_XGETBV
6713 #endif
6714 #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID)
6715 static MA_INLINE void ma_cpuid(int info[4], int fid)
6716 {
6717 /*
6718 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
6719 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
6720 supporting different assembly dialects.
6721
6722 What's basically happening is that we're saving and restoring the ebx register manually.
6723 */
6724 #if defined(DRFLAC_X86) && defined(__PIC__)
6725 __asm__ __volatile__ (
6726 "xchg{l} {%%}ebx, %k1;"
6727 "cpuid;"
6728 "xchg{l} {%%}ebx, %k1;"
6729 : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
6730 );
6731 #else
6732 __asm__ __volatile__ (
6733 "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
6734 );
6735 #endif
6736 }
6737
6738 static MA_INLINE ma_uint64 ma_xgetbv(int reg)
6739 {
6740 unsigned int hi;
6741 unsigned int lo;
6742
6743 __asm__ __volatile__ (
6744 "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg)
6745 );
6746
6747 return ((ma_uint64)hi << 32) | (ma_uint64)lo;
6748 }
6749 #else
6750 #define MA_NO_CPUID
6751 #define MA_NO_XGETBV
6752 #endif
6753#else
6754 #define MA_NO_CPUID
6755 #define MA_NO_XGETBV
6756#endif
6757
6758static MA_INLINE ma_bool32 ma_has_sse2(void)
6759{
6760#if defined(MA_SUPPORT_SSE2)
6761 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2)
6762 #if defined(MA_X64)
6763 return MA_TRUE; /* 64-bit targets always support SSE2. */
6764 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
6765 return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */
6766 #else
6767 #if defined(MA_NO_CPUID)
6768 return MA_FALSE;
6769 #else
6770 int info[4];
6771 ma_cpuid(info, 1);
6772 return (info[3] & (1 << 26)) != 0;
6773 #endif
6774 #endif
6775 #else
6776 return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */
6777 #endif
6778#else
6779 return MA_FALSE; /* No compiler support. */
6780#endif
6781}
6782
6783#if 0
6784static MA_INLINE ma_bool32 ma_has_avx()
6785{
6786#if defined(MA_SUPPORT_AVX)
6787 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX)
6788 #if defined(_AVX_) || defined(__AVX__)
6789 return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */
6790 #else
6791 /* AVX requires both CPU and OS support. */
6792 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
6793 return MA_FALSE;
6794 #else
6795 int info[4];
6796 ma_cpuid(info, 1);
6797 if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) {
6798 ma_uint64 xrc = ma_xgetbv(0);
6799 if ((xrc & 0x06) == 0x06) {
6800 return MA_TRUE;
6801 } else {
6802 return MA_FALSE;
6803 }
6804 } else {
6805 return MA_FALSE;
6806 }
6807 #endif
6808 #endif
6809 #else
6810 return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */
6811 #endif
6812#else
6813 return MA_FALSE; /* No compiler support. */
6814#endif
6815}
6816#endif
6817
6818static MA_INLINE ma_bool32 ma_has_avx2(void)
6819{
6820#if defined(MA_SUPPORT_AVX2)
6821 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2)
6822 #if defined(_AVX2_) || defined(__AVX2__)
6823 return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */
6824 #else
6825 /* AVX2 requires both CPU and OS support. */
6826 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
6827 return MA_FALSE;
6828 #else
6829 int info1[4];
6830 int info7[4];
6831 ma_cpuid(info1, 1);
6832 ma_cpuid(info7, 7);
6833 if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) {
6834 ma_uint64 xrc = ma_xgetbv(0);
6835 if ((xrc & 0x06) == 0x06) {
6836 return MA_TRUE;
6837 } else {
6838 return MA_FALSE;
6839 }
6840 } else {
6841 return MA_FALSE;
6842 }
6843 #endif
6844 #endif
6845 #else
6846 return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */
6847 #endif
6848#else
6849 return MA_FALSE; /* No compiler support. */
6850#endif
6851}
6852
6853static MA_INLINE ma_bool32 ma_has_avx512f(void)
6854{
6855#if defined(MA_SUPPORT_AVX512)
6856 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX512)
6857 #if defined(__AVX512F__)
6858 return MA_TRUE; /* If the compiler is allowed to freely generate AVX-512F code we can assume support. */
6859 #else
6860 /* AVX-512 requires both CPU and OS support. */
6861 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
6862 return MA_FALSE;
6863 #else
6864 int info1[4];
6865 int info7[4];
6866 ma_cpuid(info1, 1);
6867 ma_cpuid(info7, 7);
6868 if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 16)) != 0)) {
6869 ma_uint64 xrc = ma_xgetbv(0);
6870 if ((xrc & 0xE6) == 0xE6) {
6871 return MA_TRUE;
6872 } else {
6873 return MA_FALSE;
6874 }
6875 } else {
6876 return MA_FALSE;
6877 }
6878 #endif
6879 #endif
6880 #else
6881 return MA_FALSE; /* AVX-512F is only supported on x86 and x64 architectures. */
6882 #endif
6883#else
6884 return MA_FALSE; /* No compiler support. */
6885#endif
6886}
6887
6888static MA_INLINE ma_bool32 ma_has_neon(void)
6889{
6890#if defined(MA_SUPPORT_NEON)
6891 #if defined(MA_ARM) && !defined(MA_NO_NEON)
6892 #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
6893 return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */
6894 #else
6895 /* TODO: Runtime check. */
6896 return MA_FALSE;
6897 #endif
6898 #else
6899 return MA_FALSE; /* NEON is only supported on ARM architectures. */
6900 #endif
6901#else
6902 return MA_FALSE; /* No compiler support. */
6903#endif
6904}
6905
6906#define MA_SIMD_NONE 0
6907#define MA_SIMD_SSE2 1
6908#define MA_SIMD_AVX2 2
6909#define MA_SIMD_NEON 3
6910
6911#ifndef MA_PREFERRED_SIMD
6912 # if defined(MA_SUPPORT_SSE2) && defined(MA_PREFER_SSE2)
6913 #define MA_PREFERRED_SIMD MA_SIMD_SSE2
6914 #elif defined(MA_SUPPORT_AVX2) && defined(MA_PREFER_AVX2)
6915 #define MA_PREFERRED_SIMD MA_SIMD_AVX2
6916 #elif defined(MA_SUPPORT_NEON) && defined(MA_PREFER_NEON)
6917 #define MA_PREFERRED_SIMD MA_SIMD_NEON
6918 #else
6919 #define MA_PREFERRED_SIMD MA_SIMD_NONE
6920 #endif
6921#endif
6922
6923#if defined(__has_builtin)
6924 #define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x)
6925#else
6926 #define MA_COMPILER_HAS_BUILTIN(x) 0
6927#endif
6928
6929#ifndef MA_ASSUME
6930 #if MA_COMPILER_HAS_BUILTIN(__builtin_assume)
6931 #define MA_ASSUME(x) __builtin_assume(x)
6932 #elif MA_COMPILER_HAS_BUILTIN(__builtin_unreachable)
6933 #define MA_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0)
6934 #elif defined(_MSC_VER)
6935 #define MA_ASSUME(x) __assume(x)
6936 #else
6937 #define MA_ASSUME(x) while(0)
6938 #endif
6939#endif
6940
6941#ifndef MA_RESTRICT
6942 #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER)
6943 #define MA_RESTRICT __restrict
6944 #else
6945 #define MA_RESTRICT
6946 #endif
6947#endif
6948
6949#if defined(_MSC_VER) && _MSC_VER >= 1400
6950 #define MA_HAS_BYTESWAP16_INTRINSIC
6951 #define MA_HAS_BYTESWAP32_INTRINSIC
6952 #define MA_HAS_BYTESWAP64_INTRINSIC
6953#elif defined(__clang__)
6954 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap16)
6955 #define MA_HAS_BYTESWAP16_INTRINSIC
6956 #endif
6957 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap32)
6958 #define MA_HAS_BYTESWAP32_INTRINSIC
6959 #endif
6960 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap64)
6961 #define MA_HAS_BYTESWAP64_INTRINSIC
6962 #endif
6963#elif defined(__GNUC__)
6964 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
6965 #define MA_HAS_BYTESWAP32_INTRINSIC
6966 #define MA_HAS_BYTESWAP64_INTRINSIC
6967 #endif
6968 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
6969 #define MA_HAS_BYTESWAP16_INTRINSIC
6970 #endif
6971#endif
6972
6973
6974static MA_INLINE ma_bool32 ma_is_little_endian(void)
6975{
6976#if defined(MA_X86) || defined(MA_X64)
6977 return MA_TRUE;
6978#else
6979 int n = 1;
6980 return (*(char*)&n) == 1;
6981#endif
6982}
6983
6984static MA_INLINE ma_bool32 ma_is_big_endian(void)
6985{
6986 return !ma_is_little_endian();
6987}
6988
6989
6990static MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n)
6991{
6992#ifdef MA_HAS_BYTESWAP32_INTRINSIC
6993 #if defined(_MSC_VER)
6994 return _byteswap_ulong(n);
6995 #elif defined(__GNUC__) || defined(__clang__)
6996 #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. */
6997 /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */
6998 ma_uint32 r;
6999 __asm__ __volatile__ (
7000 #if defined(MA_64BIT)
7001 "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! */
7002 #else
7003 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
7004 #endif
7005 );
7006 return r;
7007 #else
7008 return __builtin_bswap32(n);
7009 #endif
7010 #else
7011 #error "This compiler does not support the byte swap intrinsic."
7012 #endif
7013#else
7014 return ((n & 0xFF000000) >> 24) |
7015 ((n & 0x00FF0000) >> 8) |
7016 ((n & 0x0000FF00) << 8) |
7017 ((n & 0x000000FF) << 24);
7018#endif
7019}
7020
7021
7022#if !defined(MA_EMSCRIPTEN)
7023#ifdef MA_WIN32
7024static void ma_sleep__win32(ma_uint32 milliseconds)
7025{
7026 Sleep((DWORD)milliseconds);
7027}
7028#endif
7029#ifdef MA_POSIX
7030static void ma_sleep__posix(ma_uint32 milliseconds)
7031{
7032#ifdef MA_EMSCRIPTEN
7033 (void)milliseconds;
7034 MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */
7035#else
7036 #if _POSIX_C_SOURCE >= 199309L
7037 struct timespec ts;
7038 ts.tv_sec = milliseconds / 1000;
7039 ts.tv_nsec = milliseconds % 1000 * 1000000;
7040 nanosleep(&ts, NULL);
7041 #else
7042 struct timeval tv;
7043 tv.tv_sec = milliseconds / 1000;
7044 tv.tv_usec = milliseconds % 1000 * 1000;
7045 select(0, NULL, NULL, NULL, &tv);
7046 #endif
7047#endif
7048}
7049#endif
7050
7051static void ma_sleep(ma_uint32 milliseconds)
7052{
7053#ifdef MA_WIN32
7054 ma_sleep__win32(milliseconds);
7055#endif
7056#ifdef MA_POSIX
7057 ma_sleep__posix(milliseconds);
7058#endif
7059}
7060#endif
7061
7062static MA_INLINE void ma_yield()
7063{
7064#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
7065 /* x86/x64 */
7066 #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__)
7067 #if _MSC_VER >= 1400
7068 _mm_pause();
7069 #else
7070 #if defined(__DMC__)
7071 /* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */
7072 __asm nop;
7073 #else
7074 __asm pause;
7075 #endif
7076 #endif
7077 #else
7078 __asm__ __volatile__ ("pause");
7079 #endif
7080#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__)
7081 /* ARM */
7082 #if defined(_MSC_VER)
7083 /* 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. */
7084 __yield();
7085 #else
7086 __asm__ __volatile__ ("yield"); /* ARMv6K/ARMv6T2 and above. */
7087 #endif
7088#else
7089 /* Unknown or unsupported architecture. No-op. */
7090#endif
7091}
7092
7093
7094
7095#ifndef MA_COINIT_VALUE
7096#define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */
7097#endif
7098
7099
7100#ifndef MA_FLT_MAX
7101 #ifdef FLT_MAX
7102 #define MA_FLT_MAX FLT_MAX
7103 #else
7104 #define MA_FLT_MAX 3.402823466e+38F
7105 #endif
7106#endif
7107
7108
7109#ifndef MA_PI
7110#define MA_PI 3.14159265358979323846264f
7111#endif
7112#ifndef MA_PI_D
7113#define MA_PI_D 3.14159265358979323846264
7114#endif
7115#ifndef MA_TAU
7116#define MA_TAU 6.28318530717958647693f
7117#endif
7118#ifndef MA_TAU_D
7119#define MA_TAU_D 6.28318530717958647693
7120#endif
7121
7122
7123/* The default format when ma_format_unknown (0) is requested when initializing a device. */
7124#ifndef MA_DEFAULT_FORMAT
7125#define MA_DEFAULT_FORMAT ma_format_f32
7126#endif
7127
7128/* The default channel count to use when 0 is used when initializing a device. */
7129#ifndef MA_DEFAULT_CHANNELS
7130#define MA_DEFAULT_CHANNELS 2
7131#endif
7132
7133/* The default sample rate to use when 0 is used when initializing a device. */
7134#ifndef MA_DEFAULT_SAMPLE_RATE
7135#define MA_DEFAULT_SAMPLE_RATE 48000
7136#endif
7137
7138/* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */
7139#ifndef MA_DEFAULT_PERIODS
7140#define MA_DEFAULT_PERIODS 3
7141#endif
7142
7143/* The default period size in milliseconds for low latency mode. */
7144#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY
7145#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10
7146#endif
7147
7148/* The default buffer size in milliseconds for conservative mode. */
7149#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE
7150#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100
7151#endif
7152
7153/* The default LPF filter order for linear resampling. Note that this is clamped to MA_MAX_FILTER_ORDER. */
7154#ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER
7155 #if MA_MAX_FILTER_ORDER >= 4
7156 #define MA_DEFAULT_RESAMPLER_LPF_ORDER 4
7157 #else
7158 #define MA_DEFAULT_RESAMPLER_LPF_ORDER MA_MAX_FILTER_ORDER
7159 #endif
7160#endif
7161
7162
7163#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
7164 #pragma GCC diagnostic push
7165 #pragma GCC diagnostic ignored "-Wunused-variable"
7166#endif
7167
7168/* Standard sample rates, in order of priority. */
7169static ma_uint32 g_maStandardSampleRatePriorities[] = {
7170 (ma_uint32)ma_standard_sample_rate_48000,
7171 (ma_uint32)ma_standard_sample_rate_44100,
7172
7173 (ma_uint32)ma_standard_sample_rate_32000,
7174 (ma_uint32)ma_standard_sample_rate_24000,
7175 (ma_uint32)ma_standard_sample_rate_22050,
7176
7177 (ma_uint32)ma_standard_sample_rate_88200,
7178 (ma_uint32)ma_standard_sample_rate_96000,
7179 (ma_uint32)ma_standard_sample_rate_176400,
7180 (ma_uint32)ma_standard_sample_rate_192000,
7181
7182 (ma_uint32)ma_standard_sample_rate_16000,
7183 (ma_uint32)ma_standard_sample_rate_11025,
7184 (ma_uint32)ma_standard_sample_rate_8000,
7185
7186 (ma_uint32)ma_standard_sample_rate_352800,
7187 (ma_uint32)ma_standard_sample_rate_384000
7188};
7189
7190static MA_INLINE ma_bool32 ma_is_standard_sample_rate(ma_uint32 sampleRate)
7191{
7192 ma_uint32 iSampleRate;
7193
7194 for (iSampleRate = 0; iSampleRate < sizeof(g_maStandardSampleRatePriorities) / sizeof(g_maStandardSampleRatePriorities[0]); iSampleRate += 1) {
7195 if (g_maStandardSampleRatePriorities[iSampleRate] == sampleRate) {
7196 return MA_TRUE;
7197 }
7198 }
7199
7200 /* Getting here means the sample rate is not supported. */
7201 return MA_FALSE;
7202}
7203
7204
7205static ma_format g_maFormatPriorities[] = {
7206 ma_format_s16, /* Most common */
7207 ma_format_f32,
7208
7209 /*ma_format_s24_32,*/ /* Clean alignment */
7210 ma_format_s32,
7211
7212 ma_format_s24, /* Unclean alignment */
7213
7214 ma_format_u8 /* Low quality */
7215};
7216#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
7217 #pragma GCC diagnostic pop
7218#endif
7219
7220
7221MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)
7222{
7223 if (pMajor) {
7224 *pMajor = MA_VERSION_MAJOR;
7225 }
7226
7227 if (pMinor) {
7228 *pMinor = MA_VERSION_MINOR;
7229 }
7230
7231 if (pRevision) {
7232 *pRevision = MA_VERSION_REVISION;
7233 }
7234}
7235
7236MA_API const char* ma_version_string(void)
7237{
7238 return MA_VERSION_STRING;
7239}
7240
7241
7242/******************************************************************************
7243
7244Standard Library Stuff
7245
7246******************************************************************************/
7247#ifndef MA_MALLOC
7248#ifdef MA_WIN32
7249#define MA_MALLOC(sz) HeapAlloc(GetProcessHeap(), 0, (sz))
7250#else
7251#define MA_MALLOC(sz) malloc((sz))
7252#endif
7253#endif
7254
7255#ifndef MA_REALLOC
7256#ifdef MA_WIN32
7257#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)))
7258#else
7259#define MA_REALLOC(p, sz) realloc((p), (sz))
7260#endif
7261#endif
7262
7263#ifndef MA_FREE
7264#ifdef MA_WIN32
7265#define MA_FREE(p) HeapFree(GetProcessHeap(), 0, (p))
7266#else
7267#define MA_FREE(p) free((p))
7268#endif
7269#endif
7270
7271#ifndef MA_ZERO_MEMORY
7272#ifdef MA_WIN32
7273#define MA_ZERO_MEMORY(p, sz) ZeroMemory((p), (sz))
7274#else
7275#define MA_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
7276#endif
7277#endif
7278
7279#ifndef MA_COPY_MEMORY
7280#ifdef MA_WIN32
7281#define MA_COPY_MEMORY(dst, src, sz) CopyMemory((dst), (src), (sz))
7282#else
7283#define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
7284#endif
7285#endif
7286
7287#ifndef MA_MOVE_MEMORY
7288#ifdef MA_WIN32
7289#define MA_MOVE_MEMORY(dst, src, sz) MoveMemory((dst), (src), (sz))
7290#else
7291#define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz))
7292#endif
7293#endif
7294
7295#ifndef MA_ASSERT
7296#ifdef MA_WIN32
7297#define MA_ASSERT(condition) assert(condition)
7298#else
7299#define MA_ASSERT(condition) assert(condition)
7300#endif
7301#endif
7302
7303#define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p)))
7304
7305#define ma_countof(x) (sizeof(x) / sizeof(x[0]))
7306#define ma_max(x, y) (((x) > (y)) ? (x) : (y))
7307#define ma_min(x, y) (((x) < (y)) ? (x) : (y))
7308#define ma_abs(x) (((x) > 0) ? (x) : -(x))
7309#define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi)))
7310#define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset))
7311#define ma_align(x, a) ((x + (a-1)) & ~(a-1))
7312#define ma_align_64(x) ma_align(x, 8)
7313
7314#define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels))
7315
7316static MA_INLINE double ma_sind(double x)
7317{
7318 /* TODO: Implement custom sin(x). */
7319 return sin(x);
7320}
7321
7322static MA_INLINE double ma_expd(double x)
7323{
7324 /* TODO: Implement custom exp(x). */
7325 return exp(x);
7326}
7327
7328static MA_INLINE double ma_logd(double x)
7329{
7330 /* TODO: Implement custom log(x). */
7331 return log(x);
7332}
7333
7334static MA_INLINE double ma_powd(double x, double y)
7335{
7336 /* TODO: Implement custom pow(x, y). */
7337 return pow(x, y);
7338}
7339
7340static MA_INLINE double ma_sqrtd(double x)
7341{
7342 /* TODO: Implement custom sqrt(x). */
7343 return sqrt(x);
7344}
7345
7346
7347static MA_INLINE double ma_cosd(double x)
7348{
7349 return ma_sind((MA_PI_D*0.5) - x);
7350}
7351
7352static MA_INLINE double ma_log10d(double x)
7353{
7354 return ma_logd(x) * 0.43429448190325182765;
7355}
7356
7357static MA_INLINE float ma_powf(float x, float y)
7358{
7359 return (float)ma_powd((double)x, (double)y);
7360}
7361
7362static MA_INLINE float ma_log10f(float x)
7363{
7364 return (float)ma_log10d((double)x);
7365}
7366
7367
7368static MA_INLINE double ma_degrees_to_radians(double degrees)
7369{
7370 return degrees * 0.01745329252;
7371}
7372
7373static MA_INLINE double ma_radians_to_degrees(double radians)
7374{
7375 return radians * 57.295779512896;
7376}
7377
7378static MA_INLINE float ma_degrees_to_radians_f(float degrees)
7379{
7380 return degrees * 0.01745329252f;
7381}
7382
7383static MA_INLINE float ma_radians_to_degrees_f(float radians)
7384{
7385 return radians * 57.295779512896f;
7386}
7387
7388
7389/*
7390Return Values:
7391 0: Success
7392 22: EINVAL
7393 34: ERANGE
7394
7395Not using symbolic constants for errors because I want to avoid #including errno.h
7396*/
7397MA_API int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
7398{
7399 size_t i;
7400
7401 if (dst == 0) {
7402 return 22;
7403 }
7404 if (dstSizeInBytes == 0) {
7405 return 34;
7406 }
7407 if (src == 0) {
7408 dst[0] = '\0';
7409 return 22;
7410 }
7411
7412 for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
7413 dst[i] = src[i];
7414 }
7415
7416 if (i < dstSizeInBytes) {
7417 dst[i] = '\0';
7418 return 0;
7419 }
7420
7421 dst[0] = '\0';
7422 return 34;
7423}
7424
7425MA_API int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src)
7426{
7427 size_t i;
7428
7429 if (dst == 0) {
7430 return 22;
7431 }
7432 if (dstCap == 0) {
7433 return 34;
7434 }
7435 if (src == 0) {
7436 dst[0] = '\0';
7437 return 22;
7438 }
7439
7440 for (i = 0; i < dstCap && src[i] != '\0'; ++i) {
7441 dst[i] = src[i];
7442 }
7443
7444 if (i < dstCap) {
7445 dst[i] = '\0';
7446 return 0;
7447 }
7448
7449 dst[0] = '\0';
7450 return 34;
7451}
7452
7453
7454MA_API int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
7455{
7456 size_t maxcount;
7457 size_t i;
7458
7459 if (dst == 0) {
7460 return 22;
7461 }
7462 if (dstSizeInBytes == 0) {
7463 return 34;
7464 }
7465 if (src == 0) {
7466 dst[0] = '\0';
7467 return 22;
7468 }
7469
7470 maxcount = count;
7471 if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */
7472 maxcount = dstSizeInBytes - 1;
7473 }
7474
7475 for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
7476 dst[i] = src[i];
7477 }
7478
7479 if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
7480 dst[i] = '\0';
7481 return 0;
7482 }
7483
7484 dst[0] = '\0';
7485 return 34;
7486}
7487
7488MA_API int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
7489{
7490 char* dstorig;
7491
7492 if (dst == 0) {
7493 return 22;
7494 }
7495 if (dstSizeInBytes == 0) {
7496 return 34;
7497 }
7498 if (src == 0) {
7499 dst[0] = '\0';
7500 return 22;
7501 }
7502
7503 dstorig = dst;
7504
7505 while (dstSizeInBytes > 0 && dst[0] != '\0') {
7506 dst += 1;
7507 dstSizeInBytes -= 1;
7508 }
7509
7510 if (dstSizeInBytes == 0) {
7511 return 22; /* Unterminated. */
7512 }
7513
7514
7515 while (dstSizeInBytes > 0 && src[0] != '\0') {
7516 *dst++ = *src++;
7517 dstSizeInBytes -= 1;
7518 }
7519
7520 if (dstSizeInBytes > 0) {
7521 dst[0] = '\0';
7522 } else {
7523 dstorig[0] = '\0';
7524 return 34;
7525 }
7526
7527 return 0;
7528}
7529
7530MA_API int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
7531{
7532 char* dstorig;
7533
7534 if (dst == 0) {
7535 return 22;
7536 }
7537 if (dstSizeInBytes == 0) {
7538 return 34;
7539 }
7540 if (src == 0) {
7541 return 22;
7542 }
7543
7544 dstorig = dst;
7545
7546 while (dstSizeInBytes > 0 && dst[0] != '\0') {
7547 dst += 1;
7548 dstSizeInBytes -= 1;
7549 }
7550
7551 if (dstSizeInBytes == 0) {
7552 return 22; /* Unterminated. */
7553 }
7554
7555
7556 if (count == ((size_t)-1)) { /* _TRUNCATE */
7557 count = dstSizeInBytes - 1;
7558 }
7559
7560 while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) {
7561 *dst++ = *src++;
7562 dstSizeInBytes -= 1;
7563 count -= 1;
7564 }
7565
7566 if (dstSizeInBytes > 0) {
7567 dst[0] = '\0';
7568 } else {
7569 dstorig[0] = '\0';
7570 return 34;
7571 }
7572
7573 return 0;
7574}
7575
7576MA_API int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix)
7577{
7578 int sign;
7579 unsigned int valueU;
7580 char* dstEnd;
7581
7582 if (dst == NULL || dstSizeInBytes == 0) {
7583 return 22;
7584 }
7585 if (radix < 2 || radix > 36) {
7586 dst[0] = '\0';
7587 return 22;
7588 }
7589
7590 sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */
7591
7592 if (value < 0) {
7593 valueU = -value;
7594 } else {
7595 valueU = value;
7596 }
7597
7598 dstEnd = dst;
7599 do
7600 {
7601 int remainder = valueU % radix;
7602 if (remainder > 9) {
7603 *dstEnd = (char)((remainder - 10) + 'a');
7604 } else {
7605 *dstEnd = (char)(remainder + '0');
7606 }
7607
7608 dstEnd += 1;
7609 dstSizeInBytes -= 1;
7610 valueU /= radix;
7611 } while (dstSizeInBytes > 0 && valueU > 0);
7612
7613 if (dstSizeInBytes == 0) {
7614 dst[0] = '\0';
7615 return 22; /* Ran out of room in the output buffer. */
7616 }
7617
7618 if (sign < 0) {
7619 *dstEnd++ = '-';
7620 dstSizeInBytes -= 1;
7621 }
7622
7623 if (dstSizeInBytes == 0) {
7624 dst[0] = '\0';
7625 return 22; /* Ran out of room in the output buffer. */
7626 }
7627
7628 *dstEnd = '\0';
7629
7630
7631 /* At this point the string will be reversed. */
7632 dstEnd -= 1;
7633 while (dst < dstEnd) {
7634 char temp = *dst;
7635 *dst = *dstEnd;
7636 *dstEnd = temp;
7637
7638 dst += 1;
7639 dstEnd -= 1;
7640 }
7641
7642 return 0;
7643}
7644
7645MA_API int ma_strcmp(const char* str1, const char* str2)
7646{
7647 if (str1 == str2) return 0;
7648
7649 /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */
7650 if (str1 == NULL) return -1;
7651 if (str2 == NULL) return 1;
7652
7653 for (;;) {
7654 if (str1[0] == '\0') {
7655 break;
7656 }
7657 if (str1[0] != str2[0]) {
7658 break;
7659 }
7660
7661 str1 += 1;
7662 str2 += 1;
7663 }
7664
7665 return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0];
7666}
7667
7668MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB)
7669{
7670 int result;
7671
7672 result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1);
7673 if (result != 0) {
7674 return result;
7675 }
7676
7677 result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1);
7678 if (result != 0) {
7679 return result;
7680 }
7681
7682 return result;
7683}
7684
7685MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks)
7686{
7687 size_t sz = strlen(src)+1;
7688 char* dst = (char*)ma_malloc(sz, pAllocationCallbacks);
7689 if (dst == NULL) {
7690 return NULL;
7691 }
7692
7693 ma_strcpy_s(dst, sz, src);
7694
7695 return dst;
7696}
7697
7698MA_API wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks)
7699{
7700 size_t sz = wcslen(src)+1;
7701 wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks);
7702 if (dst == NULL) {
7703 return NULL;
7704 }
7705
7706 ma_wcscpy_s(dst, sz, src);
7707
7708 return dst;
7709}
7710
7711
7712#include <errno.h>
7713static ma_result ma_result_from_errno(int e)
7714{
7715 switch (e)
7716 {
7717 case 0: return MA_SUCCESS;
7718 #ifdef EPERM
7719 case EPERM: return MA_INVALID_OPERATION;
7720 #endif
7721 #ifdef ENOENT
7722 case ENOENT: return MA_DOES_NOT_EXIST;
7723 #endif
7724 #ifdef ESRCH
7725 case ESRCH: return MA_DOES_NOT_EXIST;
7726 #endif
7727 #ifdef EINTR
7728 case EINTR: return MA_INTERRUPT;
7729 #endif
7730 #ifdef EIO
7731 case EIO: return MA_IO_ERROR;
7732 #endif
7733 #ifdef ENXIO
7734 case ENXIO: return MA_DOES_NOT_EXIST;
7735 #endif
7736 #ifdef E2BIG
7737 case E2BIG: return MA_INVALID_ARGS;
7738 #endif
7739 #ifdef ENOEXEC
7740 case ENOEXEC: return MA_INVALID_FILE;
7741 #endif
7742 #ifdef EBADF
7743 case EBADF: return MA_INVALID_FILE;
7744 #endif
7745 #ifdef ECHILD
7746 case ECHILD: return MA_ERROR;
7747 #endif
7748 #ifdef EAGAIN
7749 case EAGAIN: return MA_UNAVAILABLE;
7750 #endif
7751 #ifdef ENOMEM
7752 case ENOMEM: return MA_OUT_OF_MEMORY;
7753 #endif
7754 #ifdef EACCES
7755 case EACCES: return MA_ACCESS_DENIED;
7756 #endif
7757 #ifdef EFAULT
7758 case EFAULT: return MA_BAD_ADDRESS;
7759 #endif
7760 #ifdef ENOTBLK
7761 case ENOTBLK: return MA_ERROR;
7762 #endif
7763 #ifdef EBUSY
7764 case EBUSY: return MA_BUSY;
7765 #endif
7766 #ifdef EEXIST
7767 case EEXIST: return MA_ALREADY_EXISTS;
7768 #endif
7769 #ifdef EXDEV
7770 case EXDEV: return MA_ERROR;
7771 #endif
7772 #ifdef ENODEV
7773 case ENODEV: return MA_DOES_NOT_EXIST;
7774 #endif
7775 #ifdef ENOTDIR
7776 case ENOTDIR: return MA_NOT_DIRECTORY;
7777 #endif
7778 #ifdef EISDIR
7779 case EISDIR: return MA_IS_DIRECTORY;
7780 #endif
7781 #ifdef EINVAL
7782 case EINVAL: return MA_INVALID_ARGS;
7783 #endif
7784 #ifdef ENFILE
7785 case ENFILE: return MA_TOO_MANY_OPEN_FILES;
7786 #endif
7787 #ifdef EMFILE
7788 case EMFILE: return MA_TOO_MANY_OPEN_FILES;
7789 #endif
7790 #ifdef ENOTTY
7791 case ENOTTY: return MA_INVALID_OPERATION;
7792 #endif
7793 #ifdef ETXTBSY
7794 case ETXTBSY: return MA_BUSY;
7795 #endif
7796 #ifdef EFBIG
7797 case EFBIG: return MA_TOO_BIG;
7798 #endif
7799 #ifdef ENOSPC
7800 case ENOSPC: return MA_NO_SPACE;
7801 #endif
7802 #ifdef ESPIPE
7803 case ESPIPE: return MA_BAD_SEEK;
7804 #endif
7805 #ifdef EROFS
7806 case EROFS: return MA_ACCESS_DENIED;
7807 #endif
7808 #ifdef EMLINK
7809 case EMLINK: return MA_TOO_MANY_LINKS;
7810 #endif
7811 #ifdef EPIPE
7812 case EPIPE: return MA_BAD_PIPE;
7813 #endif
7814 #ifdef EDOM
7815 case EDOM: return MA_OUT_OF_RANGE;
7816 #endif
7817 #ifdef ERANGE
7818 case ERANGE: return MA_OUT_OF_RANGE;
7819 #endif
7820 #ifdef EDEADLK
7821 case EDEADLK: return MA_DEADLOCK;
7822 #endif
7823 #ifdef ENAMETOOLONG
7824 case ENAMETOOLONG: return MA_PATH_TOO_LONG;
7825 #endif
7826 #ifdef ENOLCK
7827 case ENOLCK: return MA_ERROR;
7828 #endif
7829 #ifdef ENOSYS
7830 case ENOSYS: return MA_NOT_IMPLEMENTED;
7831 #endif
7832 #ifdef ENOTEMPTY
7833 case ENOTEMPTY: return MA_DIRECTORY_NOT_EMPTY;
7834 #endif
7835 #ifdef ELOOP
7836 case ELOOP: return MA_TOO_MANY_LINKS;
7837 #endif
7838 #ifdef ENOMSG
7839 case ENOMSG: return MA_NO_MESSAGE;
7840 #endif
7841 #ifdef EIDRM
7842 case EIDRM: return MA_ERROR;
7843 #endif
7844 #ifdef ECHRNG
7845 case ECHRNG: return MA_ERROR;
7846 #endif
7847 #ifdef EL2NSYNC
7848 case EL2NSYNC: return MA_ERROR;
7849 #endif
7850 #ifdef EL3HLT
7851 case EL3HLT: return MA_ERROR;
7852 #endif
7853 #ifdef EL3RST
7854 case EL3RST: return MA_ERROR;
7855 #endif
7856 #ifdef ELNRNG
7857 case ELNRNG: return MA_OUT_OF_RANGE;
7858 #endif
7859 #ifdef EUNATCH
7860 case EUNATCH: return MA_ERROR;
7861 #endif
7862 #ifdef ENOCSI
7863 case ENOCSI: return MA_ERROR;
7864 #endif
7865 #ifdef EL2HLT
7866 case EL2HLT: return MA_ERROR;
7867 #endif
7868 #ifdef EBADE
7869 case EBADE: return MA_ERROR;
7870 #endif
7871 #ifdef EBADR
7872 case EBADR: return MA_ERROR;
7873 #endif
7874 #ifdef EXFULL
7875 case EXFULL: return MA_ERROR;
7876 #endif
7877 #ifdef ENOANO
7878 case ENOANO: return MA_ERROR;
7879 #endif
7880 #ifdef EBADRQC
7881 case EBADRQC: return MA_ERROR;
7882 #endif
7883 #ifdef EBADSLT
7884 case EBADSLT: return MA_ERROR;
7885 #endif
7886 #ifdef EBFONT
7887 case EBFONT: return MA_INVALID_FILE;
7888 #endif
7889 #ifdef ENOSTR
7890 case ENOSTR: return MA_ERROR;
7891 #endif
7892 #ifdef ENODATA
7893 case ENODATA: return MA_NO_DATA_AVAILABLE;
7894 #endif
7895 #ifdef ETIME
7896 case ETIME: return MA_TIMEOUT;
7897 #endif
7898 #ifdef ENOSR
7899 case ENOSR: return MA_NO_DATA_AVAILABLE;
7900 #endif
7901 #ifdef ENONET
7902 case ENONET: return MA_NO_NETWORK;
7903 #endif
7904 #ifdef ENOPKG
7905 case ENOPKG: return MA_ERROR;
7906 #endif
7907 #ifdef EREMOTE
7908 case EREMOTE: return MA_ERROR;
7909 #endif
7910 #ifdef ENOLINK
7911 case ENOLINK: return MA_ERROR;
7912 #endif
7913 #ifdef EADV
7914 case EADV: return MA_ERROR;
7915 #endif
7916 #ifdef ESRMNT
7917 case ESRMNT: return MA_ERROR;
7918 #endif
7919 #ifdef ECOMM
7920 case ECOMM: return MA_ERROR;
7921 #endif
7922 #ifdef EPROTO
7923 case EPROTO: return MA_ERROR;
7924 #endif
7925 #ifdef EMULTIHOP
7926 case EMULTIHOP: return MA_ERROR;
7927 #endif
7928 #ifdef EDOTDOT
7929 case EDOTDOT: return MA_ERROR;
7930 #endif
7931 #ifdef EBADMSG
7932 case EBADMSG: return MA_BAD_MESSAGE;
7933 #endif
7934 #ifdef EOVERFLOW
7935 case EOVERFLOW: return MA_TOO_BIG;
7936 #endif
7937 #ifdef ENOTUNIQ
7938 case ENOTUNIQ: return MA_NOT_UNIQUE;
7939 #endif
7940 #ifdef EBADFD
7941 case EBADFD: return MA_ERROR;
7942 #endif
7943 #ifdef EREMCHG
7944 case EREMCHG: return MA_ERROR;
7945 #endif
7946 #ifdef ELIBACC
7947 case ELIBACC: return MA_ACCESS_DENIED;
7948 #endif
7949 #ifdef ELIBBAD
7950 case ELIBBAD: return MA_INVALID_FILE;
7951 #endif
7952 #ifdef ELIBSCN
7953 case ELIBSCN: return MA_INVALID_FILE;
7954 #endif
7955 #ifdef ELIBMAX
7956 case ELIBMAX: return MA_ERROR;
7957 #endif
7958 #ifdef ELIBEXEC
7959 case ELIBEXEC: return MA_ERROR;
7960 #endif
7961 #ifdef EILSEQ
7962 case EILSEQ: return MA_INVALID_DATA;
7963 #endif
7964 #ifdef ERESTART
7965 case ERESTART: return MA_ERROR;
7966 #endif
7967 #ifdef ESTRPIPE
7968 case ESTRPIPE: return MA_ERROR;
7969 #endif
7970 #ifdef EUSERS
7971 case EUSERS: return MA_ERROR;
7972 #endif
7973 #ifdef ENOTSOCK
7974 case ENOTSOCK: return MA_NOT_SOCKET;
7975 #endif
7976 #ifdef EDESTADDRREQ
7977 case EDESTADDRREQ: return MA_NO_ADDRESS;
7978 #endif
7979 #ifdef EMSGSIZE
7980 case EMSGSIZE: return MA_TOO_BIG;
7981 #endif
7982 #ifdef EPROTOTYPE
7983 case EPROTOTYPE: return MA_BAD_PROTOCOL;
7984 #endif
7985 #ifdef ENOPROTOOPT
7986 case ENOPROTOOPT: return MA_PROTOCOL_UNAVAILABLE;
7987 #endif
7988 #ifdef EPROTONOSUPPORT
7989 case EPROTONOSUPPORT: return MA_PROTOCOL_NOT_SUPPORTED;
7990 #endif
7991 #ifdef ESOCKTNOSUPPORT
7992 case ESOCKTNOSUPPORT: return MA_SOCKET_NOT_SUPPORTED;
7993 #endif
7994 #ifdef EOPNOTSUPP
7995 case EOPNOTSUPP: return MA_INVALID_OPERATION;
7996 #endif
7997 #ifdef EPFNOSUPPORT
7998 case EPFNOSUPPORT: return MA_PROTOCOL_FAMILY_NOT_SUPPORTED;
7999 #endif
8000 #ifdef EAFNOSUPPORT
8001 case EAFNOSUPPORT: return MA_ADDRESS_FAMILY_NOT_SUPPORTED;
8002 #endif
8003 #ifdef EADDRINUSE
8004 case EADDRINUSE: return MA_ALREADY_IN_USE;
8005 #endif
8006 #ifdef EADDRNOTAVAIL
8007 case EADDRNOTAVAIL: return MA_ERROR;
8008 #endif
8009 #ifdef ENETDOWN
8010 case ENETDOWN: return MA_NO_NETWORK;
8011 #endif
8012 #ifdef ENETUNREACH
8013 case ENETUNREACH: return MA_NO_NETWORK;
8014 #endif
8015 #ifdef ENETRESET
8016 case ENETRESET: return MA_NO_NETWORK;
8017 #endif
8018 #ifdef ECONNABORTED
8019 case ECONNABORTED: return MA_NO_NETWORK;
8020 #endif
8021 #ifdef ECONNRESET
8022 case ECONNRESET: return MA_CONNECTION_RESET;
8023 #endif
8024 #ifdef ENOBUFS
8025 case ENOBUFS: return MA_NO_SPACE;
8026 #endif
8027 #ifdef EISCONN
8028 case EISCONN: return MA_ALREADY_CONNECTED;
8029 #endif
8030 #ifdef ENOTCONN
8031 case ENOTCONN: return MA_NOT_CONNECTED;
8032 #endif
8033 #ifdef ESHUTDOWN
8034 case ESHUTDOWN: return MA_ERROR;
8035 #endif
8036 #ifdef ETOOMANYREFS
8037 case ETOOMANYREFS: return MA_ERROR;
8038 #endif
8039 #ifdef ETIMEDOUT
8040 case ETIMEDOUT: return MA_TIMEOUT;
8041 #endif
8042 #ifdef ECONNREFUSED
8043 case ECONNREFUSED: return MA_CONNECTION_REFUSED;
8044 #endif
8045 #ifdef EHOSTDOWN
8046 case EHOSTDOWN: return MA_NO_HOST;
8047 #endif
8048 #ifdef EHOSTUNREACH
8049 case EHOSTUNREACH: return MA_NO_HOST;
8050 #endif
8051 #ifdef EALREADY
8052 case EALREADY: return MA_IN_PROGRESS;
8053 #endif
8054 #ifdef EINPROGRESS
8055 case EINPROGRESS: return MA_IN_PROGRESS;
8056 #endif
8057 #ifdef ESTALE
8058 case ESTALE: return MA_INVALID_FILE;
8059 #endif
8060 #ifdef EUCLEAN
8061 case EUCLEAN: return MA_ERROR;
8062 #endif
8063 #ifdef ENOTNAM
8064 case ENOTNAM: return MA_ERROR;
8065 #endif
8066 #ifdef ENAVAIL
8067 case ENAVAIL: return MA_ERROR;
8068 #endif
8069 #ifdef EISNAM
8070 case EISNAM: return MA_ERROR;
8071 #endif
8072 #ifdef EREMOTEIO
8073 case EREMOTEIO: return MA_IO_ERROR;
8074 #endif
8075 #ifdef EDQUOT
8076 case EDQUOT: return MA_NO_SPACE;
8077 #endif
8078 #ifdef ENOMEDIUM
8079 case ENOMEDIUM: return MA_DOES_NOT_EXIST;
8080 #endif
8081 #ifdef EMEDIUMTYPE
8082 case EMEDIUMTYPE: return MA_ERROR;
8083 #endif
8084 #ifdef ECANCELED
8085 case ECANCELED: return MA_CANCELLED;
8086 #endif
8087 #ifdef ENOKEY
8088 case ENOKEY: return MA_ERROR;
8089 #endif
8090 #ifdef EKEYEXPIRED
8091 case EKEYEXPIRED: return MA_ERROR;
8092 #endif
8093 #ifdef EKEYREVOKED
8094 case EKEYREVOKED: return MA_ERROR;
8095 #endif
8096 #ifdef EKEYREJECTED
8097 case EKEYREJECTED: return MA_ERROR;
8098 #endif
8099 #ifdef EOWNERDEAD
8100 case EOWNERDEAD: return MA_ERROR;
8101 #endif
8102 #ifdef ENOTRECOVERABLE
8103 case ENOTRECOVERABLE: return MA_ERROR;
8104 #endif
8105 #ifdef ERFKILL
8106 case ERFKILL: return MA_ERROR;
8107 #endif
8108 #ifdef EHWPOISON
8109 case EHWPOISON: return MA_ERROR;
8110 #endif
8111 default: return MA_ERROR;
8112 }
8113}
8114
8115MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
8116{
8117#if defined(_MSC_VER) && _MSC_VER >= 1400
8118 errno_t err;
8119#endif
8120
8121 if (ppFile != NULL) {
8122 *ppFile = NULL; /* Safety. */
8123 }
8124
8125 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
8126 return MA_INVALID_ARGS;
8127 }
8128
8129#if defined(_MSC_VER) && _MSC_VER >= 1400
8130 err = fopen_s(ppFile, pFilePath, pOpenMode);
8131 if (err != 0) {
8132 return ma_result_from_errno(err);
8133 }
8134#else
8135#if defined(_WIN32) || defined(__APPLE__)
8136 *ppFile = fopen(pFilePath, pOpenMode);
8137#else
8138 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
8139 *ppFile = fopen64(pFilePath, pOpenMode);
8140 #else
8141 *ppFile = fopen(pFilePath, pOpenMode);
8142 #endif
8143#endif
8144 if (*ppFile == NULL) {
8145 ma_result result = ma_result_from_errno(errno);
8146 if (result == MA_SUCCESS) {
8147 result = MA_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */
8148 }
8149
8150 return result;
8151 }
8152#endif
8153
8154 return MA_SUCCESS;
8155}
8156
8157
8158
8159/*
8160_wfopen() isn't always available in all compilation environments.
8161
8162 * Windows only.
8163 * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back).
8164 * MinGW-64 (both 32- and 64-bit) seems to support it.
8165 * MinGW wraps it in !defined(__STRICT_ANSI__).
8166 * OpenWatcom wraps it in !defined(_NO_EXT_KEYS).
8167
8168This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs()
8169fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support.
8170*/
8171#if defined(_WIN32)
8172 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
8173 #define MA_HAS_WFOPEN
8174 #endif
8175#endif
8176
8177MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks)
8178{
8179 if (ppFile != NULL) {
8180 *ppFile = NULL; /* Safety. */
8181 }
8182
8183 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
8184 return MA_INVALID_ARGS;
8185 }
8186
8187#if defined(MA_HAS_WFOPEN)
8188 {
8189 /* Use _wfopen() on Windows. */
8190 #if defined(_MSC_VER) && _MSC_VER >= 1400
8191 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
8192 if (err != 0) {
8193 return ma_result_from_errno(err);
8194 }
8195 #else
8196 *ppFile = _wfopen(pFilePath, pOpenMode);
8197 if (*ppFile == NULL) {
8198 return ma_result_from_errno(errno);
8199 }
8200 #endif
8201 (void)pAllocationCallbacks;
8202 }
8203#else
8204 /*
8205 Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can
8206 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
8207 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.
8208 */
8209 {
8210 mbstate_t mbs;
8211 size_t lenMB;
8212 const wchar_t* pFilePathTemp = pFilePath;
8213 char* pFilePathMB = NULL;
8214 char pOpenModeMB[32] = {0};
8215
8216 /* Get the length first. */
8217 MA_ZERO_OBJECT(&mbs);
8218 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
8219 if (lenMB == (size_t)-1) {
8220 return ma_result_from_errno(errno);
8221 }
8222
8223 pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks);
8224 if (pFilePathMB == NULL) {
8225 return MA_OUT_OF_MEMORY;
8226 }
8227
8228 pFilePathTemp = pFilePath;
8229 MA_ZERO_OBJECT(&mbs);
8230 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
8231
8232 /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */
8233 {
8234 size_t i = 0;
8235 for (;;) {
8236 if (pOpenMode[i] == 0) {
8237 pOpenModeMB[i] = '\0';
8238 break;
8239 }
8240
8241 pOpenModeMB[i] = (char)pOpenMode[i];
8242 i += 1;
8243 }
8244 }
8245
8246 *ppFile = fopen(pFilePathMB, pOpenModeMB);
8247
8248 ma_free(pFilePathMB, pAllocationCallbacks);
8249 }
8250
8251 if (*ppFile == NULL) {
8252 return MA_ERROR;
8253 }
8254#endif
8255
8256 return MA_SUCCESS;
8257}
8258
8259
8260
8261static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes)
8262{
8263#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
8264 MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes);
8265#else
8266 while (sizeInBytes > 0) {
8267 ma_uint64 bytesToCopyNow = sizeInBytes;
8268 if (bytesToCopyNow > MA_SIZE_MAX) {
8269 bytesToCopyNow = MA_SIZE_MAX;
8270 }
8271
8272 MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */
8273
8274 sizeInBytes -= bytesToCopyNow;
8275 dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow);
8276 src = (const void*)((const ma_uint8*)src + bytesToCopyNow);
8277 }
8278#endif
8279}
8280
8281static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes)
8282{
8283#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
8284 MA_ZERO_MEMORY(dst, (size_t)sizeInBytes);
8285#else
8286 while (sizeInBytes > 0) {
8287 ma_uint64 bytesToZeroNow = sizeInBytes;
8288 if (bytesToZeroNow > MA_SIZE_MAX) {
8289 bytesToZeroNow = MA_SIZE_MAX;
8290 }
8291
8292 MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */
8293
8294 sizeInBytes -= bytesToZeroNow;
8295 dst = (void*)((ma_uint8*)dst + bytesToZeroNow);
8296 }
8297#endif
8298}
8299
8300
8301/* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */
8302static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x)
8303{
8304 x--;
8305 x |= x >> 1;
8306 x |= x >> 2;
8307 x |= x >> 4;
8308 x |= x >> 8;
8309 x |= x >> 16;
8310 x++;
8311
8312 return x;
8313}
8314
8315static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x)
8316{
8317 return ma_next_power_of_2(x) >> 1;
8318}
8319
8320static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x)
8321{
8322 unsigned int prev = ma_prev_power_of_2(x);
8323 unsigned int next = ma_next_power_of_2(x);
8324 if ((next - x) > (x - prev)) {
8325 return prev;
8326 } else {
8327 return next;
8328 }
8329}
8330
8331static MA_INLINE unsigned int ma_count_set_bits(unsigned int x)
8332{
8333 unsigned int count = 0;
8334 while (x != 0) {
8335 if (x & 1) {
8336 count += 1;
8337 }
8338
8339 x = x >> 1;
8340 }
8341
8342 return count;
8343}
8344
8345
8346
8347/**************************************************************************************************************************************************************
8348
8349Allocation Callbacks
8350
8351**************************************************************************************************************************************************************/
8352static void* ma__malloc_default(size_t sz, void* pUserData)
8353{
8354 (void)pUserData;
8355 return MA_MALLOC(sz);
8356}
8357
8358static void* ma__realloc_default(void* p, size_t sz, void* pUserData)
8359{
8360 (void)pUserData;
8361 return MA_REALLOC(p, sz);
8362}
8363
8364static void ma__free_default(void* p, void* pUserData)
8365{
8366 (void)pUserData;
8367 MA_FREE(p);
8368}
8369
8370
8371static void* ma__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
8372{
8373 if (pAllocationCallbacks == NULL) {
8374 return NULL;
8375 }
8376
8377 if (pAllocationCallbacks->onMalloc != NULL) {
8378 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
8379 }
8380
8381 /* Try using realloc(). */
8382 if (pAllocationCallbacks->onRealloc != NULL) {
8383 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
8384 }
8385
8386 return NULL;
8387}
8388
8389static void* ma__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks)
8390{
8391 if (pAllocationCallbacks == NULL) {
8392 return NULL;
8393 }
8394
8395 if (pAllocationCallbacks->onRealloc != NULL) {
8396 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
8397 }
8398
8399 /* Try emulating realloc() in terms of malloc()/free(). */
8400 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
8401 void* p2;
8402
8403 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
8404 if (p2 == NULL) {
8405 return NULL;
8406 }
8407
8408 if (p != NULL) {
8409 MA_COPY_MEMORY(p2, p, szOld);
8410 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
8411 }
8412
8413 return p2;
8414 }
8415
8416 return NULL;
8417}
8418
8419static MA_INLINE void* ma__calloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
8420{
8421 void* p = ma__malloc_from_callbacks(sz, pAllocationCallbacks);
8422 if (p != NULL) {
8423 MA_ZERO_MEMORY(p, sz);
8424 }
8425
8426 return p;
8427}
8428
8429static void ma__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
8430{
8431 if (p == NULL || pAllocationCallbacks == NULL) {
8432 return;
8433 }
8434
8435 if (pAllocationCallbacks->onFree != NULL) {
8436 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
8437 }
8438}
8439
8440static ma_allocation_callbacks ma_allocation_callbacks_init_default(void)
8441{
8442 ma_allocation_callbacks callbacks;
8443 callbacks.pUserData = NULL;
8444 callbacks.onMalloc = ma__malloc_default;
8445 callbacks.onRealloc = ma__realloc_default;
8446 callbacks.onFree = ma__free_default;
8447
8448 return callbacks;
8449}
8450
8451static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc)
8452{
8453 if (pDst == NULL) {
8454 return MA_INVALID_ARGS;
8455 }
8456
8457 if (pSrc == NULL) {
8458 *pDst = ma_allocation_callbacks_init_default();
8459 } else {
8460 if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) {
8461 *pDst = ma_allocation_callbacks_init_default();
8462 } else {
8463 if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) {
8464 return MA_INVALID_ARGS; /* Invalid allocation callbacks. */
8465 } else {
8466 *pDst = *pSrc;
8467 }
8468 }
8469 }
8470
8471 return MA_SUCCESS;
8472}
8473
8474
8475
8476
8477/**************************************************************************************************************************************************************
8478
8479Logging
8480
8481**************************************************************************************************************************************************************/
8482MA_API const char* ma_log_level_to_string(ma_uint32 logLevel)
8483{
8484 switch (logLevel)
8485 {
8486 case MA_LOG_LEVEL_DEBUG: return "DEBUG";
8487 case MA_LOG_LEVEL_INFO: return "INFO";
8488 case MA_LOG_LEVEL_WARNING: return "WARNING";
8489 case MA_LOG_LEVEL_ERROR: return "ERROR";
8490 default: return "ERROR";
8491 }
8492}
8493
8494#if defined(MA_DEBUG_OUTPUT)
8495
8496/* Customize this to use a specific tag in __android_log_print() for debug output messages. */
8497#ifndef MA_ANDROID_LOG_TAG
8498#define MA_ANDROID_LOG_TAG "miniaudio"
8499#endif
8500
8501void ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage)
8502{
8503 (void)pUserData;
8504
8505 /* Special handling for some platforms. */
8506 #if defined(MA_ANDROID)
8507 {
8508 /* Android. */
8509 __android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, "%s: %s", ma_log_level_to_string(level), pMessage);
8510 }
8511 #else
8512 {
8513 /* Everything else. */
8514 printf("%s: %s", ma_log_level_to_string(level), pMessage);
8515 }
8516 #endif
8517}
8518#endif
8519
8520MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData)
8521{
8522 ma_log_callback callback;
8523
8524 MA_ZERO_OBJECT(&callback);
8525 callback.onLog = onLog;
8526 callback.pUserData = pUserData;
8527
8528 return callback;
8529}
8530
8531
8532MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog)
8533{
8534 if (pLog == NULL) {
8535 return MA_INVALID_ARGS;
8536 }
8537
8538 MA_ZERO_OBJECT(pLog);
8539 ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks);
8540
8541 /* We need a mutex for thread safety. */
8542 #ifndef MA_NO_THREADING
8543 {
8544 ma_result result = ma_mutex_init(&pLog->lock);
8545 if (result != MA_SUCCESS) {
8546 return result;
8547 }
8548 }
8549 #endif
8550
8551 /* If we're using debug output, enable it. */
8552 #if defined(MA_DEBUG_OUTPUT)
8553 {
8554 ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL)); /* Doesn't really matter if this fails. */
8555 }
8556 #endif
8557
8558 return MA_SUCCESS;
8559}
8560
8561MA_API void ma_log_uninit(ma_log* pLog)
8562{
8563 if (pLog == NULL) {
8564 return;
8565 }
8566
8567#ifndef MA_NO_THREADING
8568 ma_mutex_uninit(&pLog->lock);
8569#endif
8570}
8571
8572static void ma_log_lock(ma_log* pLog)
8573{
8574#ifndef MA_NO_THREADING
8575 ma_mutex_lock(&pLog->lock);
8576#else
8577 (void)pLog;
8578#endif
8579}
8580
8581static void ma_log_unlock(ma_log* pLog)
8582{
8583#ifndef MA_NO_THREADING
8584 ma_mutex_unlock(&pLog->lock);
8585#else
8586 (void)pLog;
8587#endif
8588}
8589
8590MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback)
8591{
8592 ma_result result = MA_SUCCESS;
8593
8594 if (pLog == NULL || callback.onLog == NULL) {
8595 return MA_INVALID_ARGS;
8596 }
8597
8598 ma_log_lock(pLog);
8599 {
8600 if (pLog->callbackCount == ma_countof(pLog->callbacks)) {
8601 result = MA_OUT_OF_MEMORY; /* Reached the maximum allowed log callbacks. */
8602 } else {
8603 pLog->callbacks[pLog->callbackCount] = callback;
8604 pLog->callbackCount += 1;
8605 }
8606 }
8607 ma_log_unlock(pLog);
8608
8609 return result;
8610}
8611
8612MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback)
8613{
8614 if (pLog == NULL) {
8615 return MA_INVALID_ARGS;
8616 }
8617
8618 ma_log_lock(pLog);
8619 {
8620 ma_uint32 iLog;
8621 for (iLog = 0; iLog < pLog->callbackCount; ) {
8622 if (pLog->callbacks[iLog].onLog == callback.onLog) {
8623 /* Found. Move everything down a slot. */
8624 ma_uint32 jLog;
8625 for (jLog = iLog; jLog < pLog->callbackCount-1; jLog += 1) {
8626 pLog->callbacks[jLog] = pLog->callbacks[jLog + 1];
8627 }
8628
8629 pLog->callbackCount -= 1;
8630 } else {
8631 /* Not found. */
8632 iLog += 1;
8633 }
8634 }
8635 }
8636 ma_log_unlock(pLog);
8637
8638 return MA_SUCCESS;
8639}
8640
8641MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage)
8642{
8643 if (pLog == NULL || pMessage == NULL) {
8644 return MA_INVALID_ARGS;
8645 }
8646
8647 /* If it's a debug log, ignore it unless MA_DEBUG_OUTPUT is enabled. */
8648 #if !defined(MA_DEBUG_OUTPUT)
8649 {
8650 if (level == MA_LOG_LEVEL_DEBUG) {
8651 return MA_INVALID_ARGS; /* Don't post debug messages if debug output is disabled. */
8652 }
8653 }
8654 #endif
8655
8656 ma_log_lock(pLog);
8657 {
8658 ma_uint32 iLog;
8659 for (iLog = 0; iLog < pLog->callbackCount; iLog += 1) {
8660 if (pLog->callbacks[iLog].onLog) {
8661 pLog->callbacks[iLog].onLog(pLog->callbacks[iLog].pUserData, level, pMessage);
8662 }
8663 }
8664 }
8665 ma_log_unlock(pLog);
8666
8667 return MA_SUCCESS;
8668}
8669
8670
8671/*
8672We need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a
8673logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf().
8674*/
8675#if defined(_MSC_VER) && _MSC_VER < 1900
8676static int ma_vscprintf(const ma_allocation_callbacks* pAllocationCallbacks, const char* format, va_list args)
8677{
8678#if _MSC_VER > 1200
8679 return _vscprintf(format, args);
8680#else
8681 int result;
8682 char* pTempBuffer = NULL;
8683 size_t tempBufferCap = 1024;
8684
8685 if (format == NULL) {
8686 errno = EINVAL;
8687 return -1;
8688 }
8689
8690 for (;;) {
8691 char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, pAllocationCallbacks);
8692 if (pNewTempBuffer == NULL) {
8693 ma_free(pTempBuffer, pAllocationCallbacks);
8694 errno = ENOMEM;
8695 return -1; /* Out of memory. */
8696 }
8697
8698 pTempBuffer = pNewTempBuffer;
8699
8700 result = _vsnprintf(pTempBuffer, tempBufferCap, format, args);
8701 ma_free(pTempBuffer, NULL);
8702
8703 if (result != -1) {
8704 break; /* Got it. */
8705 }
8706
8707 /* 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. */
8708 tempBufferCap *= 2;
8709 }
8710
8711 return result;
8712#endif
8713}
8714#endif
8715
8716MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args)
8717{
8718 if (pLog == NULL || pFormat == NULL) {
8719 return MA_INVALID_ARGS;
8720 }
8721
8722 /*
8723 If it's a debug log, ignore it unless MA_DEBUG_OUTPUT is enabled. Do this before generating the
8724 formatted message string so that we don't waste time only to have ma_log_post() reject it.
8725 */
8726 #if !defined(MA_DEBUG_OUTPUT)
8727 {
8728 if (level == MA_LOG_LEVEL_DEBUG) {
8729 return MA_INVALID_ARGS; /* Don't post debug messages if debug output is disabled. */
8730 }
8731 }
8732 #endif
8733
8734 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
8735 {
8736 ma_result result;
8737 int length;
8738 char pFormattedMessageStack[1024];
8739 char* pFormattedMessageHeap = NULL;
8740
8741 /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */
8742 length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args);
8743 if (length < 0) {
8744 return MA_INVALID_OPERATION; /* An error occured when trying to convert the buffer. */
8745 }
8746
8747 if ((size_t)length < sizeof(pFormattedMessageStack)) {
8748 /* The string was written to the stack. */
8749 result = ma_log_post(pLog, level, pFormattedMessageStack);
8750 } else {
8751 /* The stack buffer was too small, try the heap. */
8752 pFormattedMessageHeap = (char*)ma_malloc(length + 1, &pLog->allocationCallbacks);
8753 if (pFormattedMessageHeap == NULL) {
8754 return MA_OUT_OF_MEMORY;
8755 }
8756
8757 length = vsnprintf(pFormattedMessageHeap, length + 1, pFormat, args);
8758 if (length < 0) {
8759 ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks);
8760 return MA_INVALID_OPERATION;
8761 }
8762
8763 result = ma_log_post(pLog, level, pFormattedMessageHeap);
8764 ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks);
8765 }
8766
8767 return result;
8768 }
8769 #else
8770 {
8771 /*
8772 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
8773 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
8774 a fixed sized stack allocated buffer.
8775 */
8776 #if defined(_MSC_VER) && _MSC_VER >= 1200 /* 1200 = VC6 */
8777 {
8778 ma_result result;
8779 int formattedLen;
8780 char* pFormattedMessage = NULL;
8781 va_list args2;
8782
8783 #if _MSC_VER >= 1800
8784 {
8785 va_copy(args2, args);
8786 }
8787 #else
8788 {
8789 args2 = args;
8790 }
8791 #endif
8792
8793 formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2);
8794 va_end(args2);
8795
8796 if (formattedLen <= 0) {
8797 return MA_INVALID_OPERATION;
8798 }
8799
8800 pFormattedMessage = (char*)ma_malloc(formattedLen + 1, &pLog->allocationCallbacks);
8801 if (pFormattedMessage == NULL) {
8802 return MA_OUT_OF_MEMORY;
8803 }
8804
8805 /* We'll get errors on newer versions of Visual Studio if we try to use vsprintf(). */
8806 #if _MSC_VER >= 1400 /* 1400 = Visual Studio 2005 */
8807 {
8808 vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args);
8809 }
8810 #else
8811 {
8812 vsprintf(pFormattedMessage, pFormat, args);
8813 }
8814 #endif
8815
8816 result = ma_log_post(pLog, level, pFormattedMessage);
8817 ma_free(pFormattedMessage, &pLog->allocationCallbacks);
8818
8819 return result;
8820 }
8821 #else
8822 {
8823 /* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */
8824 (void)level;
8825 (void)args;
8826
8827 return MA_INVALID_OPERATION;
8828 }
8829 #endif
8830 }
8831 #endif
8832}
8833
8834MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...)
8835{
8836 ma_result result;
8837 va_list args;
8838
8839 if (pLog == NULL || pFormat == NULL) {
8840 return MA_INVALID_ARGS;
8841 }
8842
8843 /*
8844 If it's a debug log, ignore it unless MA_DEBUG_OUTPUT is enabled. Do this before generating the
8845 formatted message string so that we don't waste time only to have ma_log_post() reject it.
8846 */
8847 #if !defined(MA_DEBUG_OUTPUT)
8848 {
8849 if (level == MA_LOG_LEVEL_DEBUG) {
8850 return MA_INVALID_ARGS; /* Don't post debug messages if debug output is disabled. */
8851 }
8852 }
8853 #endif
8854
8855 va_start(args, pFormat);
8856 {
8857 result = ma_log_postv(pLog, level, pFormat, args);
8858 }
8859 va_end(args);
8860
8861 return result;
8862}
8863
8864
8865
8866
8867/* Clamps an f32 sample to -1..1 */
8868static MA_INLINE float ma_clip_f32(float x)
8869{
8870 if (x < -1) return -1;
8871 if (x > +1) return +1;
8872 return x;
8873}
8874
8875static MA_INLINE float ma_mix_f32(float x, float y, float a)
8876{
8877 return x*(1-a) + y*a;
8878}
8879static MA_INLINE float ma_mix_f32_fast(float x, float y, float a)
8880{
8881 float r0 = (y - x);
8882 float r1 = r0*a;
8883 return x + r1;
8884 /*return x + (y - x)*a;*/
8885}
8886
8887#if defined(MA_SUPPORT_SSE2)
8888static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a)
8889{
8890 return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a));
8891}
8892#endif
8893#if defined(MA_SUPPORT_AVX2)
8894static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a)
8895{
8896 return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a));
8897}
8898#endif
8899#if defined(MA_SUPPORT_AVX512)
8900static MA_INLINE __m512 ma_mix_f32_fast__avx512(__m512 x, __m512 y, __m512 a)
8901{
8902 return _mm512_add_ps(x, _mm512_mul_ps(_mm512_sub_ps(y, x), a));
8903}
8904#endif
8905#if defined(MA_SUPPORT_NEON)
8906static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a)
8907{
8908 return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a));
8909}
8910#endif
8911
8912
8913static MA_INLINE double ma_mix_f64(double x, double y, double a)
8914{
8915 return x*(1-a) + y*a;
8916}
8917static MA_INLINE double ma_mix_f64_fast(double x, double y, double a)
8918{
8919 return x + (y - x)*a;
8920}
8921
8922static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi)
8923{
8924 return lo + x*(hi-lo);
8925}
8926
8927
8928/*
8929Greatest common factor using Euclid's algorithm iteratively.
8930*/
8931static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b)
8932{
8933 for (;;) {
8934 if (b == 0) {
8935 break;
8936 } else {
8937 ma_uint32 t = a;
8938 a = b;
8939 b = t % a;
8940 }
8941 }
8942
8943 return a;
8944}
8945
8946
8947/*
8948Random Number Generation
8949
8950miniaudio uses the LCG random number generation algorithm. This is good enough for audio.
8951
8952Note that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across
8953multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for
8954miniaudio's purposes.
8955*/
8956#ifndef MA_DEFAULT_LCG_SEED
8957#define MA_DEFAULT_LCG_SEED 4321
8958#endif
8959
8960#define MA_LCG_M 2147483647
8961#define MA_LCG_A 48271
8962#define MA_LCG_C 0
8963
8964static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */
8965
8966static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed)
8967{
8968 MA_ASSERT(pLCG != NULL);
8969 pLCG->state = seed;
8970}
8971
8972static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG)
8973{
8974 pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M;
8975 return pLCG->state;
8976}
8977
8978static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG)
8979{
8980 return (ma_uint32)ma_lcg_rand_s32(pLCG);
8981}
8982
8983static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG)
8984{
8985 return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF);
8986}
8987
8988static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG)
8989{
8990 return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF;
8991}
8992
8993static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG)
8994{
8995 return (float)ma_lcg_rand_f64(pLCG);
8996}
8997
8998static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi)
8999{
9000 return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi);
9001}
9002
9003static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi)
9004{
9005 if (lo == hi) {
9006 return lo;
9007 }
9008
9009 return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1);
9010}
9011
9012
9013
9014static MA_INLINE void ma_seed(ma_int32 seed)
9015{
9016 ma_lcg_seed(&g_maLCG, seed);
9017}
9018
9019static MA_INLINE ma_int32 ma_rand_s32(void)
9020{
9021 return ma_lcg_rand_s32(&g_maLCG);
9022}
9023
9024static MA_INLINE ma_uint32 ma_rand_u32(void)
9025{
9026 return ma_lcg_rand_u32(&g_maLCG);
9027}
9028
9029static MA_INLINE double ma_rand_f64(void)
9030{
9031 return ma_lcg_rand_f64(&g_maLCG);
9032}
9033
9034static MA_INLINE float ma_rand_f32(void)
9035{
9036 return ma_lcg_rand_f32(&g_maLCG);
9037}
9038
9039static MA_INLINE float ma_rand_range_f32(float lo, float hi)
9040{
9041 return ma_lcg_rand_range_f32(&g_maLCG, lo, hi);
9042}
9043
9044static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi)
9045{
9046 return ma_lcg_rand_range_s32(&g_maLCG, lo, hi);
9047}
9048
9049
9050static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax)
9051{
9052 return ma_rand_range_f32(ditherMin, ditherMax);
9053}
9054
9055static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax)
9056{
9057 float a = ma_rand_range_f32(ditherMin, 0);
9058 float b = ma_rand_range_f32(0, ditherMax);
9059 return a + b;
9060}
9061
9062static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax)
9063{
9064 if (ditherMode == ma_dither_mode_rectangle) {
9065 return ma_dither_f32_rectangle(ditherMin, ditherMax);
9066 }
9067 if (ditherMode == ma_dither_mode_triangle) {
9068 return ma_dither_f32_triangle(ditherMin, ditherMax);
9069 }
9070
9071 return 0;
9072}
9073
9074static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax)
9075{
9076 if (ditherMode == ma_dither_mode_rectangle) {
9077 ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax);
9078 return a;
9079 }
9080 if (ditherMode == ma_dither_mode_triangle) {
9081 ma_int32 a = ma_rand_range_s32(ditherMin, 0);
9082 ma_int32 b = ma_rand_range_s32(0, ditherMax);
9083 return a + b;
9084 }
9085
9086 return 0;
9087}
9088
9089
9090/**************************************************************************************************************************************************************
9091
9092Atomics
9093
9094**************************************************************************************************************************************************************/
9095/* c89atomic.h begin */
9096#ifndef c89atomic_h
9097#define c89atomic_h
9098#if defined(__cplusplus)
9099extern "C" {
9100#endif
9101typedef signed char c89atomic_int8;
9102typedef unsigned char c89atomic_uint8;
9103typedef signed short c89atomic_int16;
9104typedef unsigned short c89atomic_uint16;
9105typedef signed int c89atomic_int32;
9106typedef unsigned int c89atomic_uint32;
9107#if defined(_MSC_VER)
9108 typedef signed __int64 c89atomic_int64;
9109 typedef unsigned __int64 c89atomic_uint64;
9110#else
9111 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
9112 #pragma GCC diagnostic push
9113 #pragma GCC diagnostic ignored "-Wlong-long"
9114 #if defined(__clang__)
9115 #pragma GCC diagnostic ignored "-Wc++11-long-long"
9116 #endif
9117 #endif
9118 typedef signed long long c89atomic_int64;
9119 typedef unsigned long long c89atomic_uint64;
9120 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
9121 #pragma GCC diagnostic pop
9122 #endif
9123#endif
9124typedef int c89atomic_memory_order;
9125typedef unsigned char c89atomic_bool;
9126#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT)
9127#ifdef _WIN32
9128#ifdef _WIN64
9129#define C89ATOMIC_64BIT
9130#else
9131#define C89ATOMIC_32BIT
9132#endif
9133#endif
9134#endif
9135#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT)
9136#ifdef __GNUC__
9137#ifdef __LP64__
9138#define C89ATOMIC_64BIT
9139#else
9140#define C89ATOMIC_32BIT
9141#endif
9142#endif
9143#endif
9144#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT)
9145#include <stdint.h>
9146#if INTPTR_MAX == INT64_MAX
9147#define C89ATOMIC_64BIT
9148#else
9149#define C89ATOMIC_32BIT
9150#endif
9151#endif
9152#if defined(__x86_64__) || defined(_M_X64)
9153#define C89ATOMIC_X64
9154#elif defined(__i386) || defined(_M_IX86)
9155#define C89ATOMIC_X86
9156#elif defined(__arm__) || defined(_M_ARM)
9157#define C89ATOMIC_ARM
9158#endif
9159#if defined(_MSC_VER)
9160 #define C89ATOMIC_INLINE __forceinline
9161#elif defined(__GNUC__)
9162 #if defined(__STRICT_ANSI__)
9163 #define C89ATOMIC_INLINE __inline__ __attribute__((always_inline))
9164 #else
9165 #define C89ATOMIC_INLINE inline __attribute__((always_inline))
9166 #endif
9167#elif defined(__WATCOMC__) || defined(__DMC__)
9168 #define C89ATOMIC_INLINE __inline
9169#else
9170 #define C89ATOMIC_INLINE
9171#endif
9172#define C89ATOMIC_HAS_8
9173#define C89ATOMIC_HAS_16
9174#define C89ATOMIC_HAS_32
9175#define C89ATOMIC_HAS_64
9176#if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__)
9177 #define c89atomic_memory_order_relaxed 0
9178 #define c89atomic_memory_order_consume 1
9179 #define c89atomic_memory_order_acquire 2
9180 #define c89atomic_memory_order_release 3
9181 #define c89atomic_memory_order_acq_rel 4
9182 #define c89atomic_memory_order_seq_cst 5
9183 #if _MSC_VER < 1600 && defined(C89ATOMIC_32BIT)
9184 #define C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY
9185 #endif
9186 #if _MSC_VER < 1600
9187 #undef C89ATOMIC_HAS_8
9188 #undef C89ATOMIC_HAS_16
9189 #endif
9190 #if !defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
9191 #include <intrin.h>
9192 #endif
9193 #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
9194 #if defined(C89ATOMIC_HAS_8)
9195 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired)
9196 {
9197 c89atomic_uint8 result = 0;
9198 __asm {
9199 mov ecx, dst
9200 mov al, expected
9201 mov dl, desired
9202 lock cmpxchg [ecx], dl
9203 mov result, al
9204 }
9205 return result;
9206 }
9207 #endif
9208 #if defined(C89ATOMIC_HAS_16)
9209 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired)
9210 {
9211 c89atomic_uint16 result = 0;
9212 __asm {
9213 mov ecx, dst
9214 mov ax, expected
9215 mov dx, desired
9216 lock cmpxchg [ecx], dx
9217 mov result, ax
9218 }
9219 return result;
9220 }
9221 #endif
9222 #if defined(C89ATOMIC_HAS_32)
9223 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired)
9224 {
9225 c89atomic_uint32 result = 0;
9226 __asm {
9227 mov ecx, dst
9228 mov eax, expected
9229 mov edx, desired
9230 lock cmpxchg [ecx], edx
9231 mov result, eax
9232 }
9233 return result;
9234 }
9235 #endif
9236 #if defined(C89ATOMIC_HAS_64)
9237 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired)
9238 {
9239 c89atomic_uint32 resultEAX = 0;
9240 c89atomic_uint32 resultEDX = 0;
9241 __asm {
9242 mov esi, dst
9243 mov eax, dword ptr expected
9244 mov edx, dword ptr expected + 4
9245 mov ebx, dword ptr desired
9246 mov ecx, dword ptr desired + 4
9247 lock cmpxchg8b qword ptr [esi]
9248 mov resultEAX, eax
9249 mov resultEDX, edx
9250 }
9251 return ((c89atomic_uint64)resultEDX << 32) | resultEAX;
9252 }
9253 #endif
9254 #else
9255 #if defined(C89ATOMIC_HAS_8)
9256 #define c89atomic_compare_and_swap_8( dst, expected, desired) (c89atomic_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected)
9257 #endif
9258 #if defined(C89ATOMIC_HAS_16)
9259 #define c89atomic_compare_and_swap_16(dst, expected, desired) (c89atomic_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected)
9260 #endif
9261 #if defined(C89ATOMIC_HAS_32)
9262 #define c89atomic_compare_and_swap_32(dst, expected, desired) (c89atomic_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected)
9263 #endif
9264 #if defined(C89ATOMIC_HAS_64)
9265 #define c89atomic_compare_and_swap_64(dst, expected, desired) (c89atomic_uint64)_InterlockedCompareExchange64((volatile long long*)dst, (long long)desired, (long long)expected)
9266 #endif
9267 #endif
9268 #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
9269 #if defined(C89ATOMIC_HAS_8)
9270 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9271 {
9272 c89atomic_uint8 result = 0;
9273 (void)order;
9274 __asm {
9275 mov ecx, dst
9276 mov al, src
9277 lock xchg [ecx], al
9278 mov result, al
9279 }
9280 return result;
9281 }
9282 #endif
9283 #if defined(C89ATOMIC_HAS_16)
9284 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9285 {
9286 c89atomic_uint16 result = 0;
9287 (void)order;
9288 __asm {
9289 mov ecx, dst
9290 mov ax, src
9291 lock xchg [ecx], ax
9292 mov result, ax
9293 }
9294 return result;
9295 }
9296 #endif
9297 #if defined(C89ATOMIC_HAS_32)
9298 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9299 {
9300 c89atomic_uint32 result = 0;
9301 (void)order;
9302 __asm {
9303 mov ecx, dst
9304 mov eax, src
9305 lock xchg [ecx], eax
9306 mov result, eax
9307 }
9308 return result;
9309 }
9310 #endif
9311 #else
9312 #if defined(C89ATOMIC_HAS_8)
9313 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9314 {
9315 (void)order;
9316 return (c89atomic_uint8)_InterlockedExchange8((volatile char*)dst, (char)src);
9317 }
9318 #endif
9319 #if defined(C89ATOMIC_HAS_16)
9320 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9321 {
9322 (void)order;
9323 return (c89atomic_uint16)_InterlockedExchange16((volatile short*)dst, (short)src);
9324 }
9325 #endif
9326 #if defined(C89ATOMIC_HAS_32)
9327 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9328 {
9329 (void)order;
9330 return (c89atomic_uint32)_InterlockedExchange((volatile long*)dst, (long)src);
9331 }
9332 #endif
9333 #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT)
9334 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9335 {
9336 (void)order;
9337 return (c89atomic_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src);
9338 }
9339 #else
9340 #endif
9341 #endif
9342 #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT)
9343 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9344 {
9345 c89atomic_uint64 oldValue;
9346 do {
9347 oldValue = *dst;
9348 } while (c89atomic_compare_and_swap_64(dst, oldValue, src) != oldValue);
9349 (void)order;
9350 return oldValue;
9351 }
9352 #endif
9353 #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
9354 #if defined(C89ATOMIC_HAS_8)
9355 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9356 {
9357 c89atomic_uint8 result = 0;
9358 (void)order;
9359 __asm {
9360 mov ecx, dst
9361 mov al, src
9362 lock xadd [ecx], al
9363 mov result, al
9364 }
9365 return result;
9366 }
9367 #endif
9368 #if defined(C89ATOMIC_HAS_16)
9369 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9370 {
9371 c89atomic_uint16 result = 0;
9372 (void)order;
9373 __asm {
9374 mov ecx, dst
9375 mov ax, src
9376 lock xadd [ecx], ax
9377 mov result, ax
9378 }
9379 return result;
9380 }
9381 #endif
9382 #if defined(C89ATOMIC_HAS_32)
9383 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9384 {
9385 c89atomic_uint32 result = 0;
9386 (void)order;
9387 __asm {
9388 mov ecx, dst
9389 mov eax, src
9390 lock xadd [ecx], eax
9391 mov result, eax
9392 }
9393 return result;
9394 }
9395 #endif
9396 #else
9397 #if defined(C89ATOMIC_HAS_8)
9398 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9399 {
9400 (void)order;
9401 return (c89atomic_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src);
9402 }
9403 #endif
9404 #if defined(C89ATOMIC_HAS_16)
9405 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9406 {
9407 (void)order;
9408 return (c89atomic_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src);
9409 }
9410 #endif
9411 #if defined(C89ATOMIC_HAS_32)
9412 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9413 {
9414 (void)order;
9415 return (c89atomic_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src);
9416 }
9417 #endif
9418 #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT)
9419 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9420 {
9421 (void)order;
9422 return (c89atomic_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src);
9423 }
9424 #else
9425 #endif
9426 #endif
9427 #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT)
9428 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9429 {
9430 c89atomic_uint64 oldValue;
9431 c89atomic_uint64 newValue;
9432 do {
9433 oldValue = *dst;
9434 newValue = oldValue + src;
9435 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
9436 (void)order;
9437 return oldValue;
9438 }
9439 #endif
9440 #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
9441 static C89ATOMIC_INLINE void __stdcall c89atomic_thread_fence(c89atomic_memory_order order)
9442 {
9443 (void)order;
9444 __asm {
9445 lock add [esp], 0
9446 }
9447 }
9448 #else
9449 #if defined(C89ATOMIC_X64)
9450 #define c89atomic_thread_fence(order) __faststorefence(), (void)order
9451 #else
9452 static C89ATOMIC_INLINE void c89atomic_thread_fence(c89atomic_memory_order order)
9453 {
9454 volatile c89atomic_uint32 barrier = 0;
9455 c89atomic_fetch_add_explicit_32(&barrier, 0, order);
9456 }
9457 #endif
9458 #endif
9459 #define c89atomic_compiler_fence() c89atomic_thread_fence(c89atomic_memory_order_seq_cst)
9460 #define c89atomic_signal_fence(order) c89atomic_thread_fence(order)
9461 #if defined(C89ATOMIC_HAS_8)
9462 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order)
9463 {
9464 (void)order;
9465 return c89atomic_compare_and_swap_8((volatile c89atomic_uint8*)ptr, 0, 0);
9466 }
9467 #endif
9468 #if defined(C89ATOMIC_HAS_16)
9469 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order)
9470 {
9471 (void)order;
9472 return c89atomic_compare_and_swap_16((volatile c89atomic_uint16*)ptr, 0, 0);
9473 }
9474 #endif
9475 #if defined(C89ATOMIC_HAS_32)
9476 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order)
9477 {
9478 (void)order;
9479 return c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)ptr, 0, 0);
9480 }
9481 #endif
9482 #if defined(C89ATOMIC_HAS_64)
9483 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order)
9484 {
9485 (void)order;
9486 return c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)ptr, 0, 0);
9487 }
9488 #endif
9489 #if defined(C89ATOMIC_HAS_8)
9490 #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order)
9491 #endif
9492 #if defined(C89ATOMIC_HAS_16)
9493 #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order)
9494 #endif
9495 #if defined(C89ATOMIC_HAS_32)
9496 #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order)
9497 #endif
9498 #if defined(C89ATOMIC_HAS_64)
9499 #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order)
9500 #endif
9501 #if defined(C89ATOMIC_HAS_8)
9502 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9503 {
9504 c89atomic_uint8 oldValue;
9505 c89atomic_uint8 newValue;
9506 do {
9507 oldValue = *dst;
9508 newValue = (c89atomic_uint8)(oldValue - src);
9509 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
9510 (void)order;
9511 return oldValue;
9512 }
9513 #endif
9514 #if defined(C89ATOMIC_HAS_16)
9515 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9516 {
9517 c89atomic_uint16 oldValue;
9518 c89atomic_uint16 newValue;
9519 do {
9520 oldValue = *dst;
9521 newValue = (c89atomic_uint16)(oldValue - src);
9522 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
9523 (void)order;
9524 return oldValue;
9525 }
9526 #endif
9527 #if defined(C89ATOMIC_HAS_32)
9528 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9529 {
9530 c89atomic_uint32 oldValue;
9531 c89atomic_uint32 newValue;
9532 do {
9533 oldValue = *dst;
9534 newValue = oldValue - src;
9535 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
9536 (void)order;
9537 return oldValue;
9538 }
9539 #endif
9540 #if defined(C89ATOMIC_HAS_64)
9541 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9542 {
9543 c89atomic_uint64 oldValue;
9544 c89atomic_uint64 newValue;
9545 do {
9546 oldValue = *dst;
9547 newValue = oldValue - src;
9548 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
9549 (void)order;
9550 return oldValue;
9551 }
9552 #endif
9553 #if defined(C89ATOMIC_HAS_8)
9554 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9555 {
9556 c89atomic_uint8 oldValue;
9557 c89atomic_uint8 newValue;
9558 do {
9559 oldValue = *dst;
9560 newValue = (c89atomic_uint8)(oldValue & src);
9561 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
9562 (void)order;
9563 return oldValue;
9564 }
9565 #endif
9566 #if defined(C89ATOMIC_HAS_16)
9567 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9568 {
9569 c89atomic_uint16 oldValue;
9570 c89atomic_uint16 newValue;
9571 do {
9572 oldValue = *dst;
9573 newValue = (c89atomic_uint16)(oldValue & src);
9574 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
9575 (void)order;
9576 return oldValue;
9577 }
9578 #endif
9579 #if defined(C89ATOMIC_HAS_32)
9580 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9581 {
9582 c89atomic_uint32 oldValue;
9583 c89atomic_uint32 newValue;
9584 do {
9585 oldValue = *dst;
9586 newValue = oldValue & src;
9587 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
9588 (void)order;
9589 return oldValue;
9590 }
9591 #endif
9592 #if defined(C89ATOMIC_HAS_64)
9593 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9594 {
9595 c89atomic_uint64 oldValue;
9596 c89atomic_uint64 newValue;
9597 do {
9598 oldValue = *dst;
9599 newValue = oldValue & src;
9600 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
9601 (void)order;
9602 return oldValue;
9603 }
9604 #endif
9605 #if defined(C89ATOMIC_HAS_8)
9606 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9607 {
9608 c89atomic_uint8 oldValue;
9609 c89atomic_uint8 newValue;
9610 do {
9611 oldValue = *dst;
9612 newValue = (c89atomic_uint8)(oldValue ^ src);
9613 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
9614 (void)order;
9615 return oldValue;
9616 }
9617 #endif
9618 #if defined(C89ATOMIC_HAS_16)
9619 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9620 {
9621 c89atomic_uint16 oldValue;
9622 c89atomic_uint16 newValue;
9623 do {
9624 oldValue = *dst;
9625 newValue = (c89atomic_uint16)(oldValue ^ src);
9626 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
9627 (void)order;
9628 return oldValue;
9629 }
9630 #endif
9631 #if defined(C89ATOMIC_HAS_32)
9632 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9633 {
9634 c89atomic_uint32 oldValue;
9635 c89atomic_uint32 newValue;
9636 do {
9637 oldValue = *dst;
9638 newValue = oldValue ^ src;
9639 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
9640 (void)order;
9641 return oldValue;
9642 }
9643 #endif
9644 #if defined(C89ATOMIC_HAS_64)
9645 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9646 {
9647 c89atomic_uint64 oldValue;
9648 c89atomic_uint64 newValue;
9649 do {
9650 oldValue = *dst;
9651 newValue = oldValue ^ src;
9652 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
9653 (void)order;
9654 return oldValue;
9655 }
9656 #endif
9657 #if defined(C89ATOMIC_HAS_8)
9658 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9659 {
9660 c89atomic_uint8 oldValue;
9661 c89atomic_uint8 newValue;
9662 do {
9663 oldValue = *dst;
9664 newValue = (c89atomic_uint8)(oldValue | src);
9665 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
9666 (void)order;
9667 return oldValue;
9668 }
9669 #endif
9670 #if defined(C89ATOMIC_HAS_16)
9671 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9672 {
9673 c89atomic_uint16 oldValue;
9674 c89atomic_uint16 newValue;
9675 do {
9676 oldValue = *dst;
9677 newValue = (c89atomic_uint16)(oldValue | src);
9678 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
9679 (void)order;
9680 return oldValue;
9681 }
9682 #endif
9683 #if defined(C89ATOMIC_HAS_32)
9684 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9685 {
9686 c89atomic_uint32 oldValue;
9687 c89atomic_uint32 newValue;
9688 do {
9689 oldValue = *dst;
9690 newValue = oldValue | src;
9691 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
9692 (void)order;
9693 return oldValue;
9694 }
9695 #endif
9696 #if defined(C89ATOMIC_HAS_64)
9697 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9698 {
9699 c89atomic_uint64 oldValue;
9700 c89atomic_uint64 newValue;
9701 do {
9702 oldValue = *dst;
9703 newValue = oldValue | src;
9704 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
9705 (void)order;
9706 return oldValue;
9707 }
9708 #endif
9709 #if defined(C89ATOMIC_HAS_8)
9710 #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order)
9711 #endif
9712 #if defined(C89ATOMIC_HAS_16)
9713 #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order)
9714 #endif
9715 #if defined(C89ATOMIC_HAS_32)
9716 #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order)
9717 #endif
9718 #if defined(C89ATOMIC_HAS_64)
9719 #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order)
9720 #endif
9721 #if defined(C89ATOMIC_HAS_8)
9722 #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order)
9723 #endif
9724 #if defined(C89ATOMIC_HAS_16)
9725 #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order)
9726 #endif
9727 #if defined(C89ATOMIC_HAS_32)
9728 #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order)
9729 #endif
9730 #if defined(C89ATOMIC_HAS_64)
9731 #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order)
9732 #endif
9733 #if defined(C89ATOMIC_HAS_8)
9734 typedef c89atomic_uint8 c89atomic_flag;
9735 #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order)
9736 #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order)
9737 #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order)
9738 #else
9739 typedef c89atomic_uint32 c89atomic_flag;
9740 #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_32(ptr, order)
9741 #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_32(ptr, order)
9742 #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_32(ptr, order)
9743 #endif
9744#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
9745 #define C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE
9746 #define C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE
9747 #define c89atomic_memory_order_relaxed __ATOMIC_RELAXED
9748 #define c89atomic_memory_order_consume __ATOMIC_CONSUME
9749 #define c89atomic_memory_order_acquire __ATOMIC_ACQUIRE
9750 #define c89atomic_memory_order_release __ATOMIC_RELEASE
9751 #define c89atomic_memory_order_acq_rel __ATOMIC_ACQ_REL
9752 #define c89atomic_memory_order_seq_cst __ATOMIC_SEQ_CST
9753 #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory")
9754 #define c89atomic_thread_fence(order) __atomic_thread_fence(order)
9755 #define c89atomic_signal_fence(order) __atomic_signal_fence(order)
9756 #define c89atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr)
9757 #define c89atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr)
9758 #define c89atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr)
9759 #define c89atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr)
9760 #define c89atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order)
9761 #define c89atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order)
9762 #define c89atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order)
9763 #define c89atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order)
9764 #define c89atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order)
9765 #define c89atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order)
9766 #define c89atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order)
9767 #define c89atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order)
9768 #define c89atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order)
9769 #define c89atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order)
9770 #define c89atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order)
9771 #define c89atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order)
9772 #define c89atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order)
9773 #define c89atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order)
9774 #define c89atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order)
9775 #define c89atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order)
9776 #define c89atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order)
9777 #define c89atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order)
9778 #define c89atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order)
9779 #define c89atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order)
9780 #define c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
9781 #define c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
9782 #define c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
9783 #define c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
9784 #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
9785 #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
9786 #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
9787 #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
9788 #define c89atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order)
9789 #define c89atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order)
9790 #define c89atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order)
9791 #define c89atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order)
9792 #define c89atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order)
9793 #define c89atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order)
9794 #define c89atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order)
9795 #define c89atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order)
9796 #define c89atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order)
9797 #define c89atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order)
9798 #define c89atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order)
9799 #define c89atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order)
9800 #define c89atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order)
9801 #define c89atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order)
9802 #define c89atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order)
9803 #define c89atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order)
9804 #define c89atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order)
9805 #define c89atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order)
9806 #define c89atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order)
9807 #define c89atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order)
9808 #define c89atomic_compare_and_swap_8 (dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
9809 #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
9810 #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
9811 #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
9812 typedef c89atomic_uint8 c89atomic_flag;
9813 #define c89atomic_flag_test_and_set_explicit(dst, order) (c89atomic_bool)__atomic_test_and_set(dst, order)
9814 #define c89atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order)
9815 #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order)
9816#else
9817 #define c89atomic_memory_order_relaxed 1
9818 #define c89atomic_memory_order_consume 2
9819 #define c89atomic_memory_order_acquire 3
9820 #define c89atomic_memory_order_release 4
9821 #define c89atomic_memory_order_acq_rel 5
9822 #define c89atomic_memory_order_seq_cst 6
9823 #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory")
9824 #if defined(__GNUC__)
9825 #define c89atomic_thread_fence(order) __sync_synchronize(), (void)order
9826 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9827 {
9828 if (order > c89atomic_memory_order_acquire) {
9829 __sync_synchronize();
9830 }
9831 return __sync_lock_test_and_set(dst, src);
9832 }
9833 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9834 {
9835 c89atomic_uint16 oldValue;
9836 do {
9837 oldValue = *dst;
9838 } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);
9839 (void)order;
9840 return oldValue;
9841 }
9842 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9843 {
9844 c89atomic_uint32 oldValue;
9845 do {
9846 oldValue = *dst;
9847 } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);
9848 (void)order;
9849 return oldValue;
9850 }
9851 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9852 {
9853 c89atomic_uint64 oldValue;
9854 do {
9855 oldValue = *dst;
9856 } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);
9857 (void)order;
9858 return oldValue;
9859 }
9860 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9861 {
9862 (void)order;
9863 return __sync_fetch_and_add(dst, src);
9864 }
9865 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9866 {
9867 (void)order;
9868 return __sync_fetch_and_add(dst, src);
9869 }
9870 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9871 {
9872 (void)order;
9873 return __sync_fetch_and_add(dst, src);
9874 }
9875 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9876 {
9877 (void)order;
9878 return __sync_fetch_and_add(dst, src);
9879 }
9880 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9881 {
9882 (void)order;
9883 return __sync_fetch_and_sub(dst, src);
9884 }
9885 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9886 {
9887 (void)order;
9888 return __sync_fetch_and_sub(dst, src);
9889 }
9890 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9891 {
9892 (void)order;
9893 return __sync_fetch_and_sub(dst, src);
9894 }
9895 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9896 {
9897 (void)order;
9898 return __sync_fetch_and_sub(dst, src);
9899 }
9900 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9901 {
9902 (void)order;
9903 return __sync_fetch_and_or(dst, src);
9904 }
9905 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9906 {
9907 (void)order;
9908 return __sync_fetch_and_or(dst, src);
9909 }
9910 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9911 {
9912 (void)order;
9913 return __sync_fetch_and_or(dst, src);
9914 }
9915 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9916 {
9917 (void)order;
9918 return __sync_fetch_and_or(dst, src);
9919 }
9920 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9921 {
9922 (void)order;
9923 return __sync_fetch_and_xor(dst, src);
9924 }
9925 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9926 {
9927 (void)order;
9928 return __sync_fetch_and_xor(dst, src);
9929 }
9930 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9931 {
9932 (void)order;
9933 return __sync_fetch_and_xor(dst, src);
9934 }
9935 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9936 {
9937 (void)order;
9938 return __sync_fetch_and_xor(dst, src);
9939 }
9940 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
9941 {
9942 (void)order;
9943 return __sync_fetch_and_and(dst, src);
9944 }
9945 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
9946 {
9947 (void)order;
9948 return __sync_fetch_and_and(dst, src);
9949 }
9950 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
9951 {
9952 (void)order;
9953 return __sync_fetch_and_and(dst, src);
9954 }
9955 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
9956 {
9957 (void)order;
9958 return __sync_fetch_and_and(dst, src);
9959 }
9960 #define c89atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
9961 #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
9962 #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
9963 #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
9964 #else
9965 #if defined(C89ATOMIC_X86)
9966 #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc")
9967 #elif defined(C89ATOMIC_X64)
9968 #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc")
9969 #else
9970 #error Unsupported architecture. Please submit a feature request.
9971 #endif
9972 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired)
9973 {
9974 c89atomic_uint8 result;
9975 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
9976 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
9977 #else
9978 #error Unsupported architecture. Please submit a feature request.
9979 #endif
9980 return result;
9981 }
9982 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired)
9983 {
9984 c89atomic_uint16 result;
9985 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
9986 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
9987 #else
9988 #error Unsupported architecture. Please submit a feature request.
9989 #endif
9990 return result;
9991 }
9992 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired)
9993 {
9994 c89atomic_uint32 result;
9995 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
9996 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
9997 #else
9998 #error Unsupported architecture. Please submit a feature request.
9999 #endif
10000 return result;
10001 }
10002 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired)
10003 {
10004 volatile c89atomic_uint64 result;
10005 #if defined(C89ATOMIC_X86)
10006 c89atomic_uint32 resultEAX;
10007 c89atomic_uint32 resultEDX;
10008 __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");
10009 result = ((c89atomic_uint64)resultEDX << 32) | resultEAX;
10010 #elif defined(C89ATOMIC_X64)
10011 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
10012 #else
10013 #error Unsupported architecture. Please submit a feature request.
10014 #endif
10015 return result;
10016 }
10017 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
10018 {
10019 c89atomic_uint8 result = 0;
10020 (void)order;
10021 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
10022 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
10023 #else
10024 #error Unsupported architecture. Please submit a feature request.
10025 #endif
10026 return result;
10027 }
10028 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
10029 {
10030 c89atomic_uint16 result = 0;
10031 (void)order;
10032 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
10033 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
10034 #else
10035 #error Unsupported architecture. Please submit a feature request.
10036 #endif
10037 return result;
10038 }
10039 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
10040 {
10041 c89atomic_uint32 result;
10042 (void)order;
10043 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
10044 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
10045 #else
10046 #error Unsupported architecture. Please submit a feature request.
10047 #endif
10048 return result;
10049 }
10050 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
10051 {
10052 c89atomic_uint64 result;
10053 (void)order;
10054 #if defined(C89ATOMIC_X86)
10055 do {
10056 result = *dst;
10057 } while (c89atomic_compare_and_swap_64(dst, result, src) != result);
10058 #elif defined(C89ATOMIC_X64)
10059 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
10060 #else
10061 #error Unsupported architecture. Please submit a feature request.
10062 #endif
10063 return result;
10064 }
10065 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
10066 {
10067 c89atomic_uint8 result;
10068 (void)order;
10069 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
10070 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc");
10071 #else
10072 #error Unsupported architecture. Please submit a feature request.
10073 #endif
10074 return result;
10075 }
10076 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
10077 {
10078 c89atomic_uint16 result;
10079 (void)order;
10080 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
10081 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc");
10082 #else
10083 #error Unsupported architecture. Please submit a feature request.
10084 #endif
10085 return result;
10086 }
10087 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
10088 {
10089 c89atomic_uint32 result;
10090 (void)order;
10091 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
10092 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc");
10093 #else
10094 #error Unsupported architecture. Please submit a feature request.
10095 #endif
10096 return result;
10097 }
10098 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
10099 {
10100 #if defined(C89ATOMIC_X86)
10101 c89atomic_uint64 oldValue;
10102 c89atomic_uint64 newValue;
10103 (void)order;
10104 do {
10105 oldValue = *dst;
10106 newValue = oldValue + src;
10107 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
10108 return oldValue;
10109 #elif defined(C89ATOMIC_X64)
10110 c89atomic_uint64 result;
10111 (void)order;
10112 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc");
10113 return result;
10114 #endif
10115 }
10116 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
10117 {
10118 c89atomic_uint8 oldValue;
10119 c89atomic_uint8 newValue;
10120 do {
10121 oldValue = *dst;
10122 newValue = (c89atomic_uint8)(oldValue - src);
10123 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
10124 (void)order;
10125 return oldValue;
10126 }
10127 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
10128 {
10129 c89atomic_uint16 oldValue;
10130 c89atomic_uint16 newValue;
10131 do {
10132 oldValue = *dst;
10133 newValue = (c89atomic_uint16)(oldValue - src);
10134 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
10135 (void)order;
10136 return oldValue;
10137 }
10138 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
10139 {
10140 c89atomic_uint32 oldValue;
10141 c89atomic_uint32 newValue;
10142 do {
10143 oldValue = *dst;
10144 newValue = oldValue - src;
10145 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
10146 (void)order;
10147 return oldValue;
10148 }
10149 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
10150 {
10151 c89atomic_uint64 oldValue;
10152 c89atomic_uint64 newValue;
10153 do {
10154 oldValue = *dst;
10155 newValue = oldValue - src;
10156 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
10157 (void)order;
10158 return oldValue;
10159 }
10160 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
10161 {
10162 c89atomic_uint8 oldValue;
10163 c89atomic_uint8 newValue;
10164 do {
10165 oldValue = *dst;
10166 newValue = (c89atomic_uint8)(oldValue & src);
10167 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
10168 (void)order;
10169 return oldValue;
10170 }
10171 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
10172 {
10173 c89atomic_uint16 oldValue;
10174 c89atomic_uint16 newValue;
10175 do {
10176 oldValue = *dst;
10177 newValue = (c89atomic_uint16)(oldValue & src);
10178 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
10179 (void)order;
10180 return oldValue;
10181 }
10182 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
10183 {
10184 c89atomic_uint32 oldValue;
10185 c89atomic_uint32 newValue;
10186 do {
10187 oldValue = *dst;
10188 newValue = oldValue & src;
10189 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
10190 (void)order;
10191 return oldValue;
10192 }
10193 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
10194 {
10195 c89atomic_uint64 oldValue;
10196 c89atomic_uint64 newValue;
10197 do {
10198 oldValue = *dst;
10199 newValue = oldValue & src;
10200 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
10201 (void)order;
10202 return oldValue;
10203 }
10204 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
10205 {
10206 c89atomic_uint8 oldValue;
10207 c89atomic_uint8 newValue;
10208 do {
10209 oldValue = *dst;
10210 newValue = (c89atomic_uint8)(oldValue ^ src);
10211 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
10212 (void)order;
10213 return oldValue;
10214 }
10215 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
10216 {
10217 c89atomic_uint16 oldValue;
10218 c89atomic_uint16 newValue;
10219 do {
10220 oldValue = *dst;
10221 newValue = (c89atomic_uint16)(oldValue ^ src);
10222 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
10223 (void)order;
10224 return oldValue;
10225 }
10226 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
10227 {
10228 c89atomic_uint32 oldValue;
10229 c89atomic_uint32 newValue;
10230 do {
10231 oldValue = *dst;
10232 newValue = oldValue ^ src;
10233 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
10234 (void)order;
10235 return oldValue;
10236 }
10237 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
10238 {
10239 c89atomic_uint64 oldValue;
10240 c89atomic_uint64 newValue;
10241 do {
10242 oldValue = *dst;
10243 newValue = oldValue ^ src;
10244 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
10245 (void)order;
10246 return oldValue;
10247 }
10248 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
10249 {
10250 c89atomic_uint8 oldValue;
10251 c89atomic_uint8 newValue;
10252 do {
10253 oldValue = *dst;
10254 newValue = (c89atomic_uint8)(oldValue | src);
10255 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
10256 (void)order;
10257 return oldValue;
10258 }
10259 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
10260 {
10261 c89atomic_uint16 oldValue;
10262 c89atomic_uint16 newValue;
10263 do {
10264 oldValue = *dst;
10265 newValue = (c89atomic_uint16)(oldValue | src);
10266 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
10267 (void)order;
10268 return oldValue;
10269 }
10270 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
10271 {
10272 c89atomic_uint32 oldValue;
10273 c89atomic_uint32 newValue;
10274 do {
10275 oldValue = *dst;
10276 newValue = oldValue | src;
10277 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
10278 (void)order;
10279 return oldValue;
10280 }
10281 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
10282 {
10283 c89atomic_uint64 oldValue;
10284 c89atomic_uint64 newValue;
10285 do {
10286 oldValue = *dst;
10287 newValue = oldValue | src;
10288 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
10289 (void)order;
10290 return oldValue;
10291 }
10292 #endif
10293 #define c89atomic_signal_fence(order) c89atomic_thread_fence(order)
10294 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order)
10295 {
10296 (void)order;
10297 return c89atomic_compare_and_swap_8((c89atomic_uint8*)ptr, 0, 0);
10298 }
10299 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order)
10300 {
10301 (void)order;
10302 return c89atomic_compare_and_swap_16((c89atomic_uint16*)ptr, 0, 0);
10303 }
10304 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order)
10305 {
10306 (void)order;
10307 return c89atomic_compare_and_swap_32((c89atomic_uint32*)ptr, 0, 0);
10308 }
10309 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order)
10310 {
10311 (void)order;
10312 return c89atomic_compare_and_swap_64((c89atomic_uint64*)ptr, 0, 0);
10313 }
10314 #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order)
10315 #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order)
10316 #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order)
10317 #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order)
10318 #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order)
10319 #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order)
10320 #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order)
10321 #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order)
10322 #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order)
10323 #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order)
10324 #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order)
10325 #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order)
10326 typedef c89atomic_uint8 c89atomic_flag;
10327 #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order)
10328 #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order)
10329 #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order)
10330#endif
10331#if !defined(C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE)
10332 #if defined(C89ATOMIC_HAS_8)
10333 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)
10334 {
10335 c89atomic_uint8 expectedValue;
10336 c89atomic_uint8 result;
10337 (void)successOrder;
10338 (void)failureOrder;
10339 expectedValue = c89atomic_load_explicit_8(expected, c89atomic_memory_order_seq_cst);
10340 result = c89atomic_compare_and_swap_8(dst, expectedValue, desired);
10341 if (result == expectedValue) {
10342 return 1;
10343 } else {
10344 c89atomic_store_explicit_8(expected, result, failureOrder);
10345 return 0;
10346 }
10347 }
10348 #endif
10349 #if defined(C89ATOMIC_HAS_16)
10350 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)
10351 {
10352 c89atomic_uint16 expectedValue;
10353 c89atomic_uint16 result;
10354 (void)successOrder;
10355 (void)failureOrder;
10356 expectedValue = c89atomic_load_explicit_16(expected, c89atomic_memory_order_seq_cst);
10357 result = c89atomic_compare_and_swap_16(dst, expectedValue, desired);
10358 if (result == expectedValue) {
10359 return 1;
10360 } else {
10361 c89atomic_store_explicit_16(expected, result, failureOrder);
10362 return 0;
10363 }
10364 }
10365 #endif
10366 #if defined(C89ATOMIC_HAS_32)
10367 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)
10368 {
10369 c89atomic_uint32 expectedValue;
10370 c89atomic_uint32 result;
10371 (void)successOrder;
10372 (void)failureOrder;
10373 expectedValue = c89atomic_load_explicit_32(expected, c89atomic_memory_order_seq_cst);
10374 result = c89atomic_compare_and_swap_32(dst, expectedValue, desired);
10375 if (result == expectedValue) {
10376 return 1;
10377 } else {
10378 c89atomic_store_explicit_32(expected, result, failureOrder);
10379 return 0;
10380 }
10381 }
10382 #endif
10383 #if defined(C89ATOMIC_HAS_64)
10384 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)
10385 {
10386 c89atomic_uint64 expectedValue;
10387 c89atomic_uint64 result;
10388 (void)successOrder;
10389 (void)failureOrder;
10390 expectedValue = c89atomic_load_explicit_64(expected, c89atomic_memory_order_seq_cst);
10391 result = c89atomic_compare_and_swap_64(dst, expectedValue, desired);
10392 if (result == expectedValue) {
10393 return 1;
10394 } else {
10395 c89atomic_store_explicit_64(expected, result, failureOrder);
10396 return 0;
10397 }
10398 }
10399 #endif
10400 #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder)
10401 #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder)
10402 #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder)
10403 #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder)
10404#endif
10405#if !defined(C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE)
10406 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_8(volatile void* ptr)
10407 {
10408 (void)ptr;
10409 return 1;
10410 }
10411 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_16(volatile void* ptr)
10412 {
10413 (void)ptr;
10414 return 1;
10415 }
10416 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_32(volatile void* ptr)
10417 {
10418 (void)ptr;
10419 return 1;
10420 }
10421 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_64(volatile void* ptr)
10422 {
10423 (void)ptr;
10424 #if defined(C89ATOMIC_64BIT)
10425 return 1;
10426 #else
10427 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
10428 return 1;
10429 #else
10430 return 0;
10431 #endif
10432 #endif
10433 }
10434#endif
10435#if defined(C89ATOMIC_64BIT)
10436 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr)
10437 {
10438 return c89atomic_is_lock_free_64((volatile c89atomic_uint64*)ptr);
10439 }
10440 static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order)
10441 {
10442 return (void*)c89atomic_load_explicit_64((volatile c89atomic_uint64*)ptr, order);
10443 }
10444 static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order)
10445 {
10446 c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order);
10447 }
10448 static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order)
10449 {
10450 return (void*)c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order);
10451 }
10452 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, volatile void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
10453 {
10454 return c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder);
10455 }
10456 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, volatile void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
10457 {
10458 return c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder);
10459 }
10460 static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired)
10461 {
10462 return (void*)c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)desired);
10463 }
10464#elif defined(C89ATOMIC_32BIT)
10465 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr)
10466 {
10467 return c89atomic_is_lock_free_32((volatile c89atomic_uint32*)ptr);
10468 }
10469 static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order)
10470 {
10471 return (void*)c89atomic_load_explicit_32((volatile c89atomic_uint32*)ptr, order);
10472 }
10473 static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order)
10474 {
10475 c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order);
10476 }
10477 static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order)
10478 {
10479 return (void*)c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order);
10480 }
10481 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)
10482 {
10483 return c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder);
10484 }
10485 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, volatile void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
10486 {
10487 return c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder);
10488 }
10489 static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired)
10490 {
10491 return (void*)c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)desired);
10492 }
10493#else
10494 #error Unsupported architecture.
10495#endif
10496#define c89atomic_flag_test_and_set(ptr) c89atomic_flag_test_and_set_explicit(ptr, c89atomic_memory_order_seq_cst)
10497#define c89atomic_flag_clear(ptr) c89atomic_flag_clear_explicit(ptr, c89atomic_memory_order_seq_cst)
10498#define c89atomic_store_ptr(dst, src) c89atomic_store_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst)
10499#define c89atomic_load_ptr(ptr) c89atomic_load_explicit_ptr((volatile void**)ptr, c89atomic_memory_order_seq_cst)
10500#define c89atomic_exchange_ptr(dst, src) c89atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst)
10501#define c89atomic_compare_exchange_strong_ptr(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (volatile void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
10502#define c89atomic_compare_exchange_weak_ptr(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (volatile void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
10503#define c89atomic_test_and_set_8( ptr) c89atomic_test_and_set_explicit_8( ptr, c89atomic_memory_order_seq_cst)
10504#define c89atomic_test_and_set_16(ptr) c89atomic_test_and_set_explicit_16(ptr, c89atomic_memory_order_seq_cst)
10505#define c89atomic_test_and_set_32(ptr) c89atomic_test_and_set_explicit_32(ptr, c89atomic_memory_order_seq_cst)
10506#define c89atomic_test_and_set_64(ptr) c89atomic_test_and_set_explicit_64(ptr, c89atomic_memory_order_seq_cst)
10507#define c89atomic_clear_8( ptr) c89atomic_clear_explicit_8( ptr, c89atomic_memory_order_seq_cst)
10508#define c89atomic_clear_16(ptr) c89atomic_clear_explicit_16(ptr, c89atomic_memory_order_seq_cst)
10509#define c89atomic_clear_32(ptr) c89atomic_clear_explicit_32(ptr, c89atomic_memory_order_seq_cst)
10510#define c89atomic_clear_64(ptr) c89atomic_clear_explicit_64(ptr, c89atomic_memory_order_seq_cst)
10511#define c89atomic_store_8( dst, src) c89atomic_store_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
10512#define c89atomic_store_16(dst, src) c89atomic_store_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
10513#define c89atomic_store_32(dst, src) c89atomic_store_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
10514#define c89atomic_store_64(dst, src) c89atomic_store_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
10515#define c89atomic_load_8( ptr) c89atomic_load_explicit_8( ptr, c89atomic_memory_order_seq_cst)
10516#define c89atomic_load_16(ptr) c89atomic_load_explicit_16(ptr, c89atomic_memory_order_seq_cst)
10517#define c89atomic_load_32(ptr) c89atomic_load_explicit_32(ptr, c89atomic_memory_order_seq_cst)
10518#define c89atomic_load_64(ptr) c89atomic_load_explicit_64(ptr, c89atomic_memory_order_seq_cst)
10519#define c89atomic_exchange_8( dst, src) c89atomic_exchange_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
10520#define c89atomic_exchange_16(dst, src) c89atomic_exchange_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
10521#define c89atomic_exchange_32(dst, src) c89atomic_exchange_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
10522#define c89atomic_exchange_64(dst, src) c89atomic_exchange_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
10523#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)
10524#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)
10525#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)
10526#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)
10527#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)
10528#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)
10529#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)
10530#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)
10531#define c89atomic_fetch_add_8( dst, src) c89atomic_fetch_add_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
10532#define c89atomic_fetch_add_16(dst, src) c89atomic_fetch_add_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
10533#define c89atomic_fetch_add_32(dst, src) c89atomic_fetch_add_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
10534#define c89atomic_fetch_add_64(dst, src) c89atomic_fetch_add_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
10535#define c89atomic_fetch_sub_8( dst, src) c89atomic_fetch_sub_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
10536#define c89atomic_fetch_sub_16(dst, src) c89atomic_fetch_sub_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
10537#define c89atomic_fetch_sub_32(dst, src) c89atomic_fetch_sub_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
10538#define c89atomic_fetch_sub_64(dst, src) c89atomic_fetch_sub_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
10539#define c89atomic_fetch_or_8( dst, src) c89atomic_fetch_or_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
10540#define c89atomic_fetch_or_16(dst, src) c89atomic_fetch_or_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
10541#define c89atomic_fetch_or_32(dst, src) c89atomic_fetch_or_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
10542#define c89atomic_fetch_or_64(dst, src) c89atomic_fetch_or_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
10543#define c89atomic_fetch_xor_8( dst, src) c89atomic_fetch_xor_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
10544#define c89atomic_fetch_xor_16(dst, src) c89atomic_fetch_xor_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
10545#define c89atomic_fetch_xor_32(dst, src) c89atomic_fetch_xor_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
10546#define c89atomic_fetch_xor_64(dst, src) c89atomic_fetch_xor_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
10547#define c89atomic_fetch_and_8( dst, src) c89atomic_fetch_and_explicit_8 (dst, src, c89atomic_memory_order_seq_cst)
10548#define c89atomic_fetch_and_16(dst, src) c89atomic_fetch_and_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
10549#define c89atomic_fetch_and_32(dst, src) c89atomic_fetch_and_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
10550#define c89atomic_fetch_and_64(dst, src) c89atomic_fetch_and_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
10551#define c89atomic_test_and_set_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_test_and_set_explicit_8( (c89atomic_uint8* )ptr, order)
10552#define c89atomic_test_and_set_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_test_and_set_explicit_16((c89atomic_uint16*)ptr, order)
10553#define c89atomic_test_and_set_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_test_and_set_explicit_32((c89atomic_uint32*)ptr, order)
10554#define c89atomic_test_and_set_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_test_and_set_explicit_64((c89atomic_uint64*)ptr, order)
10555#define c89atomic_clear_explicit_i8( ptr, order) c89atomic_clear_explicit_8( (c89atomic_uint8* )ptr, order)
10556#define c89atomic_clear_explicit_i16(ptr, order) c89atomic_clear_explicit_16((c89atomic_uint16*)ptr, order)
10557#define c89atomic_clear_explicit_i32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order)
10558#define c89atomic_clear_explicit_i64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order)
10559#define c89atomic_store_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_store_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
10560#define c89atomic_store_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_store_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
10561#define c89atomic_store_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_store_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
10562#define c89atomic_store_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_store_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
10563#define c89atomic_load_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_load_explicit_8( (c89atomic_uint8* )ptr, order)
10564#define c89atomic_load_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_load_explicit_16((c89atomic_uint16*)ptr, order)
10565#define c89atomic_load_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_load_explicit_32((c89atomic_uint32*)ptr, order)
10566#define c89atomic_load_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_load_explicit_64((c89atomic_uint64*)ptr, order)
10567#define c89atomic_exchange_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_exchange_explicit_8 ((c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
10568#define c89atomic_exchange_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_exchange_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
10569#define c89atomic_exchange_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_exchange_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
10570#define c89atomic_exchange_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_exchange_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
10571#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)
10572#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)
10573#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)
10574#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)
10575#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)
10576#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)
10577#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)
10578#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)
10579#define c89atomic_fetch_add_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_add_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
10580#define c89atomic_fetch_add_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_add_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
10581#define c89atomic_fetch_add_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_add_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
10582#define c89atomic_fetch_add_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_add_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
10583#define c89atomic_fetch_sub_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_sub_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
10584#define c89atomic_fetch_sub_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_sub_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
10585#define c89atomic_fetch_sub_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_sub_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
10586#define c89atomic_fetch_sub_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_sub_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
10587#define c89atomic_fetch_or_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_or_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
10588#define c89atomic_fetch_or_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_or_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
10589#define c89atomic_fetch_or_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_or_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
10590#define c89atomic_fetch_or_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_or_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
10591#define c89atomic_fetch_xor_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_xor_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
10592#define c89atomic_fetch_xor_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_xor_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
10593#define c89atomic_fetch_xor_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_xor_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
10594#define c89atomic_fetch_xor_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_xor_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
10595#define c89atomic_fetch_and_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_and_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
10596#define c89atomic_fetch_and_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_and_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
10597#define c89atomic_fetch_and_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_and_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
10598#define c89atomic_fetch_and_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_and_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
10599#define c89atomic_test_and_set_i8( ptr) c89atomic_test_and_set_explicit_i8( ptr, c89atomic_memory_order_seq_cst)
10600#define c89atomic_test_and_set_i16(ptr) c89atomic_test_and_set_explicit_i16(ptr, c89atomic_memory_order_seq_cst)
10601#define c89atomic_test_and_set_i32(ptr) c89atomic_test_and_set_explicit_i32(ptr, c89atomic_memory_order_seq_cst)
10602#define c89atomic_test_and_set_i64(ptr) c89atomic_test_and_set_explicit_i64(ptr, c89atomic_memory_order_seq_cst)
10603#define c89atomic_clear_i8( ptr) c89atomic_clear_explicit_i8( ptr, c89atomic_memory_order_seq_cst)
10604#define c89atomic_clear_i16(ptr) c89atomic_clear_explicit_i16(ptr, c89atomic_memory_order_seq_cst)
10605#define c89atomic_clear_i32(ptr) c89atomic_clear_explicit_i32(ptr, c89atomic_memory_order_seq_cst)
10606#define c89atomic_clear_i64(ptr) c89atomic_clear_explicit_i64(ptr, c89atomic_memory_order_seq_cst)
10607#define c89atomic_store_i8( dst, src) c89atomic_store_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
10608#define c89atomic_store_i16(dst, src) c89atomic_store_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
10609#define c89atomic_store_i32(dst, src) c89atomic_store_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
10610#define c89atomic_store_i64(dst, src) c89atomic_store_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
10611#define c89atomic_load_i8( ptr) c89atomic_load_explicit_i8( ptr, c89atomic_memory_order_seq_cst)
10612#define c89atomic_load_i16(ptr) c89atomic_load_explicit_i16(ptr, c89atomic_memory_order_seq_cst)
10613#define c89atomic_load_i32(ptr) c89atomic_load_explicit_i32(ptr, c89atomic_memory_order_seq_cst)
10614#define c89atomic_load_i64(ptr) c89atomic_load_explicit_i64(ptr, c89atomic_memory_order_seq_cst)
10615#define c89atomic_exchange_i8( dst, src) c89atomic_exchange_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
10616#define c89atomic_exchange_i16(dst, src) c89atomic_exchange_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
10617#define c89atomic_exchange_i32(dst, src) c89atomic_exchange_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
10618#define c89atomic_exchange_i64(dst, src) c89atomic_exchange_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
10619#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)
10620#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)
10621#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)
10622#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)
10623#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)
10624#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)
10625#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)
10626#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)
10627#define c89atomic_fetch_add_i8( dst, src) c89atomic_fetch_add_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
10628#define c89atomic_fetch_add_i16(dst, src) c89atomic_fetch_add_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
10629#define c89atomic_fetch_add_i32(dst, src) c89atomic_fetch_add_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
10630#define c89atomic_fetch_add_i64(dst, src) c89atomic_fetch_add_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
10631#define c89atomic_fetch_sub_i8( dst, src) c89atomic_fetch_sub_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
10632#define c89atomic_fetch_sub_i16(dst, src) c89atomic_fetch_sub_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
10633#define c89atomic_fetch_sub_i32(dst, src) c89atomic_fetch_sub_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
10634#define c89atomic_fetch_sub_i64(dst, src) c89atomic_fetch_sub_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
10635#define c89atomic_fetch_or_i8( dst, src) c89atomic_fetch_or_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
10636#define c89atomic_fetch_or_i16(dst, src) c89atomic_fetch_or_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
10637#define c89atomic_fetch_or_i32(dst, src) c89atomic_fetch_or_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
10638#define c89atomic_fetch_or_i64(dst, src) c89atomic_fetch_or_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
10639#define c89atomic_fetch_xor_i8( dst, src) c89atomic_fetch_xor_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
10640#define c89atomic_fetch_xor_i16(dst, src) c89atomic_fetch_xor_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
10641#define c89atomic_fetch_xor_i32(dst, src) c89atomic_fetch_xor_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
10642#define c89atomic_fetch_xor_i64(dst, src) c89atomic_fetch_xor_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
10643#define c89atomic_fetch_and_i8( dst, src) c89atomic_fetch_and_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
10644#define c89atomic_fetch_and_i16(dst, src) c89atomic_fetch_and_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
10645#define c89atomic_fetch_and_i32(dst, src) c89atomic_fetch_and_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
10646#define c89atomic_fetch_and_i64(dst, src) c89atomic_fetch_and_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
10647typedef union
10648{
10649 c89atomic_uint32 i;
10650 float f;
10651} c89atomic_if32;
10652typedef union
10653{
10654 c89atomic_uint64 i;
10655 double f;
10656} c89atomic_if64;
10657#define c89atomic_clear_explicit_f32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order)
10658#define c89atomic_clear_explicit_f64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order)
10659static C89ATOMIC_INLINE void c89atomic_store_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order)
10660{
10661 c89atomic_if32 x;
10662 x.f = src;
10663 c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, x.i, order);
10664}
10665static C89ATOMIC_INLINE void c89atomic_store_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order)
10666{
10667 c89atomic_if64 x;
10668 x.f = src;
10669 c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, x.i, order);
10670}
10671static C89ATOMIC_INLINE float c89atomic_load_explicit_f32(volatile float* ptr, c89atomic_memory_order order)
10672{
10673 c89atomic_if32 r;
10674 r.i = c89atomic_load_explicit_32((volatile c89atomic_uint32*)ptr, order);
10675 return r.f;
10676}
10677static C89ATOMIC_INLINE double c89atomic_load_explicit_f64(volatile double* ptr, c89atomic_memory_order order)
10678{
10679 c89atomic_if64 r;
10680 r.i = c89atomic_load_explicit_64((volatile c89atomic_uint64*)ptr, order);
10681 return r.f;
10682}
10683static C89ATOMIC_INLINE float c89atomic_exchange_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order)
10684{
10685 c89atomic_if32 r;
10686 c89atomic_if32 x;
10687 x.f = src;
10688 r.i = c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, x.i, order);
10689 return r.f;
10690}
10691static C89ATOMIC_INLINE double c89atomic_exchange_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order)
10692{
10693 c89atomic_if64 r;
10694 c89atomic_if64 x;
10695 x.f = src;
10696 r.i = c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, x.i, order);
10697 return r.f;
10698}
10699#define c89atomic_clear_f32(ptr) (float )c89atomic_clear_explicit_f32(ptr, c89atomic_memory_order_seq_cst)
10700#define c89atomic_clear_f64(ptr) (double)c89atomic_clear_explicit_f64(ptr, c89atomic_memory_order_seq_cst)
10701#define c89atomic_store_f32(dst, src) c89atomic_store_explicit_f32(dst, src, c89atomic_memory_order_seq_cst)
10702#define c89atomic_store_f64(dst, src) c89atomic_store_explicit_f64(dst, src, c89atomic_memory_order_seq_cst)
10703#define c89atomic_load_f32(ptr) (float )c89atomic_load_explicit_f32(ptr, c89atomic_memory_order_seq_cst)
10704#define c89atomic_load_f64(ptr) (double)c89atomic_load_explicit_f64(ptr, c89atomic_memory_order_seq_cst)
10705#define c89atomic_exchange_f32(dst, src) (float )c89atomic_exchange_explicit_f32(dst, src, c89atomic_memory_order_seq_cst)
10706#define c89atomic_exchange_f64(dst, src) (double)c89atomic_exchange_explicit_f64(dst, src, c89atomic_memory_order_seq_cst)
10707typedef c89atomic_flag c89atomic_spinlock;
10708static C89ATOMIC_INLINE void c89atomic_spinlock_lock(volatile c89atomic_spinlock* pSpinlock)
10709{
10710 for (;;) {
10711 if (c89atomic_flag_test_and_set_explicit(pSpinlock, c89atomic_memory_order_acquire) == 0) {
10712 break;
10713 }
10714 while (c89atoimc_flag_load_explicit(pSpinlock, c89atomic_memory_order_relaxed) == 1) {
10715 }
10716 }
10717}
10718static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlock* pSpinlock)
10719{
10720 c89atomic_flag_clear_explicit(pSpinlock, c89atomic_memory_order_release);
10721}
10722#if defined(__cplusplus)
10723}
10724#endif
10725#endif
10726/* c89atomic.h end */
10727
10728
10729
10730MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn)
10731{
10732 /* For robustness we're going to use a resampler object to calculate this since that already has a way of calculating this. */
10733 ma_result result;
10734 ma_uint64 frameCountOut;
10735 ma_resampler_config config;
10736 ma_resampler resampler;
10737
10738 if (sampleRateOut == sampleRateIn) {
10739 return frameCountIn;
10740 }
10741
10742 config = ma_resampler_config_init(ma_format_s16, 1, sampleRateIn, sampleRateOut, ma_resample_algorithm_linear);
10743 result = ma_resampler_init(&config, &resampler);
10744 if (result != MA_SUCCESS) {
10745 return 0;
10746 }
10747
10748 frameCountOut = ma_resampler_get_expected_output_frame_count(&resampler, frameCountIn);
10749
10750 ma_resampler_uninit(&resampler);
10751 return frameCountOut;
10752}
10753
10754#ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE
10755#define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096
10756#endif
10757
10758
10759
10760#if defined(MA_WIN32)
10761static ma_result ma_result_from_GetLastError(DWORD error)
10762{
10763 switch (error)
10764 {
10765 case ERROR_SUCCESS: return MA_SUCCESS;
10766 case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST;
10767 case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES;
10768 case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY;
10769 case ERROR_DISK_FULL: return MA_NO_SPACE;
10770 case ERROR_HANDLE_EOF: return MA_AT_END;
10771 case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK;
10772 case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS;
10773 case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED;
10774 case ERROR_SEM_TIMEOUT: return MA_TIMEOUT;
10775 case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST;
10776 default: break;
10777 }
10778
10779 return MA_ERROR;
10780}
10781#endif /* MA_WIN32 */
10782
10783
10784/*******************************************************************************
10785
10786Threading
10787
10788*******************************************************************************/
10789#ifndef MA_NO_THREADING
10790#ifdef MA_WIN32
10791 #define MA_THREADCALL WINAPI
10792 typedef unsigned long ma_thread_result;
10793#else
10794 #define MA_THREADCALL
10795 typedef void* ma_thread_result;
10796#endif
10797typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData);
10798
10799static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield)
10800{
10801 if (pSpinlock == NULL) {
10802 return MA_INVALID_ARGS;
10803 }
10804
10805 for (;;) {
10806 if (c89atomic_exchange_explicit_32(pSpinlock, 1, c89atomic_memory_order_acquire) == 0) {
10807 break;
10808 }
10809
10810 while (c89atomic_load_explicit_32(pSpinlock, c89atomic_memory_order_relaxed) == 1) {
10811 if (yield) {
10812 ma_yield();
10813 }
10814 }
10815 }
10816
10817 return MA_SUCCESS;
10818}
10819
10820MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock)
10821{
10822 return ma_spinlock_lock_ex(pSpinlock, MA_TRUE);
10823}
10824
10825MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock)
10826{
10827 return ma_spinlock_lock_ex(pSpinlock, MA_FALSE);
10828}
10829
10830MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock)
10831{
10832 if (pSpinlock == NULL) {
10833 return MA_INVALID_ARGS;
10834 }
10835
10836 c89atomic_store_explicit_32(pSpinlock, 0, c89atomic_memory_order_release);
10837 return MA_SUCCESS;
10838}
10839
10840#ifdef MA_WIN32
10841static int ma_thread_priority_to_win32(ma_thread_priority priority)
10842{
10843 switch (priority) {
10844 case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE;
10845 case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST;
10846 case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL;
10847 case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL;
10848 case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL;
10849 case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST;
10850 case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL;
10851 default: return THREAD_PRIORITY_NORMAL;
10852 }
10853}
10854
10855static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)
10856{
10857 *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, NULL);
10858 if (*pThread == NULL) {
10859 return ma_result_from_GetLastError(GetLastError());
10860 }
10861
10862 SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority));
10863
10864 return MA_SUCCESS;
10865}
10866
10867static void ma_thread_wait__win32(ma_thread* pThread)
10868{
10869 WaitForSingleObject((HANDLE)*pThread, INFINITE);
10870 CloseHandle((HANDLE)*pThread);
10871}
10872
10873
10874static ma_result ma_mutex_init__win32(ma_mutex* pMutex)
10875{
10876 *pMutex = CreateEventW(NULL, FALSE, TRUE, NULL);
10877 if (*pMutex == NULL) {
10878 return ma_result_from_GetLastError(GetLastError());
10879 }
10880
10881 return MA_SUCCESS;
10882}
10883
10884static void ma_mutex_uninit__win32(ma_mutex* pMutex)
10885{
10886 CloseHandle((HANDLE)*pMutex);
10887}
10888
10889static void ma_mutex_lock__win32(ma_mutex* pMutex)
10890{
10891 WaitForSingleObject((HANDLE)*pMutex, INFINITE);
10892}
10893
10894static void ma_mutex_unlock__win32(ma_mutex* pMutex)
10895{
10896 SetEvent((HANDLE)*pMutex);
10897}
10898
10899
10900static ma_result ma_event_init__win32(ma_event* pEvent)
10901{
10902 *pEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
10903 if (*pEvent == NULL) {
10904 return ma_result_from_GetLastError(GetLastError());
10905 }
10906
10907 return MA_SUCCESS;
10908}
10909
10910static void ma_event_uninit__win32(ma_event* pEvent)
10911{
10912 CloseHandle((HANDLE)*pEvent);
10913}
10914
10915static ma_result ma_event_wait__win32(ma_event* pEvent)
10916{
10917 DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE);
10918 if (result == WAIT_OBJECT_0) {
10919 return MA_SUCCESS;
10920 }
10921
10922 if (result == WAIT_TIMEOUT) {
10923 return MA_TIMEOUT;
10924 }
10925
10926 return ma_result_from_GetLastError(GetLastError());
10927}
10928
10929static ma_result ma_event_signal__win32(ma_event* pEvent)
10930{
10931 BOOL result = SetEvent((HANDLE)*pEvent);
10932 if (result == 0) {
10933 return ma_result_from_GetLastError(GetLastError());
10934 }
10935
10936 return MA_SUCCESS;
10937}
10938
10939
10940static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore)
10941{
10942 *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL);
10943 if (*pSemaphore == NULL) {
10944 return ma_result_from_GetLastError(GetLastError());
10945 }
10946
10947 return MA_SUCCESS;
10948}
10949
10950static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore)
10951{
10952 CloseHandle((HANDLE)*pSemaphore);
10953}
10954
10955static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore)
10956{
10957 DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE);
10958 if (result == WAIT_OBJECT_0) {
10959 return MA_SUCCESS;
10960 }
10961
10962 if (result == WAIT_TIMEOUT) {
10963 return MA_TIMEOUT;
10964 }
10965
10966 return ma_result_from_GetLastError(GetLastError());
10967}
10968
10969static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore)
10970{
10971 BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL);
10972 if (result == 0) {
10973 return ma_result_from_GetLastError(GetLastError());
10974 }
10975
10976 return MA_SUCCESS;
10977}
10978#endif
10979
10980
10981#ifdef MA_POSIX
10982static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)
10983{
10984 int result;
10985 pthread_attr_t* pAttr = NULL;
10986
10987#if !defined(__EMSCRIPTEN__)
10988 /* Try setting the thread priority. It's not critical if anything fails here. */
10989 pthread_attr_t attr;
10990 if (pthread_attr_init(&attr) == 0) {
10991 int scheduler = -1;
10992 if (priority == ma_thread_priority_idle) {
10993#ifdef SCHED_IDLE
10994 if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) {
10995 scheduler = SCHED_IDLE;
10996 }
10997#endif
10998 } else if (priority == ma_thread_priority_realtime) {
10999#ifdef SCHED_FIFO
11000 if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) {
11001 scheduler = SCHED_FIFO;
11002 }
11003#endif
11004#ifdef MA_LINUX
11005 } else {
11006 scheduler = sched_getscheduler(0);
11007#endif
11008 }
11009
11010 if (stackSize > 0) {
11011 pthread_attr_setstacksize(&attr, stackSize);
11012 }
11013
11014 if (scheduler != -1) {
11015 int priorityMin = sched_get_priority_min(scheduler);
11016 int priorityMax = sched_get_priority_max(scheduler);
11017 int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */
11018
11019 struct sched_param sched;
11020 if (pthread_attr_getschedparam(&attr, &sched) == 0) {
11021 if (priority == ma_thread_priority_idle) {
11022 sched.sched_priority = priorityMin;
11023 } else if (priority == ma_thread_priority_realtime) {
11024 sched.sched_priority = priorityMax;
11025 } else {
11026 sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */
11027 if (sched.sched_priority < priorityMin) {
11028 sched.sched_priority = priorityMin;
11029 }
11030 if (sched.sched_priority > priorityMax) {
11031 sched.sched_priority = priorityMax;
11032 }
11033 }
11034
11035 if (pthread_attr_setschedparam(&attr, &sched) == 0) {
11036 pAttr = &attr;
11037 }
11038 }
11039 }
11040 }
11041#else
11042 /* It's the emscripten build. We'll have a few unused parameters. */
11043 (void)priority;
11044 (void)stackSize;
11045#endif
11046
11047 result = pthread_create(pThread, pAttr, entryProc, pData);
11048
11049 /* The thread attributes object is no longer required. */
11050 if (pAttr != NULL) {
11051 pthread_attr_destroy(pAttr);
11052 }
11053
11054 if (result != 0) {
11055 return ma_result_from_errno(result);
11056 }
11057
11058 return MA_SUCCESS;
11059}
11060
11061static void ma_thread_wait__posix(ma_thread* pThread)
11062{
11063 pthread_join(*pThread, NULL);
11064 pthread_detach(*pThread);
11065}
11066
11067
11068static ma_result ma_mutex_init__posix(ma_mutex* pMutex)
11069{
11070 int result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL);
11071 if (result != 0) {
11072 return ma_result_from_errno(result);
11073 }
11074
11075 return MA_SUCCESS;
11076}
11077
11078static void ma_mutex_uninit__posix(ma_mutex* pMutex)
11079{
11080 pthread_mutex_destroy((pthread_mutex_t*)pMutex);
11081}
11082
11083static void ma_mutex_lock__posix(ma_mutex* pMutex)
11084{
11085 pthread_mutex_lock((pthread_mutex_t*)pMutex);
11086}
11087
11088static void ma_mutex_unlock__posix(ma_mutex* pMutex)
11089{
11090 pthread_mutex_unlock((pthread_mutex_t*)pMutex);
11091}
11092
11093
11094static ma_result ma_event_init__posix(ma_event* pEvent)
11095{
11096 int result;
11097
11098 result = pthread_mutex_init(&pEvent->lock, NULL);
11099 if (result != 0) {
11100 return ma_result_from_errno(result);
11101 }
11102
11103 result = pthread_cond_init(&pEvent->cond, NULL);
11104 if (result != 0) {
11105 pthread_mutex_destroy(&pEvent->lock);
11106 return ma_result_from_errno(result);
11107 }
11108
11109 pEvent->value = 0;
11110 return MA_SUCCESS;
11111}
11112
11113static void ma_event_uninit__posix(ma_event* pEvent)
11114{
11115 pthread_cond_destroy(&pEvent->cond);
11116 pthread_mutex_destroy(&pEvent->lock);
11117}
11118
11119static ma_result ma_event_wait__posix(ma_event* pEvent)
11120{
11121 pthread_mutex_lock(&pEvent->lock);
11122 {
11123 while (pEvent->value == 0) {
11124 pthread_cond_wait(&pEvent->cond, &pEvent->lock);
11125 }
11126 pEvent->value = 0; /* Auto-reset. */
11127 }
11128 pthread_mutex_unlock(&pEvent->lock);
11129
11130 return MA_SUCCESS;
11131}
11132
11133static ma_result ma_event_signal__posix(ma_event* pEvent)
11134{
11135 pthread_mutex_lock(&pEvent->lock);
11136 {
11137 pEvent->value = 1;
11138 pthread_cond_signal(&pEvent->cond);
11139 }
11140 pthread_mutex_unlock(&pEvent->lock);
11141
11142 return MA_SUCCESS;
11143}
11144
11145
11146static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore)
11147{
11148 int result;
11149
11150 if (pSemaphore == NULL) {
11151 return MA_INVALID_ARGS;
11152 }
11153
11154 pSemaphore->value = initialValue;
11155
11156 result = pthread_mutex_init(&pSemaphore->lock, NULL);
11157 if (result != 0) {
11158 return ma_result_from_errno(result); /* Failed to create mutex. */
11159 }
11160
11161 result = pthread_cond_init(&pSemaphore->cond, NULL);
11162 if (result != 0) {
11163 pthread_mutex_destroy(&pSemaphore->lock);
11164 return ma_result_from_errno(result); /* Failed to create condition variable. */
11165 }
11166
11167 return MA_SUCCESS;
11168}
11169
11170static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore)
11171{
11172 if (pSemaphore == NULL) {
11173 return;
11174 }
11175
11176 pthread_cond_destroy(&pSemaphore->cond);
11177 pthread_mutex_destroy(&pSemaphore->lock);
11178}
11179
11180static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore)
11181{
11182 if (pSemaphore == NULL) {
11183 return MA_INVALID_ARGS;
11184 }
11185
11186 pthread_mutex_lock(&pSemaphore->lock);
11187 {
11188 /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */
11189 while (pSemaphore->value == 0) {
11190 pthread_cond_wait(&pSemaphore->cond, &pSemaphore->lock);
11191 }
11192
11193 pSemaphore->value -= 1;
11194 }
11195 pthread_mutex_unlock(&pSemaphore->lock);
11196
11197 return MA_SUCCESS;
11198}
11199
11200static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore)
11201{
11202 if (pSemaphore == NULL) {
11203 return MA_INVALID_ARGS;
11204 }
11205
11206 pthread_mutex_lock(&pSemaphore->lock);
11207 {
11208 pSemaphore->value += 1;
11209 pthread_cond_signal(&pSemaphore->cond);
11210 }
11211 pthread_mutex_unlock(&pSemaphore->lock);
11212
11213 return MA_SUCCESS;
11214}
11215#endif
11216
11217typedef struct
11218{
11219 ma_thread_entry_proc entryProc;
11220 void* pData;
11221 ma_allocation_callbacks allocationCallbacks;
11222} ma_thread_proxy_data;
11223
11224static ma_thread_result MA_THREADCALL ma_thread_entry_proxy(void* pData)
11225{
11226 ma_thread_proxy_data* pProxyData = (ma_thread_proxy_data*)pData;
11227 ma_thread_entry_proc entryProc;
11228 void* pEntryProcData;
11229 ma_thread_result result;
11230
11231 #if defined(MA_ON_THREAD_ENTRY)
11232 MA_ON_THREAD_ENTRY
11233 #endif
11234
11235 entryProc = pProxyData->entryProc;
11236 pEntryProcData = pProxyData->pData;
11237
11238 /* Free the proxy data before getting into the real thread entry proc. */
11239 ma_free(pProxyData, &pProxyData->allocationCallbacks);
11240
11241 result = entryProc(pEntryProcData);
11242
11243 #if defined(MA_ON_THREAD_EXIT)
11244 MA_ON_THREAD_EXIT
11245 #endif
11246
11247 return result;
11248}
11249
11250static 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)
11251{
11252 ma_result result;
11253 ma_thread_proxy_data* pProxyData;
11254
11255 if (pThread == NULL || entryProc == NULL) {
11256 return MA_FALSE;
11257 }
11258
11259 pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks); /* Will be freed by the proxy entry proc. */
11260 if (pProxyData == NULL) {
11261 return MA_OUT_OF_MEMORY;
11262 }
11263
11264 pProxyData->entryProc = entryProc;
11265 pProxyData->pData = pData;
11266 ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks);
11267
11268#ifdef MA_WIN32
11269 result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData);
11270#endif
11271#ifdef MA_POSIX
11272 result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData);
11273#endif
11274
11275 if (result != MA_SUCCESS) {
11276 ma_free(pProxyData, pAllocationCallbacks);
11277 return result;
11278 }
11279
11280 return MA_SUCCESS;
11281}
11282
11283static void ma_thread_wait(ma_thread* pThread)
11284{
11285 if (pThread == NULL) {
11286 return;
11287 }
11288
11289#ifdef MA_WIN32
11290 ma_thread_wait__win32(pThread);
11291#endif
11292#ifdef MA_POSIX
11293 ma_thread_wait__posix(pThread);
11294#endif
11295}
11296
11297
11298MA_API ma_result ma_mutex_init(ma_mutex* pMutex)
11299{
11300 if (pMutex == NULL) {
11301 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
11302 return MA_INVALID_ARGS;
11303 }
11304
11305#ifdef MA_WIN32
11306 return ma_mutex_init__win32(pMutex);
11307#endif
11308#ifdef MA_POSIX
11309 return ma_mutex_init__posix(pMutex);
11310#endif
11311}
11312
11313MA_API void ma_mutex_uninit(ma_mutex* pMutex)
11314{
11315 if (pMutex == NULL) {
11316 return;
11317 }
11318
11319#ifdef MA_WIN32
11320 ma_mutex_uninit__win32(pMutex);
11321#endif
11322#ifdef MA_POSIX
11323 ma_mutex_uninit__posix(pMutex);
11324#endif
11325}
11326
11327MA_API void ma_mutex_lock(ma_mutex* pMutex)
11328{
11329 if (pMutex == NULL) {
11330 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
11331 return;
11332 }
11333
11334#ifdef MA_WIN32
11335 ma_mutex_lock__win32(pMutex);
11336#endif
11337#ifdef MA_POSIX
11338 ma_mutex_lock__posix(pMutex);
11339#endif
11340}
11341
11342MA_API void ma_mutex_unlock(ma_mutex* pMutex)
11343{
11344 if (pMutex == NULL) {
11345 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
11346 return;
11347}
11348
11349#ifdef MA_WIN32
11350 ma_mutex_unlock__win32(pMutex);
11351#endif
11352#ifdef MA_POSIX
11353 ma_mutex_unlock__posix(pMutex);
11354#endif
11355}
11356
11357
11358MA_API ma_result ma_event_init(ma_event* pEvent)
11359{
11360 if (pEvent == NULL) {
11361 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
11362 return MA_INVALID_ARGS;
11363 }
11364
11365#ifdef MA_WIN32
11366 return ma_event_init__win32(pEvent);
11367#endif
11368#ifdef MA_POSIX
11369 return ma_event_init__posix(pEvent);
11370#endif
11371}
11372
11373#if 0
11374static ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callbacks* pAllocationCallbacks)
11375{
11376 ma_result result;
11377 ma_event* pEvent;
11378
11379 if (ppEvent == NULL) {
11380 return MA_INVALID_ARGS;
11381 }
11382
11383 *ppEvent = NULL;
11384
11385 pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks/*, MA_ALLOCATION_TYPE_EVENT*/);
11386 if (pEvent == NULL) {
11387 return MA_OUT_OF_MEMORY;
11388 }
11389
11390 result = ma_event_init(pEvent);
11391 if (result != MA_SUCCESS) {
11392 ma_free(pEvent, pAllocationCallbacks/*, MA_ALLOCATION_TYPE_EVENT*/);
11393 return result;
11394 }
11395
11396 *ppEvent = pEvent;
11397 return result;
11398}
11399#endif
11400
11401MA_API void ma_event_uninit(ma_event* pEvent)
11402{
11403 if (pEvent == NULL) {
11404 return;
11405 }
11406
11407#ifdef MA_WIN32
11408 ma_event_uninit__win32(pEvent);
11409#endif
11410#ifdef MA_POSIX
11411 ma_event_uninit__posix(pEvent);
11412#endif
11413}
11414
11415#if 0
11416static void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* pAllocationCallbacks)
11417{
11418 if (pEvent == NULL) {
11419 return;
11420 }
11421
11422 ma_event_uninit(pEvent);
11423 ma_free(pEvent, pAllocationCallbacks/*, MA_ALLOCATION_TYPE_EVENT*/);
11424}
11425#endif
11426
11427MA_API ma_result ma_event_wait(ma_event* pEvent)
11428{
11429 if (pEvent == NULL) {
11430 MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */
11431 return MA_INVALID_ARGS;
11432 }
11433
11434#ifdef MA_WIN32
11435 return ma_event_wait__win32(pEvent);
11436#endif
11437#ifdef MA_POSIX
11438 return ma_event_wait__posix(pEvent);
11439#endif
11440}
11441
11442MA_API ma_result ma_event_signal(ma_event* pEvent)
11443{
11444 if (pEvent == NULL) {
11445 MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */
11446 return MA_INVALID_ARGS;
11447 }
11448
11449#ifdef MA_WIN32
11450 return ma_event_signal__win32(pEvent);
11451#endif
11452#ifdef MA_POSIX
11453 return ma_event_signal__posix(pEvent);
11454#endif
11455}
11456
11457
11458MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore)
11459{
11460 if (pSemaphore == NULL) {
11461 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
11462 return MA_INVALID_ARGS;
11463 }
11464
11465#ifdef MA_WIN32
11466 return ma_semaphore_init__win32(initialValue, pSemaphore);
11467#endif
11468#ifdef MA_POSIX
11469 return ma_semaphore_init__posix(initialValue, pSemaphore);
11470#endif
11471}
11472
11473MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore)
11474{
11475 if (pSemaphore == NULL) {
11476 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
11477 return;
11478 }
11479
11480#ifdef MA_WIN32
11481 ma_semaphore_uninit__win32(pSemaphore);
11482#endif
11483#ifdef MA_POSIX
11484 ma_semaphore_uninit__posix(pSemaphore);
11485#endif
11486}
11487
11488MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore)
11489{
11490 if (pSemaphore == NULL) {
11491 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
11492 return MA_INVALID_ARGS;
11493 }
11494
11495#ifdef MA_WIN32
11496 return ma_semaphore_wait__win32(pSemaphore);
11497#endif
11498#ifdef MA_POSIX
11499 return ma_semaphore_wait__posix(pSemaphore);
11500#endif
11501}
11502
11503MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore)
11504{
11505 if (pSemaphore == NULL) {
11506 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
11507 return MA_INVALID_ARGS;
11508 }
11509
11510#ifdef MA_WIN32
11511 return ma_semaphore_release__win32(pSemaphore);
11512#endif
11513#ifdef MA_POSIX
11514 return ma_semaphore_release__posix(pSemaphore);
11515#endif
11516}
11517#else
11518/* 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. */
11519#ifndef MA_NO_DEVICE_IO
11520#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO";
11521#endif
11522#endif /* MA_NO_THREADING */
11523
11524
11525
11526/************************************************************************************************************************************************************
11527*************************************************************************************************************************************************************
11528
11529DEVICE I/O
11530==========
11531
11532*************************************************************************************************************************************************************
11533************************************************************************************************************************************************************/
11534#ifndef MA_NO_DEVICE_IO
11535#ifdef MA_WIN32
11536 #include <objbase.h>
11537 #include <mmreg.h>
11538 #include <mmsystem.h>
11539#endif
11540
11541#if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
11542 #include <mach/mach_time.h> /* For mach_absolute_time() */
11543#endif
11544
11545#ifdef MA_POSIX
11546 #include <sys/types.h>
11547 #include <unistd.h>
11548 #include <dlfcn.h>
11549#endif
11550
11551/*
11552Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When
11553using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing
11554compile-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
11555disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am
11556not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere.
11557*/
11558/*#define MA_USE_RUNTIME_LINKING_FOR_PTHREAD*/
11559
11560/* Disable run-time linking on certain backends. */
11561#ifndef MA_NO_RUNTIME_LINKING
11562 #if defined(MA_EMSCRIPTEN)
11563 #define MA_NO_RUNTIME_LINKING
11564 #endif
11565#endif
11566
11567
11568MA_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)
11569{
11570 if (pDeviceInfo == NULL) {
11571 return;
11572 }
11573
11574 if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) {
11575 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
11576 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
11577 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
11578 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
11579 pDeviceInfo->nativeDataFormatCount += 1;
11580 }
11581}
11582
11583
11584MA_API const char* ma_get_backend_name(ma_backend backend)
11585{
11586 switch (backend)
11587 {
11588 case ma_backend_wasapi: return "WASAPI";
11589 case ma_backend_dsound: return "DirectSound";
11590 case ma_backend_winmm: return "WinMM";
11591 case ma_backend_coreaudio: return "Core Audio";
11592 case ma_backend_sndio: return "sndio";
11593 case ma_backend_audio4: return "audio(4)";
11594 case ma_backend_oss: return "OSS";
11595 case ma_backend_pulseaudio: return "PulseAudio";
11596 case ma_backend_alsa: return "ALSA";
11597 case ma_backend_jack: return "JACK";
11598 case ma_backend_aaudio: return "AAudio";
11599 case ma_backend_opensl: return "OpenSL|ES";
11600 case ma_backend_webaudio: return "Web Audio";
11601 case ma_backend_custom: return "Custom";
11602 case ma_backend_null: return "Null";
11603 default: return "Unknown";
11604 }
11605}
11606
11607MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend)
11608{
11609 /*
11610 This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers
11611 about some enums not being handled by the switch statement.
11612 */
11613 switch (backend)
11614 {
11615 case ma_backend_wasapi:
11616 #if defined(MA_HAS_WASAPI)
11617 return MA_TRUE;
11618 #else
11619 return MA_FALSE;
11620 #endif
11621 case ma_backend_dsound:
11622 #if defined(MA_HAS_DSOUND)
11623 return MA_TRUE;
11624 #else
11625 return MA_FALSE;
11626 #endif
11627 case ma_backend_winmm:
11628 #if defined(MA_HAS_WINMM)
11629 return MA_TRUE;
11630 #else
11631 return MA_FALSE;
11632 #endif
11633 case ma_backend_coreaudio:
11634 #if defined(MA_HAS_COREAUDIO)
11635 return MA_TRUE;
11636 #else
11637 return MA_FALSE;
11638 #endif
11639 case ma_backend_sndio:
11640 #if defined(MA_HAS_SNDIO)
11641 return MA_TRUE;
11642 #else
11643 return MA_FALSE;
11644 #endif
11645 case ma_backend_audio4:
11646 #if defined(MA_HAS_AUDIO4)
11647 return MA_TRUE;
11648 #else
11649 return MA_FALSE;
11650 #endif
11651 case ma_backend_oss:
11652 #if defined(MA_HAS_OSS)
11653 return MA_TRUE;
11654 #else
11655 return MA_FALSE;
11656 #endif
11657 case ma_backend_pulseaudio:
11658 #if defined(MA_HAS_PULSEAUDIO)
11659 return MA_TRUE;
11660 #else
11661 return MA_FALSE;
11662 #endif
11663 case ma_backend_alsa:
11664 #if defined(MA_HAS_ALSA)
11665 return MA_TRUE;
11666 #else
11667 return MA_FALSE;
11668 #endif
11669 case ma_backend_jack:
11670 #if defined(MA_HAS_JACK)
11671 return MA_TRUE;
11672 #else
11673 return MA_FALSE;
11674 #endif
11675 case ma_backend_aaudio:
11676 #if defined(MA_HAS_AAUDIO)
11677 return MA_TRUE;
11678 #else
11679 return MA_FALSE;
11680 #endif
11681 case ma_backend_opensl:
11682 #if defined(MA_HAS_OPENSL)
11683 return MA_TRUE;
11684 #else
11685 return MA_FALSE;
11686 #endif
11687 case ma_backend_webaudio:
11688 #if defined(MA_HAS_WEBAUDIO)
11689 return MA_TRUE;
11690 #else
11691 return MA_FALSE;
11692 #endif
11693 case ma_backend_custom:
11694 #if defined(MA_HAS_CUSTOM)
11695 return MA_TRUE;
11696 #else
11697 return MA_FALSE;
11698 #endif
11699 case ma_backend_null:
11700 #if defined(MA_HAS_NULL)
11701 return MA_TRUE;
11702 #else
11703 return MA_FALSE;
11704 #endif
11705
11706 default: return MA_FALSE;
11707 }
11708}
11709
11710MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount)
11711{
11712 size_t backendCount;
11713 size_t iBackend;
11714 ma_result result = MA_SUCCESS;
11715
11716 if (pBackendCount == NULL) {
11717 return MA_INVALID_ARGS;
11718 }
11719
11720 backendCount = 0;
11721
11722 for (iBackend = 0; iBackend <= ma_backend_null; iBackend += 1) {
11723 ma_backend backend = (ma_backend)iBackend;
11724
11725 if (ma_is_backend_enabled(backend)) {
11726 /* The backend is enabled. Try adding it to the list. If there's no room, MA_NO_SPACE needs to be returned. */
11727 if (backendCount == backendCap) {
11728 result = MA_NO_SPACE;
11729 break;
11730 } else {
11731 pBackends[backendCount] = backend;
11732 backendCount += 1;
11733 }
11734 }
11735 }
11736
11737 if (pBackendCount != NULL) {
11738 *pBackendCount = backendCount;
11739 }
11740
11741 return result;
11742}
11743
11744MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend)
11745{
11746 switch (backend)
11747 {
11748 case ma_backend_wasapi: return MA_TRUE;
11749 case ma_backend_dsound: return MA_FALSE;
11750 case ma_backend_winmm: return MA_FALSE;
11751 case ma_backend_coreaudio: return MA_FALSE;
11752 case ma_backend_sndio: return MA_FALSE;
11753 case ma_backend_audio4: return MA_FALSE;
11754 case ma_backend_oss: return MA_FALSE;
11755 case ma_backend_pulseaudio: return MA_FALSE;
11756 case ma_backend_alsa: return MA_FALSE;
11757 case ma_backend_jack: return MA_FALSE;
11758 case ma_backend_aaudio: return MA_FALSE;
11759 case ma_backend_opensl: return MA_FALSE;
11760 case ma_backend_webaudio: return MA_FALSE;
11761 case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */
11762 case ma_backend_null: return MA_FALSE;
11763 default: return MA_FALSE;
11764 }
11765}
11766
11767
11768
11769#ifdef MA_WIN32
11770/* WASAPI error codes. */
11771#define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001)
11772#define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002)
11773#define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003)
11774#define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004)
11775#define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005)
11776#define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006)
11777#define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007)
11778#define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008)
11779#define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009)
11780#define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A)
11781#define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B)
11782#define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C)
11783#define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D)
11784#define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E)
11785#define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F)
11786#define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010)
11787#define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011)
11788#define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012)
11789#define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013)
11790#define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014)
11791#define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015)
11792#define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016)
11793#define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017)
11794#define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018)
11795#define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019)
11796#define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020)
11797#define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021)
11798#define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022)
11799#define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023)
11800#define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024)
11801#define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025)
11802#define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026)
11803#define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027)
11804#define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028)
11805#define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029)
11806#define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030)
11807#define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040)
11808#define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001)
11809#define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002)
11810#define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003)
11811
11812#define MA_DS_OK ((HRESULT)0)
11813#define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A)
11814#define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A)
11815#define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E)
11816#define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/
11817#define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032)
11818#define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/
11819#define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046)
11820#define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/
11821#define MA_DSERR_BADFORMAT ((HRESULT)0x88780064)
11822#define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/
11823#define MA_DSERR_NODRIVER ((HRESULT)0x88780078)
11824#define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082)
11825#define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/
11826#define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096)
11827#define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0)
11828#define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA)
11829#define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/
11830#define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/
11831#define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4)
11832#define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE)
11833#define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8)
11834#define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2)
11835#define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161)
11836#define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC)
11837
11838static ma_result ma_result_from_HRESULT(HRESULT hr)
11839{
11840 switch (hr)
11841 {
11842 case NOERROR: return MA_SUCCESS;
11843 /*case S_OK: return MA_SUCCESS;*/
11844
11845 case E_POINTER: return MA_INVALID_ARGS;
11846 case E_UNEXPECTED: return MA_ERROR;
11847 case E_NOTIMPL: return MA_NOT_IMPLEMENTED;
11848 case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY;
11849 case E_INVALIDARG: return MA_INVALID_ARGS;
11850 case E_NOINTERFACE: return MA_API_NOT_FOUND;
11851 case E_HANDLE: return MA_INVALID_ARGS;
11852 case E_ABORT: return MA_ERROR;
11853 case E_FAIL: return MA_ERROR;
11854 case E_ACCESSDENIED: return MA_ACCESS_DENIED;
11855
11856 /* WASAPI */
11857 case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
11858 case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
11859 case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS;
11860 case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE;
11861 case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED;
11862 case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG;
11863 case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION;
11864 case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED;
11865 case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS;
11866 case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY;
11867 case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION;
11868 case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST;
11869 case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION;
11870 case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED;
11871 case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
11872 case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED;
11873 case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS;
11874 case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED;
11875 case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS;
11876 case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS;
11877 case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS;
11878 case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS;
11879 case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR;
11880 case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR;
11881 case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS;
11882 case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS;
11883 case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS;
11884 case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION;
11885 case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY;
11886 case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
11887 case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
11888 case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA;
11889 case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION;
11890 case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION;
11891 case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION;
11892 case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION;
11893 case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION;
11894 case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE;
11895 case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS;
11896 case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR;
11897
11898 /* DirectSound */
11899 /*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */
11900 case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS;
11901 case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE;
11902 case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION;
11903 /*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */
11904 case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION;
11905 /*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */
11906 case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION;
11907 /*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */
11908 case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED;
11909 /*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */
11910 case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND;
11911 case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
11912 case MA_DSERR_NOAGGREGATION: return MA_ERROR;
11913 case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE;
11914 case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED;
11915 case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
11916 /*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */
11917 /*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */
11918 case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE;
11919 case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION;
11920 case MA_DSERR_SENDLOOP: return MA_DEADLOCK;
11921 case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS;
11922 case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE;
11923 case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE;
11924
11925 default: return MA_ERROR;
11926 }
11927}
11928
11929typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit);
11930typedef void (WINAPI * MA_PFN_CoUninitialize)(void);
11931typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv);
11932typedef void (WINAPI * MA_PFN_CoTaskMemFree)(LPVOID pv);
11933typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(PROPVARIANT *pvar);
11934typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax);
11935
11936typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void);
11937typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void);
11938
11939/* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */
11940typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult);
11941typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey);
11942typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData);
11943#endif
11944
11945
11946#define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device"
11947#define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device"
11948
11949
11950/* Posts a log message. */
11951static void ma_post_log_message(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
11952{
11953 if (pContext == NULL) {
11954 if (pDevice != NULL) {
11955 pContext = pDevice->pContext;
11956 }
11957 }
11958
11959 if (pContext == NULL) {
11960 return;
11961 }
11962
11963 ma_log_post(ma_context_get_log(pContext), logLevel, message); /* <-- This will deal with MA_DEBUG_OUTPUT. */
11964
11965 /* Legacy. */
11966#if defined(MA_LOG_LEVEL)
11967 if (logLevel <= MA_LOG_LEVEL) {
11968 ma_log_proc onLog;
11969
11970 onLog = pContext->logCallback;
11971 if (onLog) {
11972 onLog(pContext, pDevice, logLevel, message);
11973 }
11974 }
11975#endif
11976}
11977
11978/* Posts an log message. Throw a breakpoint in here if you're needing to debug. The return value is always "resultCode". */
11979static ma_result ma_context_post_error(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode)
11980{
11981 ma_post_log_message(pContext, pDevice, logLevel, message);
11982 return resultCode;
11983}
11984
11985static ma_result ma_post_error(ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode)
11986{
11987 return ma_context_post_error(ma_device_get_context(pDevice), pDevice, logLevel, message, resultCode);
11988}
11989
11990
11991
11992
11993/*******************************************************************************
11994
11995Timing
11996
11997*******************************************************************************/
11998#ifdef MA_WIN32
11999 static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */
12000 static void ma_timer_init(ma_timer* pTimer)
12001 {
12002 LARGE_INTEGER counter;
12003
12004 if (g_ma_TimerFrequency.QuadPart == 0) {
12005 QueryPerformanceFrequency(&g_ma_TimerFrequency);
12006 }
12007
12008 QueryPerformanceCounter(&counter);
12009 pTimer->counter = counter.QuadPart;
12010 }
12011
12012 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
12013 {
12014 LARGE_INTEGER counter;
12015 if (!QueryPerformanceCounter(&counter)) {
12016 return 0;
12017 }
12018
12019 return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;
12020 }
12021#elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
12022 static ma_uint64 g_ma_TimerFrequency = 0;
12023 static void ma_timer_init(ma_timer* pTimer)
12024 {
12025 mach_timebase_info_data_t baseTime;
12026 mach_timebase_info(&baseTime);
12027 g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;
12028
12029 pTimer->counter = mach_absolute_time();
12030 }
12031
12032 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
12033 {
12034 ma_uint64 newTimeCounter = mach_absolute_time();
12035 ma_uint64 oldTimeCounter = pTimer->counter;
12036
12037 return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;
12038 }
12039#elif defined(MA_EMSCRIPTEN)
12040 static MA_INLINE void ma_timer_init(ma_timer* pTimer)
12041 {
12042 pTimer->counterD = emscripten_get_now();
12043 }
12044
12045 static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
12046 {
12047 return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */
12048 }
12049#else
12050 #if _POSIX_C_SOURCE >= 199309L
12051 #if defined(CLOCK_MONOTONIC)
12052 #define MA_CLOCK_ID CLOCK_MONOTONIC
12053 #else
12054 #define MA_CLOCK_ID CLOCK_REALTIME
12055 #endif
12056
12057 static void ma_timer_init(ma_timer* pTimer)
12058 {
12059 struct timespec newTime;
12060 clock_gettime(MA_CLOCK_ID, &newTime);
12061
12062 pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
12063 }
12064
12065 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
12066 {
12067 ma_uint64 newTimeCounter;
12068 ma_uint64 oldTimeCounter;
12069
12070 struct timespec newTime;
12071 clock_gettime(MA_CLOCK_ID, &newTime);
12072
12073 newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
12074 oldTimeCounter = pTimer->counter;
12075
12076 return (newTimeCounter - oldTimeCounter) / 1000000000.0;
12077 }
12078 #else
12079 static void ma_timer_init(ma_timer* pTimer)
12080 {
12081 struct timeval newTime;
12082 gettimeofday(&newTime, NULL);
12083
12084 pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
12085 }
12086
12087 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
12088 {
12089 ma_uint64 newTimeCounter;
12090 ma_uint64 oldTimeCounter;
12091
12092 struct timeval newTime;
12093 gettimeofday(&newTime, NULL);
12094
12095 newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
12096 oldTimeCounter = pTimer->counter;
12097
12098 return (newTimeCounter - oldTimeCounter) / 1000000.0;
12099 }
12100 #endif
12101#endif
12102
12103
12104/*******************************************************************************
12105
12106Dynamic Linking
12107
12108*******************************************************************************/
12109MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename)
12110{
12111 ma_handle handle;
12112
12113 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename);
12114
12115#ifdef _WIN32
12116#ifdef MA_WIN32_DESKTOP
12117 handle = (ma_handle)LoadLibraryA(filename);
12118#else
12119 /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */
12120 WCHAR filenameW[4096];
12121 if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) {
12122 handle = NULL;
12123 } else {
12124 handle = (ma_handle)LoadPackagedLibrary(filenameW, 0);
12125 }
12126#endif
12127#else
12128 handle = (ma_handle)dlopen(filename, RTLD_NOW);
12129#endif
12130
12131 /*
12132 I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority
12133 backend is a deliberate design choice. Instead I'm logging it as an informational message.
12134 */
12135 if (handle == NULL) {
12136 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename);
12137 }
12138
12139 (void)pContext; /* It's possible for pContext to be unused. */
12140 return handle;
12141}
12142
12143MA_API void ma_dlclose(ma_context* pContext, ma_handle handle)
12144{
12145#ifdef _WIN32
12146 FreeLibrary((HMODULE)handle);
12147#else
12148 dlclose((void*)handle);
12149#endif
12150
12151 (void)pContext;
12152}
12153
12154MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol)
12155{
12156 ma_proc proc;
12157
12158 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol);
12159
12160#ifdef _WIN32
12161 proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol);
12162#else
12163#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
12164 #pragma GCC diagnostic push
12165 #pragma GCC diagnostic ignored "-Wpedantic"
12166#endif
12167 proc = (ma_proc)dlsym((void*)handle, symbol);
12168#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
12169 #pragma GCC diagnostic pop
12170#endif
12171#endif
12172
12173 if (proc == NULL) {
12174 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol);
12175 }
12176
12177 (void)pContext; /* It's possible for pContext to be unused. */
12178 return proc;
12179}
12180
12181
12182#if 0
12183static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn)
12184{
12185 ma_uint32 closestRate = 0;
12186 ma_uint32 closestDiff = 0xFFFFFFFF;
12187 size_t iStandardRate;
12188
12189 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
12190 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
12191 ma_uint32 diff;
12192
12193 if (sampleRateIn > standardRate) {
12194 diff = sampleRateIn - standardRate;
12195 } else {
12196 diff = standardRate - sampleRateIn;
12197 }
12198
12199 if (diff == 0) {
12200 return standardRate; /* The input sample rate is a standard rate. */
12201 }
12202
12203 if (closestDiff > diff) {
12204 closestDiff = diff;
12205 closestRate = standardRate;
12206 }
12207 }
12208
12209 return closestRate;
12210}
12211#endif
12212
12213
12214static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
12215{
12216 float masterVolumeFactor;
12217
12218 ma_device_get_master_volume(pDevice, &masterVolumeFactor); /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */
12219
12220 if (pDevice->onData) {
12221 if (!pDevice->noPreZeroedOutputBuffer && pFramesOut != NULL) {
12222 ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels);
12223 }
12224
12225 /* 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. */
12226 if (pFramesIn != NULL && masterVolumeFactor < 1) {
12227 ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
12228 ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
12229 ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
12230 ma_uint32 totalFramesProcessed = 0;
12231 while (totalFramesProcessed < frameCount) {
12232 ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed;
12233 if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) {
12234 framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture;
12235 }
12236
12237 ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor);
12238
12239 pDevice->onData(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration);
12240
12241 totalFramesProcessed += framesToProcessThisIteration;
12242 }
12243 } else {
12244 pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount);
12245 }
12246
12247 /* Volume control and clipping for playback devices. */
12248 if (pFramesOut != NULL) {
12249 if (masterVolumeFactor < 1) {
12250 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. */
12251 ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor);
12252 }
12253 }
12254
12255 if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) {
12256 ma_clip_pcm_frames_f32((float*)pFramesOut, frameCount, pDevice->playback.channels);
12257 }
12258 }
12259 }
12260}
12261
12262
12263
12264/* A helper function for reading sample data from the client. */
12265static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut)
12266{
12267 MA_ASSERT(pDevice != NULL);
12268 MA_ASSERT(frameCount > 0);
12269 MA_ASSERT(pFramesOut != NULL);
12270
12271 if (pDevice->playback.converter.isPassthrough) {
12272 ma_device__on_data(pDevice, pFramesOut, NULL, frameCount);
12273 } else {
12274 ma_result result;
12275 ma_uint64 totalFramesReadOut;
12276 ma_uint64 totalFramesReadIn;
12277 void* pRunningFramesOut;
12278
12279 totalFramesReadOut = 0;
12280 totalFramesReadIn = 0;
12281 pRunningFramesOut = pFramesOut;
12282
12283 while (totalFramesReadOut < frameCount) {
12284 ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */
12285 ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
12286 ma_uint64 framesToReadThisIterationIn;
12287 ma_uint64 framesReadThisIterationIn;
12288 ma_uint64 framesToReadThisIterationOut;
12289 ma_uint64 framesReadThisIterationOut;
12290 ma_uint64 requiredInputFrameCount;
12291
12292 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
12293 framesToReadThisIterationIn = framesToReadThisIterationOut;
12294 if (framesToReadThisIterationIn > intermediaryBufferCap) {
12295 framesToReadThisIterationIn = intermediaryBufferCap;
12296 }
12297
12298 requiredInputFrameCount = ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut);
12299 if (framesToReadThisIterationIn > requiredInputFrameCount) {
12300 framesToReadThisIterationIn = requiredInputFrameCount;
12301 }
12302
12303 if (framesToReadThisIterationIn > 0) {
12304 ma_device__on_data(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn);
12305 totalFramesReadIn += framesToReadThisIterationIn;
12306 }
12307
12308 /*
12309 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
12310 input frames, we still want to try processing frames because there may some output frames generated from cached input data.
12311 */
12312 framesReadThisIterationIn = framesToReadThisIterationIn;
12313 framesReadThisIterationOut = framesToReadThisIterationOut;
12314 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
12315 if (result != MA_SUCCESS) {
12316 break;
12317 }
12318
12319 totalFramesReadOut += framesReadThisIterationOut;
12320 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
12321
12322 if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
12323 break; /* We're done. */
12324 }
12325 }
12326 }
12327}
12328
12329/* A helper for sending sample data to the client. */
12330static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat)
12331{
12332 MA_ASSERT(pDevice != NULL);
12333 MA_ASSERT(frameCountInDeviceFormat > 0);
12334 MA_ASSERT(pFramesInDeviceFormat != NULL);
12335
12336 if (pDevice->capture.converter.isPassthrough) {
12337 ma_device__on_data(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat);
12338 } else {
12339 ma_result result;
12340 ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
12341 ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
12342 ma_uint64 totalDeviceFramesProcessed = 0;
12343 ma_uint64 totalClientFramesProcessed = 0;
12344 const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
12345
12346 /* We just keep going until we've exhaused all of our input frames and cannot generate any more output frames. */
12347 for (;;) {
12348 ma_uint64 deviceFramesProcessedThisIteration;
12349 ma_uint64 clientFramesProcessedThisIteration;
12350
12351 deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
12352 clientFramesProcessedThisIteration = framesInClientFormatCap;
12353
12354 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration);
12355 if (result != MA_SUCCESS) {
12356 break;
12357 }
12358
12359 if (clientFramesProcessedThisIteration > 0) {
12360 ma_device__on_data(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */
12361 }
12362
12363 pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
12364 totalDeviceFramesProcessed += deviceFramesProcessedThisIteration;
12365 totalClientFramesProcessed += clientFramesProcessedThisIteration;
12366
12367 if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) {
12368 break; /* We're done. */
12369 }
12370 }
12371 }
12372}
12373
12374static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB)
12375{
12376 ma_result result;
12377 ma_uint32 totalDeviceFramesProcessed = 0;
12378 const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
12379
12380 MA_ASSERT(pDevice != NULL);
12381 MA_ASSERT(frameCountInDeviceFormat > 0);
12382 MA_ASSERT(pFramesInDeviceFormat != NULL);
12383 MA_ASSERT(pRB != NULL);
12384
12385 /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */
12386 for (;;) {
12387 ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
12388 ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
12389 ma_uint64 framesProcessedInDeviceFormat;
12390 ma_uint64 framesProcessedInClientFormat;
12391 void* pFramesInClientFormat;
12392
12393 result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat);
12394 if (result != MA_SUCCESS) {
12395 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer.", result);
12396 break;
12397 }
12398
12399 if (framesToProcessInClientFormat == 0) {
12400 if (ma_pcm_rb_pointer_distance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) {
12401 break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */
12402 }
12403 }
12404
12405 /* Convert. */
12406 framesProcessedInDeviceFormat = framesToProcessInDeviceFormat;
12407 framesProcessedInClientFormat = framesToProcessInClientFormat;
12408 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat);
12409 if (result != MA_SUCCESS) {
12410 break;
12411 }
12412
12413 result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat, pFramesInClientFormat); /* Safe cast. */
12414 if (result != MA_SUCCESS) {
12415 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer.", result);
12416 break;
12417 }
12418
12419 pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
12420 totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */
12421
12422 /* We're done when we're unable to process any client nor device frames. */
12423 if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) {
12424 break; /* Done. */
12425 }
12426 }
12427
12428 return MA_SUCCESS;
12429}
12430
12431static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB)
12432{
12433 ma_result result;
12434 ma_uint8 playbackFramesInExternalFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
12435 ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
12436 ma_uint32 totalFramesToReadFromClient;
12437 ma_uint32 totalFramesReadFromClient;
12438 ma_uint32 totalFramesReadOut = 0;
12439
12440 MA_ASSERT(pDevice != NULL);
12441 MA_ASSERT(frameCount > 0);
12442 MA_ASSERT(pFramesInInternalFormat != NULL);
12443 MA_ASSERT(pRB != NULL);
12444
12445 /*
12446 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
12447 the whole frameCount frames we just use silence instead for the input data.
12448 */
12449 MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames));
12450
12451 /* We need to calculate how many output frames are required to be read from the client to completely fill frameCount internal frames. */
12452 totalFramesToReadFromClient = (ma_uint32)ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, frameCount);
12453 totalFramesReadFromClient = 0;
12454 while (totalFramesReadFromClient < totalFramesToReadFromClient && ma_device_is_started(pDevice)) {
12455 ma_uint32 framesRemainingFromClient;
12456 ma_uint32 framesToProcessFromClient;
12457 ma_uint32 inputFrameCount;
12458 void* pInputFrames;
12459
12460 framesRemainingFromClient = (totalFramesToReadFromClient - totalFramesReadFromClient);
12461 framesToProcessFromClient = sizeof(playbackFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
12462 if (framesToProcessFromClient > framesRemainingFromClient) {
12463 framesToProcessFromClient = framesRemainingFromClient;
12464 }
12465
12466 /* We need to grab captured samples before firing the callback. If there's not enough input samples we just pass silence. */
12467 inputFrameCount = framesToProcessFromClient;
12468 result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames);
12469 if (result == MA_SUCCESS) {
12470 if (inputFrameCount > 0) {
12471 /* Use actual input frames. */
12472 ma_device__on_data(pDevice, playbackFramesInExternalFormat, pInputFrames, inputFrameCount);
12473 } else {
12474 if (ma_pcm_rb_pointer_distance(pRB) == 0) {
12475 break; /* Underrun. */
12476 }
12477 }
12478
12479 /* We're done with the captured samples. */
12480 result = ma_pcm_rb_commit_read(pRB, inputFrameCount, pInputFrames);
12481 if (result != MA_SUCCESS) {
12482 break; /* Don't know what to do here... Just abandon ship. */
12483 }
12484 } else {
12485 /* Use silent input frames. */
12486 inputFrameCount = ma_min(
12487 sizeof(playbackFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels),
12488 sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)
12489 );
12490
12491 ma_device__on_data(pDevice, playbackFramesInExternalFormat, silentInputFrames, inputFrameCount);
12492 }
12493
12494 /* We have samples in external format so now we need to convert to internal format and output to the device. */
12495 {
12496 ma_uint64 framesConvertedIn = inputFrameCount;
12497 ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut);
12498 ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackFramesInExternalFormat, &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut);
12499
12500 totalFramesReadFromClient += (ma_uint32)framesConvertedIn; /* Safe cast. */
12501 totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */
12502 pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
12503 }
12504 }
12505
12506 return MA_SUCCESS;
12507}
12508
12509/* A helper for changing the state of the device. */
12510static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_uint32 newState)
12511{
12512 c89atomic_exchange_32(&pDevice->state, newState);
12513}
12514
12515
12516#ifdef MA_WIN32
12517 GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
12518 GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
12519 /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
12520 /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
12521#endif
12522
12523
12524
12525MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */
12526{
12527 ma_uint32 i;
12528 for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) {
12529 if (g_maFormatPriorities[i] == format) {
12530 return i;
12531 }
12532 }
12533
12534 /* Getting here means the format could not be found or is equal to ma_format_unknown. */
12535 return (ma_uint32)-1;
12536}
12537
12538static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType);
12539
12540
12541static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor)
12542{
12543 if (pDeviceDescriptor == NULL) {
12544 return MA_FALSE;
12545 }
12546
12547 if (pDeviceDescriptor->format == ma_format_unknown) {
12548 return MA_FALSE;
12549 }
12550
12551 if (pDeviceDescriptor->channels < MA_MIN_CHANNELS || pDeviceDescriptor->channels > MA_MAX_CHANNELS) {
12552 return MA_FALSE;
12553 }
12554
12555 if (pDeviceDescriptor->sampleRate == 0) {
12556 return MA_FALSE;
12557 }
12558
12559 return MA_TRUE;
12560}
12561
12562
12563static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice)
12564{
12565 ma_result result = MA_SUCCESS;
12566 ma_bool32 exitLoop = MA_FALSE;
12567 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
12568 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
12569 ma_uint32 capturedDeviceDataCapInFrames = 0;
12570 ma_uint32 playbackDeviceDataCapInFrames = 0;
12571
12572 MA_ASSERT(pDevice != NULL);
12573
12574 /* Just some quick validation on the device type and the available callbacks. */
12575 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
12576 if (pDevice->pContext->callbacks.onDeviceRead == NULL) {
12577 return MA_NOT_IMPLEMENTED;
12578 }
12579
12580 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
12581 }
12582
12583 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
12584 if (pDevice->pContext->callbacks.onDeviceWrite == NULL) {
12585 return MA_NOT_IMPLEMENTED;
12586 }
12587
12588 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
12589 }
12590
12591 /* NOTE: The device was started outside of this function, in the worker thread. */
12592
12593 while (ma_device_get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
12594 switch (pDevice->type) {
12595 case ma_device_type_duplex:
12596 {
12597 /* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */
12598 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
12599 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
12600
12601 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
12602 ma_uint32 capturedDeviceFramesRemaining;
12603 ma_uint32 capturedDeviceFramesProcessed;
12604 ma_uint32 capturedDeviceFramesToProcess;
12605 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
12606 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
12607 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
12608 }
12609
12610 result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
12611 if (result != MA_SUCCESS) {
12612 exitLoop = MA_TRUE;
12613 break;
12614 }
12615
12616 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
12617 capturedDeviceFramesProcessed = 0;
12618
12619 /* At this point we have our captured data in device format and we now need to convert it to client format. */
12620 for (;;) {
12621 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
12622 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
12623 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
12624 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
12625 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
12626 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
12627 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
12628
12629 /* Convert capture data from device format to client format. */
12630 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
12631 if (result != MA_SUCCESS) {
12632 break;
12633 }
12634
12635 /*
12636 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
12637 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
12638 */
12639 if (capturedClientFramesToProcessThisIteration == 0) {
12640 break;
12641 }
12642
12643 ma_device__on_data(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
12644
12645 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
12646 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
12647
12648 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
12649 for (;;) {
12650 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
12651 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
12652 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
12653 if (result != MA_SUCCESS) {
12654 break;
12655 }
12656
12657 result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
12658 if (result != MA_SUCCESS) {
12659 exitLoop = MA_TRUE;
12660 break;
12661 }
12662
12663 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
12664 if (capturedClientFramesToProcessThisIteration == 0) {
12665 break;
12666 }
12667 }
12668
12669 /* In case an error happened from ma_device_write__null()... */
12670 if (result != MA_SUCCESS) {
12671 exitLoop = MA_TRUE;
12672 break;
12673 }
12674 }
12675
12676 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
12677 }
12678 } break;
12679
12680 case ma_device_type_capture:
12681 case ma_device_type_loopback:
12682 {
12683 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
12684 ma_uint32 framesReadThisPeriod = 0;
12685 while (framesReadThisPeriod < periodSizeInFrames) {
12686 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
12687 ma_uint32 framesProcessed;
12688 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
12689 if (framesToReadThisIteration > capturedDeviceDataCapInFrames) {
12690 framesToReadThisIteration = capturedDeviceDataCapInFrames;
12691 }
12692
12693 result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed);
12694 if (result != MA_SUCCESS) {
12695 exitLoop = MA_TRUE;
12696 break;
12697 }
12698
12699 ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData);
12700
12701 framesReadThisPeriod += framesProcessed;
12702 }
12703 } break;
12704
12705 case ma_device_type_playback:
12706 {
12707 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
12708 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
12709 ma_uint32 framesWrittenThisPeriod = 0;
12710 while (framesWrittenThisPeriod < periodSizeInFrames) {
12711 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
12712 ma_uint32 framesProcessed;
12713 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
12714 if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) {
12715 framesToWriteThisIteration = playbackDeviceDataCapInFrames;
12716 }
12717
12718 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData);
12719
12720 result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed);
12721 if (result != MA_SUCCESS) {
12722 exitLoop = MA_TRUE;
12723 break;
12724 }
12725
12726 framesWrittenThisPeriod += framesProcessed;
12727 }
12728 } break;
12729
12730 /* Should never get here. */
12731 default: break;
12732 }
12733 }
12734
12735 return result;
12736}
12737
12738
12739
12740/*******************************************************************************
12741
12742Null Backend
12743
12744*******************************************************************************/
12745#ifdef MA_HAS_NULL
12746
12747#define MA_DEVICE_OP_NONE__NULL 0
12748#define MA_DEVICE_OP_START__NULL 1
12749#define MA_DEVICE_OP_SUSPEND__NULL 2
12750#define MA_DEVICE_OP_KILL__NULL 3
12751
12752static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData)
12753{
12754 ma_device* pDevice = (ma_device*)pData;
12755 MA_ASSERT(pDevice != NULL);
12756
12757 for (;;) { /* Keep the thread alive until the device is uninitialized. */
12758 ma_uint32 operation;
12759
12760 /* Wait for an operation to be requested. */
12761 ma_event_wait(&pDevice->null_device.operationEvent);
12762
12763 /* At this point an event should have been triggered. */
12764 operation = pDevice->null_device.operation;
12765
12766 /* Starting the device needs to put the thread into a loop. */
12767 if (operation == MA_DEVICE_OP_START__NULL) {
12768 /* Reset the timer just in case. */
12769 ma_timer_init(&pDevice->null_device.timer);
12770
12771 /* Getting here means a suspend or kill operation has been requested. */
12772 pDevice->null_device.operationResult = MA_SUCCESS;
12773 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
12774 ma_semaphore_release(&pDevice->null_device.operationSemaphore);
12775 continue;
12776 }
12777
12778 /* Suspending the device means we need to stop the timer and just continue the loop. */
12779 if (operation == MA_DEVICE_OP_SUSPEND__NULL) {
12780 /* We need to add the current run time to the prior run time, then reset the timer. */
12781 pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);
12782 ma_timer_init(&pDevice->null_device.timer);
12783
12784 /* We're done. */
12785 pDevice->null_device.operationResult = MA_SUCCESS;
12786 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
12787 ma_semaphore_release(&pDevice->null_device.operationSemaphore);
12788 continue;
12789 }
12790
12791 /* Killing the device means we need to get out of this loop so that this thread can terminate. */
12792 if (operation == MA_DEVICE_OP_KILL__NULL) {
12793 pDevice->null_device.operationResult = MA_SUCCESS;
12794 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
12795 ma_semaphore_release(&pDevice->null_device.operationSemaphore);
12796 break;
12797 }
12798
12799 /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */
12800 if (operation == MA_DEVICE_OP_NONE__NULL) {
12801 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). */
12802 pDevice->null_device.operationResult = MA_INVALID_OPERATION;
12803 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
12804 ma_semaphore_release(&pDevice->null_device.operationSemaphore);
12805 continue; /* Continue the loop. Don't terminate. */
12806 }
12807 }
12808
12809 return (ma_thread_result)0;
12810}
12811
12812static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation)
12813{
12814 ma_result result;
12815
12816 /*
12817 TODO: Need to review this and consider just using mutual exclusion. I think the original motivation
12818 for this was to just post the event to a queue and return immediately, but that has since changed
12819 and now this function is synchronous. I think this can be simplified to just use a mutex.
12820 */
12821
12822 /*
12823 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
12824 to support queing of operations.
12825 */
12826 result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore);
12827 if (result != MA_SUCCESS) {
12828 return result; /* Failed to wait for the event. */
12829 }
12830
12831 /*
12832 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
12833 signal an event to the worker thread to let it know that it can start work.
12834 */
12835 pDevice->null_device.operation = operation;
12836
12837 /* Once the operation code has been set, the worker thread can start work. */
12838 if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) {
12839 return MA_ERROR;
12840 }
12841
12842 /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */
12843 if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) {
12844 return MA_ERROR;
12845 }
12846
12847 return pDevice->null_device.operationResult;
12848}
12849
12850static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)
12851{
12852 ma_uint32 internalSampleRate;
12853 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
12854 internalSampleRate = pDevice->capture.internalSampleRate;
12855 } else {
12856 internalSampleRate = pDevice->playback.internalSampleRate;
12857 }
12858
12859 return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);
12860}
12861
12862static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
12863{
12864 ma_bool32 cbResult = MA_TRUE;
12865
12866 MA_ASSERT(pContext != NULL);
12867 MA_ASSERT(callback != NULL);
12868
12869 /* Playback. */
12870 if (cbResult) {
12871 ma_device_info deviceInfo;
12872 MA_ZERO_OBJECT(&deviceInfo);
12873 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1);
12874 deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
12875 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
12876 }
12877
12878 /* Capture. */
12879 if (cbResult) {
12880 ma_device_info deviceInfo;
12881 MA_ZERO_OBJECT(&deviceInfo);
12882 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1);
12883 deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
12884 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
12885 }
12886
12887 (void)cbResult; /* Silence a static analysis warning. */
12888
12889 return MA_SUCCESS;
12890}
12891
12892static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
12893{
12894 MA_ASSERT(pContext != NULL);
12895
12896 if (pDeviceID != NULL && pDeviceID->nullbackend != 0) {
12897 return MA_NO_DEVICE; /* Don't know the device. */
12898 }
12899
12900 /* Name / Description */
12901 if (deviceType == ma_device_type_playback) {
12902 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1);
12903 } else {
12904 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1);
12905 }
12906
12907 pDeviceInfo->isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
12908
12909 /* Support everything on the null backend. */
12910 pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown;
12911 pDeviceInfo->nativeDataFormats[0].channels = 0;
12912 pDeviceInfo->nativeDataFormats[0].sampleRate = 0;
12913 pDeviceInfo->nativeDataFormats[0].flags = 0;
12914 pDeviceInfo->nativeDataFormatCount = 1;
12915
12916 (void)pContext;
12917 return MA_SUCCESS;
12918}
12919
12920
12921static ma_result ma_device_uninit__null(ma_device* pDevice)
12922{
12923 MA_ASSERT(pDevice != NULL);
12924
12925 /* Keep it clean and wait for the device thread to finish before returning. */
12926 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL);
12927
12928 /* Wait for the thread to finish before continuing. */
12929 ma_thread_wait(&pDevice->null_device.deviceThread);
12930
12931 /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */
12932 ma_semaphore_uninit(&pDevice->null_device.operationSemaphore);
12933 ma_event_uninit(&pDevice->null_device.operationCompletionEvent);
12934 ma_event_uninit(&pDevice->null_device.operationEvent);
12935
12936 return MA_SUCCESS;
12937}
12938
12939static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
12940{
12941 ma_result result;
12942
12943 MA_ASSERT(pDevice != NULL);
12944
12945 MA_ZERO_OBJECT(&pDevice->null_device);
12946
12947 if (pConfig->deviceType == ma_device_type_loopback) {
12948 return MA_DEVICE_TYPE_NOT_SUPPORTED;
12949 }
12950
12951 /* The null backend supports everything exactly as we specify it. */
12952 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
12953 pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT;
12954 pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS;
12955 pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE;
12956
12957 if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) {
12958 ma_get_standard_channel_map(ma_standard_channel_map_default, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
12959 }
12960
12961 pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
12962 }
12963
12964 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
12965 pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT;
12966 pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS;
12967 pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE;
12968
12969 if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) {
12970 ma_get_standard_channel_map(ma_standard_channel_map_default, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
12971 }
12972
12973 pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
12974 }
12975
12976 /*
12977 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
12978 first period is "written" to it, and then stopped in ma_device_stop__null().
12979 */
12980 result = ma_event_init(&pDevice->null_device.operationEvent);
12981 if (result != MA_SUCCESS) {
12982 return result;
12983 }
12984
12985 result = ma_event_init(&pDevice->null_device.operationCompletionEvent);
12986 if (result != MA_SUCCESS) {
12987 return result;
12988 }
12989
12990 result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */
12991 if (result != MA_SUCCESS) {
12992 return result;
12993 }
12994
12995 result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks);
12996 if (result != MA_SUCCESS) {
12997 return result;
12998 }
12999
13000 return MA_SUCCESS;
13001}
13002
13003static ma_result ma_device_start__null(ma_device* pDevice)
13004{
13005 MA_ASSERT(pDevice != NULL);
13006
13007 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL);
13008
13009 c89atomic_exchange_32(&pDevice->null_device.isStarted, MA_TRUE);
13010 return MA_SUCCESS;
13011}
13012
13013static ma_result ma_device_stop__null(ma_device* pDevice)
13014{
13015 MA_ASSERT(pDevice != NULL);
13016
13017 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL);
13018
13019 c89atomic_exchange_32(&pDevice->null_device.isStarted, MA_FALSE);
13020 return MA_SUCCESS;
13021}
13022
13023static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
13024{
13025 ma_result result = MA_SUCCESS;
13026 ma_uint32 totalPCMFramesProcessed;
13027 ma_bool32 wasStartedOnEntry;
13028
13029 if (pFramesWritten != NULL) {
13030 *pFramesWritten = 0;
13031 }
13032
13033 wasStartedOnEntry = c89atomic_load_32(&pDevice->null_device.isStarted);
13034
13035 /* Keep going until everything has been read. */
13036 totalPCMFramesProcessed = 0;
13037 while (totalPCMFramesProcessed < frameCount) {
13038 ma_uint64 targetFrame;
13039
13040 /* If there are any frames remaining in the current period, consume those first. */
13041 if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) {
13042 ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
13043 ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback;
13044 if (framesToProcess > framesRemaining) {
13045 framesToProcess = framesRemaining;
13046 }
13047
13048 /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */
13049 (void)pPCMFrames;
13050
13051 pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess;
13052 totalPCMFramesProcessed += framesToProcess;
13053 }
13054
13055 /* 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. */
13056 if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) {
13057 pDevice->null_device.currentPeriodFramesRemainingPlayback = 0;
13058
13059 if (!c89atomic_load_32(&pDevice->null_device.isStarted) && !wasStartedOnEntry) {
13060 result = ma_device_start__null(pDevice);
13061 if (result != MA_SUCCESS) {
13062 break;
13063 }
13064 }
13065 }
13066
13067 /* If we've consumed the whole buffer we can return now. */
13068 MA_ASSERT(totalPCMFramesProcessed <= frameCount);
13069 if (totalPCMFramesProcessed == frameCount) {
13070 break;
13071 }
13072
13073 /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
13074 targetFrame = pDevice->null_device.lastProcessedFramePlayback;
13075 for (;;) {
13076 ma_uint64 currentFrame;
13077
13078 /* Stop waiting if the device has been stopped. */
13079 if (!c89atomic_load_32(&pDevice->null_device.isStarted)) {
13080 break;
13081 }
13082
13083 currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
13084 if (currentFrame >= targetFrame) {
13085 break;
13086 }
13087
13088 /* Getting here means we haven't yet reached the target sample, so continue waiting. */
13089 ma_sleep(10);
13090 }
13091
13092 pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalPeriodSizeInFrames;
13093 pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames;
13094 }
13095
13096 if (pFramesWritten != NULL) {
13097 *pFramesWritten = totalPCMFramesProcessed;
13098 }
13099
13100 return result;
13101}
13102
13103static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
13104{
13105 ma_result result = MA_SUCCESS;
13106 ma_uint32 totalPCMFramesProcessed;
13107
13108 if (pFramesRead != NULL) {
13109 *pFramesRead = 0;
13110 }
13111
13112 /* Keep going until everything has been read. */
13113 totalPCMFramesProcessed = 0;
13114 while (totalPCMFramesProcessed < frameCount) {
13115 ma_uint64 targetFrame;
13116
13117 /* If there are any frames remaining in the current period, consume those first. */
13118 if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) {
13119 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
13120 ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
13121 ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture;
13122 if (framesToProcess > framesRemaining) {
13123 framesToProcess = framesRemaining;
13124 }
13125
13126 /* We need to ensure the output buffer is zeroed. */
13127 MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf);
13128
13129 pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess;
13130 totalPCMFramesProcessed += framesToProcess;
13131 }
13132
13133 /* 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. */
13134 if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) {
13135 pDevice->null_device.currentPeriodFramesRemainingCapture = 0;
13136 }
13137
13138 /* If we've consumed the whole buffer we can return now. */
13139 MA_ASSERT(totalPCMFramesProcessed <= frameCount);
13140 if (totalPCMFramesProcessed == frameCount) {
13141 break;
13142 }
13143
13144 /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
13145 targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames;
13146 for (;;) {
13147 ma_uint64 currentFrame;
13148
13149 /* Stop waiting if the device has been stopped. */
13150 if (!c89atomic_load_32(&pDevice->null_device.isStarted)) {
13151 break;
13152 }
13153
13154 currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
13155 if (currentFrame >= targetFrame) {
13156 break;
13157 }
13158
13159 /* Getting here means we haven't yet reached the target sample, so continue waiting. */
13160 ma_sleep(10);
13161 }
13162
13163 pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames;
13164 pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames;
13165 }
13166
13167 if (pFramesRead != NULL) {
13168 *pFramesRead = totalPCMFramesProcessed;
13169 }
13170
13171 return result;
13172}
13173
13174static ma_result ma_context_uninit__null(ma_context* pContext)
13175{
13176 MA_ASSERT(pContext != NULL);
13177 MA_ASSERT(pContext->backend == ma_backend_null);
13178
13179 (void)pContext;
13180 return MA_SUCCESS;
13181}
13182
13183static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
13184{
13185 MA_ASSERT(pContext != NULL);
13186
13187 (void)pConfig;
13188 (void)pContext;
13189
13190 pCallbacks->onContextInit = ma_context_init__null;
13191 pCallbacks->onContextUninit = ma_context_uninit__null;
13192 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null;
13193 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null;
13194 pCallbacks->onDeviceInit = ma_device_init__null;
13195 pCallbacks->onDeviceUninit = ma_device_uninit__null;
13196 pCallbacks->onDeviceStart = ma_device_start__null;
13197 pCallbacks->onDeviceStop = ma_device_stop__null;
13198 pCallbacks->onDeviceRead = ma_device_read__null;
13199 pCallbacks->onDeviceWrite = ma_device_write__null;
13200 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. */
13201
13202 /* The null backend always works. */
13203 return MA_SUCCESS;
13204}
13205#endif
13206
13207
13208
13209/*******************************************************************************
13210
13211WIN32 COMMON
13212
13213*******************************************************************************/
13214#if defined(MA_WIN32)
13215#if defined(MA_WIN32_DESKTOP)
13216 #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit)
13217 #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)()
13218 #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv)
13219 #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv)
13220 #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar)
13221#else
13222 #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit)
13223 #define ma_CoUninitialize(pContext) CoUninitialize()
13224 #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv)
13225 #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv)
13226 #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar)
13227#endif
13228
13229#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__)
13230typedef size_t DWORD_PTR;
13231#endif
13232
13233#if !defined(WAVE_FORMAT_44M08)
13234#define WAVE_FORMAT_44M08 0x00000100
13235#define WAVE_FORMAT_44S08 0x00000200
13236#define WAVE_FORMAT_44M16 0x00000400
13237#define WAVE_FORMAT_44S16 0x00000800
13238#define WAVE_FORMAT_48M08 0x00001000
13239#define WAVE_FORMAT_48S08 0x00002000
13240#define WAVE_FORMAT_48M16 0x00004000
13241#define WAVE_FORMAT_48S16 0x00008000
13242#define WAVE_FORMAT_96M08 0x00010000
13243#define WAVE_FORMAT_96S08 0x00020000
13244#define WAVE_FORMAT_96M16 0x00040000
13245#define WAVE_FORMAT_96S16 0x00080000
13246#endif
13247
13248#ifndef SPEAKER_FRONT_LEFT
13249#define SPEAKER_FRONT_LEFT 0x1
13250#define SPEAKER_FRONT_RIGHT 0x2
13251#define SPEAKER_FRONT_CENTER 0x4
13252#define SPEAKER_LOW_FREQUENCY 0x8
13253#define SPEAKER_BACK_LEFT 0x10
13254#define SPEAKER_BACK_RIGHT 0x20
13255#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
13256#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
13257#define SPEAKER_BACK_CENTER 0x100
13258#define SPEAKER_SIDE_LEFT 0x200
13259#define SPEAKER_SIDE_RIGHT 0x400
13260#define SPEAKER_TOP_CENTER 0x800
13261#define SPEAKER_TOP_FRONT_LEFT 0x1000
13262#define SPEAKER_TOP_FRONT_CENTER 0x2000
13263#define SPEAKER_TOP_FRONT_RIGHT 0x4000
13264#define SPEAKER_TOP_BACK_LEFT 0x8000
13265#define SPEAKER_TOP_BACK_CENTER 0x10000
13266#define SPEAKER_TOP_BACK_RIGHT 0x20000
13267#endif
13268
13269/*
13270The SDK that comes with old versions of MSVC (VC6, for example) does not appear to define WAVEFORMATEXTENSIBLE. We
13271define our own implementation in this case.
13272*/
13273#if (defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_)) || defined(__DMC__)
13274typedef struct
13275{
13276 WAVEFORMATEX Format;
13277 union
13278 {
13279 WORD wValidBitsPerSample;
13280 WORD wSamplesPerBlock;
13281 WORD wReserved;
13282 } Samples;
13283 DWORD dwChannelMask;
13284 GUID SubFormat;
13285} WAVEFORMATEXTENSIBLE;
13286#endif
13287
13288#ifndef WAVE_FORMAT_EXTENSIBLE
13289#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
13290#endif
13291
13292#ifndef WAVE_FORMAT_IEEE_FLOAT
13293#define WAVE_FORMAT_IEEE_FLOAT 0x0003
13294#endif
13295
13296/* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
13297static ma_uint8 ma_channel_id_to_ma__win32(DWORD id)
13298{
13299 switch (id)
13300 {
13301 case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
13302 case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
13303 case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
13304 case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
13305 case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
13306 case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
13307 case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
13308 case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
13309 case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
13310 case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
13311 case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
13312 case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
13313 case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
13314 case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
13315 case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
13316 case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
13317 case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
13318 case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
13319 default: return 0;
13320 }
13321}
13322
13323/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */
13324static DWORD ma_channel_id_to_win32(DWORD id)
13325{
13326 switch (id)
13327 {
13328 case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER;
13329 case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT;
13330 case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT;
13331 case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER;
13332 case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY;
13333 case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT;
13334 case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT;
13335 case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER;
13336 case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER;
13337 case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER;
13338 case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT;
13339 case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT;
13340 case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER;
13341 case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT;
13342 case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER;
13343 case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT;
13344 case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT;
13345 case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER;
13346 case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT;
13347 default: return 0;
13348 }
13349}
13350
13351/* Converts a channel mapping to a Win32-style channel mask. */
13352static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels)
13353{
13354 DWORD dwChannelMask = 0;
13355 ma_uint32 iChannel;
13356
13357 for (iChannel = 0; iChannel < channels; ++iChannel) {
13358 dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]);
13359 }
13360
13361 return dwChannelMask;
13362}
13363
13364/* Converts a Win32-style channel mask to a miniaudio channel map. */
13365static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap)
13366{
13367 if (channels == 1 && dwChannelMask == 0) {
13368 pChannelMap[0] = MA_CHANNEL_MONO;
13369 } else if (channels == 2 && dwChannelMask == 0) {
13370 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
13371 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
13372 } else {
13373 if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) {
13374 pChannelMap[0] = MA_CHANNEL_MONO;
13375 } else {
13376 /* Just iterate over each bit. */
13377 ma_uint32 iChannel = 0;
13378 ma_uint32 iBit;
13379
13380 for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {
13381 DWORD bitValue = (dwChannelMask & (1UL << iBit));
13382 if (bitValue != 0) {
13383 /* The bit is set. */
13384 pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue);
13385 iChannel += 1;
13386 }
13387 }
13388 }
13389 }
13390}
13391
13392#ifdef __cplusplus
13393static ma_bool32 ma_is_guid_equal(const void* a, const void* b)
13394{
13395 return IsEqualGUID(*(const GUID*)a, *(const GUID*)b);
13396}
13397#else
13398#define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b)
13399#endif
13400
13401static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid)
13402{
13403 static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
13404 return ma_is_guid_equal(guid, &nullguid);
13405}
13406
13407static ma_format ma_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF)
13408{
13409 MA_ASSERT(pWF != NULL);
13410
13411 if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
13412 const WAVEFORMATEXTENSIBLE* pWFEX = (const WAVEFORMATEXTENSIBLE*)pWF;
13413 if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) {
13414 if (pWFEX->Samples.wValidBitsPerSample == 32) {
13415 return ma_format_s32;
13416 }
13417 if (pWFEX->Samples.wValidBitsPerSample == 24) {
13418 if (pWFEX->Format.wBitsPerSample == 32) {
13419 /*return ma_format_s24_32;*/
13420 }
13421 if (pWFEX->Format.wBitsPerSample == 24) {
13422 return ma_format_s24;
13423 }
13424 }
13425 if (pWFEX->Samples.wValidBitsPerSample == 16) {
13426 return ma_format_s16;
13427 }
13428 if (pWFEX->Samples.wValidBitsPerSample == 8) {
13429 return ma_format_u8;
13430 }
13431 }
13432 if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
13433 if (pWFEX->Samples.wValidBitsPerSample == 32) {
13434 return ma_format_f32;
13435 }
13436 /*
13437 if (pWFEX->Samples.wValidBitsPerSample == 64) {
13438 return ma_format_f64;
13439 }
13440 */
13441 }
13442 } else {
13443 if (pWF->wFormatTag == WAVE_FORMAT_PCM) {
13444 if (pWF->wBitsPerSample == 32) {
13445 return ma_format_s32;
13446 }
13447 if (pWF->wBitsPerSample == 24) {
13448 return ma_format_s24;
13449 }
13450 if (pWF->wBitsPerSample == 16) {
13451 return ma_format_s16;
13452 }
13453 if (pWF->wBitsPerSample == 8) {
13454 return ma_format_u8;
13455 }
13456 }
13457 if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
13458 if (pWF->wBitsPerSample == 32) {
13459 return ma_format_f32;
13460 }
13461 if (pWF->wBitsPerSample == 64) {
13462 /*return ma_format_f64;*/
13463 }
13464 }
13465 }
13466
13467 return ma_format_unknown;
13468}
13469#endif
13470
13471
13472/*******************************************************************************
13473
13474WASAPI Backend
13475
13476*******************************************************************************/
13477#ifdef MA_HAS_WASAPI
13478#if 0
13479#if defined(_MSC_VER)
13480 #pragma warning(push)
13481 #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */
13482#endif
13483#include <audioclient.h>
13484#include <mmdeviceapi.h>
13485#if defined(_MSC_VER)
13486 #pragma warning(pop)
13487#endif
13488#endif /* 0 */
13489
13490static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType);
13491
13492/* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */
13493#define MA_WIN32_WINNT_VISTA 0x0600
13494#define MA_VER_MINORVERSION 0x01
13495#define MA_VER_MAJORVERSION 0x02
13496#define MA_VER_SERVICEPACKMAJOR 0x20
13497#define MA_VER_GREATER_EQUAL 0x03
13498
13499typedef struct {
13500 DWORD dwOSVersionInfoSize;
13501 DWORD dwMajorVersion;
13502 DWORD dwMinorVersion;
13503 DWORD dwBuildNumber;
13504 DWORD dwPlatformId;
13505 WCHAR szCSDVersion[128];
13506 WORD wServicePackMajor;
13507 WORD wServicePackMinor;
13508 WORD wSuiteMask;
13509 BYTE wProductType;
13510 BYTE wReserved;
13511} ma_OSVERSIONINFOEXW;
13512
13513typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
13514typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask);
13515
13516
13517#ifndef PROPERTYKEY_DEFINED
13518#define PROPERTYKEY_DEFINED
13519#ifndef __WATCOMC__
13520typedef struct
13521{
13522 GUID fmtid;
13523 DWORD pid;
13524} PROPERTYKEY;
13525#endif
13526#endif
13527
13528/* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */
13529static MA_INLINE void ma_PropVariantInit(PROPVARIANT* pProp)
13530{
13531 MA_ZERO_OBJECT(pProp);
13532}
13533
13534
13535static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14};
13536static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0};
13537
13538static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */
13539#ifndef MA_WIN32_DESKTOP
13540static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */
13541#endif
13542
13543static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */
13544static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */
13545static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */
13546static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */
13547static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */
13548static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */
13549#ifndef MA_WIN32_DESKTOP
13550static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */
13551static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */
13552static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */
13553#endif
13554
13555static const IID MA_CLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */
13556static const IID MA_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */
13557#ifdef __cplusplus
13558#define MA_CLSID_MMDeviceEnumerator MA_CLSID_MMDeviceEnumerator_Instance
13559#define MA_IID_IMMDeviceEnumerator MA_IID_IMMDeviceEnumerator_Instance
13560#else
13561#define MA_CLSID_MMDeviceEnumerator &MA_CLSID_MMDeviceEnumerator_Instance
13562#define MA_IID_IMMDeviceEnumerator &MA_IID_IMMDeviceEnumerator_Instance
13563#endif
13564
13565typedef struct ma_IUnknown ma_IUnknown;
13566#ifdef MA_WIN32_DESKTOP
13567#define MA_MM_DEVICE_STATE_ACTIVE 1
13568#define MA_MM_DEVICE_STATE_DISABLED 2
13569#define MA_MM_DEVICE_STATE_NOTPRESENT 4
13570#define MA_MM_DEVICE_STATE_UNPLUGGED 8
13571
13572typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator;
13573typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection;
13574typedef struct ma_IMMDevice ma_IMMDevice;
13575#else
13576typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler;
13577typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation;
13578#endif
13579typedef struct ma_IPropertyStore ma_IPropertyStore;
13580typedef struct ma_IAudioClient ma_IAudioClient;
13581typedef struct ma_IAudioClient2 ma_IAudioClient2;
13582typedef struct ma_IAudioClient3 ma_IAudioClient3;
13583typedef struct ma_IAudioRenderClient ma_IAudioRenderClient;
13584typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient;
13585
13586typedef ma_int64 MA_REFERENCE_TIME;
13587
13588#define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000
13589#define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000
13590#define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000
13591#define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000
13592#define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
13593#define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
13594#define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
13595#define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000
13596#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000
13597#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000
13598
13599/* Buffer flags. */
13600#define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1
13601#define MA_AUDCLNT_BUFFERFLAGS_SILENT 2
13602#define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4
13603
13604typedef enum
13605{
13606 ma_eRender = 0,
13607 ma_eCapture = 1,
13608 ma_eAll = 2
13609} ma_EDataFlow;
13610
13611typedef enum
13612{
13613 ma_eConsole = 0,
13614 ma_eMultimedia = 1,
13615 ma_eCommunications = 2
13616} ma_ERole;
13617
13618typedef enum
13619{
13620 MA_AUDCLNT_SHAREMODE_SHARED,
13621 MA_AUDCLNT_SHAREMODE_EXCLUSIVE
13622} MA_AUDCLNT_SHAREMODE;
13623
13624typedef enum
13625{
13626 MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */
13627} MA_AUDIO_STREAM_CATEGORY;
13628
13629typedef struct
13630{
13631 ma_uint32 cbSize;
13632 BOOL bIsOffload;
13633 MA_AUDIO_STREAM_CATEGORY eCategory;
13634} ma_AudioClientProperties;
13635
13636/* IUnknown */
13637typedef struct
13638{
13639 /* IUnknown */
13640 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject);
13641 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis);
13642 ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis);
13643} ma_IUnknownVtbl;
13644struct ma_IUnknown
13645{
13646 ma_IUnknownVtbl* lpVtbl;
13647};
13648static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13649static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13650static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); }
13651
13652#ifdef MA_WIN32_DESKTOP
13653 /* IMMNotificationClient */
13654 typedef struct
13655 {
13656 /* IUnknown */
13657 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject);
13658 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis);
13659 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis);
13660
13661 /* IMMNotificationClient */
13662 HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState);
13663 HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID);
13664 HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID);
13665 HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID);
13666 HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key);
13667 } ma_IMMNotificationClientVtbl;
13668
13669 /* IMMDeviceEnumerator */
13670 typedef struct
13671 {
13672 /* IUnknown */
13673 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject);
13674 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis);
13675 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis);
13676
13677 /* IMMDeviceEnumerator */
13678 HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices);
13679 HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint);
13680 HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice);
13681 HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
13682 HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
13683 } ma_IMMDeviceEnumeratorVtbl;
13684 struct ma_IMMDeviceEnumerator
13685 {
13686 ma_IMMDeviceEnumeratorVtbl* lpVtbl;
13687 };
13688 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13689 static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13690 static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); }
13691 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); }
13692 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); }
13693 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); }
13694 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); }
13695 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); }
13696
13697
13698 /* IMMDeviceCollection */
13699 typedef struct
13700 {
13701 /* IUnknown */
13702 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject);
13703 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis);
13704 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis);
13705
13706 /* IMMDeviceCollection */
13707 HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices);
13708 HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice);
13709 } ma_IMMDeviceCollectionVtbl;
13710 struct ma_IMMDeviceCollection
13711 {
13712 ma_IMMDeviceCollectionVtbl* lpVtbl;
13713 };
13714 static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13715 static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13716 static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); }
13717 static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); }
13718 static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); }
13719
13720
13721 /* IMMDevice */
13722 typedef struct
13723 {
13724 /* IUnknown */
13725 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject);
13726 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis);
13727 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis);
13728
13729 /* IMMDevice */
13730 HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface);
13731 HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties);
13732 HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, LPWSTR *pID);
13733 HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState);
13734 } ma_IMMDeviceVtbl;
13735 struct ma_IMMDevice
13736 {
13737 ma_IMMDeviceVtbl* lpVtbl;
13738 };
13739 static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13740 static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13741 static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); }
13742 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); }
13743 static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); }
13744 static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, LPWSTR *pID) { return pThis->lpVtbl->GetId(pThis, pID); }
13745 static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); }
13746#else
13747 /* IActivateAudioInterfaceAsyncOperation */
13748 typedef struct
13749 {
13750 /* IUnknown */
13751 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject);
13752 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
13753 ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
13754
13755 /* IActivateAudioInterfaceAsyncOperation */
13756 HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface);
13757 } ma_IActivateAudioInterfaceAsyncOperationVtbl;
13758 struct ma_IActivateAudioInterfaceAsyncOperation
13759 {
13760 ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl;
13761 };
13762 static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13763 static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13764 static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); }
13765 static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); }
13766#endif
13767
13768/* IPropertyStore */
13769typedef struct
13770{
13771 /* IUnknown */
13772 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject);
13773 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis);
13774 ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis);
13775
13776 /* IPropertyStore */
13777 HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount);
13778 HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey);
13779 HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar);
13780 HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar);
13781 HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis);
13782} ma_IPropertyStoreVtbl;
13783struct ma_IPropertyStore
13784{
13785 ma_IPropertyStoreVtbl* lpVtbl;
13786};
13787static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13788static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13789static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); }
13790static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); }
13791static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); }
13792static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); }
13793static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); }
13794static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); }
13795
13796
13797/* IAudioClient */
13798typedef struct
13799{
13800 /* IUnknown */
13801 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject);
13802 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis);
13803 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis);
13804
13805 /* IAudioClient */
13806 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);
13807 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames);
13808 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency);
13809 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames);
13810 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
13811 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat);
13812 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
13813 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis);
13814 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis);
13815 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis);
13816 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle);
13817 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp);
13818} ma_IAudioClientVtbl;
13819struct ma_IAudioClient
13820{
13821 ma_IAudioClientVtbl* lpVtbl;
13822};
13823static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13824static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13825static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); }
13826static 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); }
13827static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
13828static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
13829static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
13830static 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); }
13831static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
13832static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
13833static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); }
13834static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); }
13835static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); }
13836static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
13837static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
13838
13839/* IAudioClient2 */
13840typedef struct
13841{
13842 /* IUnknown */
13843 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject);
13844 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis);
13845 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis);
13846
13847 /* IAudioClient */
13848 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);
13849 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames);
13850 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency);
13851 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames);
13852 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
13853 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat);
13854 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
13855 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis);
13856 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis);
13857 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis);
13858 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle);
13859 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp);
13860
13861 /* IAudioClient2 */
13862 HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
13863 HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties);
13864 HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
13865} ma_IAudioClient2Vtbl;
13866struct ma_IAudioClient2
13867{
13868 ma_IAudioClient2Vtbl* lpVtbl;
13869};
13870static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13871static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13872static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); }
13873static 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); }
13874static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
13875static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
13876static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
13877static 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); }
13878static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
13879static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
13880static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); }
13881static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); }
13882static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); }
13883static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
13884static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
13885static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
13886static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
13887static 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); }
13888
13889
13890/* IAudioClient3 */
13891typedef struct
13892{
13893 /* IUnknown */
13894 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject);
13895 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis);
13896 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis);
13897
13898 /* IAudioClient */
13899 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);
13900 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames);
13901 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency);
13902 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames);
13903 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
13904 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat);
13905 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
13906 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis);
13907 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis);
13908 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis);
13909 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle);
13910 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp);
13911
13912 /* IAudioClient2 */
13913 HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
13914 HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties);
13915 HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
13916
13917 /* IAudioClient3 */
13918 HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames);
13919 HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames);
13920 HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
13921} ma_IAudioClient3Vtbl;
13922struct ma_IAudioClient3
13923{
13924 ma_IAudioClient3Vtbl* lpVtbl;
13925};
13926static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13927static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13928static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); }
13929static 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); }
13930static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
13931static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
13932static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
13933static 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); }
13934static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
13935static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
13936static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); }
13937static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); }
13938static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); }
13939static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
13940static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
13941static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
13942static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
13943static 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); }
13944static 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); }
13945static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); }
13946static 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); }
13947
13948
13949/* IAudioRenderClient */
13950typedef struct
13951{
13952 /* IUnknown */
13953 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject);
13954 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis);
13955 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis);
13956
13957 /* IAudioRenderClient */
13958 HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData);
13959 HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags);
13960} ma_IAudioRenderClientVtbl;
13961struct ma_IAudioRenderClient
13962{
13963 ma_IAudioRenderClientVtbl* lpVtbl;
13964};
13965static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13966static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13967static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); }
13968static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); }
13969static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); }
13970
13971
13972/* IAudioCaptureClient */
13973typedef struct
13974{
13975 /* IUnknown */
13976 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject);
13977 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis);
13978 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis);
13979
13980 /* IAudioRenderClient */
13981 HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition);
13982 HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead);
13983 HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket);
13984} ma_IAudioCaptureClientVtbl;
13985struct ma_IAudioCaptureClient
13986{
13987 ma_IAudioCaptureClientVtbl* lpVtbl;
13988};
13989static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
13990static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
13991static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); }
13992static 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); }
13993static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); }
13994static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); }
13995
13996#ifndef MA_WIN32_DESKTOP
13997#include <mmdeviceapi.h>
13998typedef struct ma_completion_handler_uwp ma_completion_handler_uwp;
13999
14000typedef struct
14001{
14002 /* IUnknown */
14003 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject);
14004 ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis);
14005 ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis);
14006
14007 /* IActivateAudioInterfaceCompletionHandler */
14008 HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation);
14009} ma_completion_handler_uwp_vtbl;
14010struct ma_completion_handler_uwp
14011{
14012 ma_completion_handler_uwp_vtbl* lpVtbl;
14013 MA_ATOMIC ma_uint32 counter;
14014 HANDLE hEvent;
14015};
14016
14017static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject)
14018{
14019 /*
14020 We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To
14021 "implement" this, we just make sure we return pThis when the IAgileObject is requested.
14022 */
14023 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)) {
14024 *ppObject = NULL;
14025 return E_NOINTERFACE;
14026 }
14027
14028 /* Getting here means the IID is IUnknown or IMMNotificationClient. */
14029 *ppObject = (void*)pThis;
14030 ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis);
14031 return S_OK;
14032}
14033
14034static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis)
14035{
14036 return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1;
14037}
14038
14039static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis)
14040{
14041 ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1;
14042 if (newRefCount == 0) {
14043 return 0; /* We don't free anything here because we never allocate the object on the heap. */
14044 }
14045
14046 return (ULONG)newRefCount;
14047}
14048
14049static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation)
14050{
14051 (void)pActivateOperation;
14052 SetEvent(pThis->hEvent);
14053 return S_OK;
14054}
14055
14056
14057static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = {
14058 ma_completion_handler_uwp_QueryInterface,
14059 ma_completion_handler_uwp_AddRef,
14060 ma_completion_handler_uwp_Release,
14061 ma_completion_handler_uwp_ActivateCompleted
14062};
14063
14064static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler)
14065{
14066 MA_ASSERT(pHandler != NULL);
14067 MA_ZERO_OBJECT(pHandler);
14068
14069 pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance;
14070 pHandler->counter = 1;
14071 pHandler->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
14072 if (pHandler->hEvent == NULL) {
14073 return ma_result_from_GetLastError(GetLastError());
14074 }
14075
14076 return MA_SUCCESS;
14077}
14078
14079static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler)
14080{
14081 if (pHandler->hEvent != NULL) {
14082 CloseHandle(pHandler->hEvent);
14083 }
14084}
14085
14086static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler)
14087{
14088 WaitForSingleObject(pHandler->hEvent, INFINITE);
14089}
14090#endif /* !MA_WIN32_DESKTOP */
14091
14092/* We need a virtual table for our notification client object that's used for detecting changes to the default device. */
14093#ifdef MA_WIN32_DESKTOP
14094static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject)
14095{
14096 /*
14097 We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else
14098 we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK.
14099 */
14100 if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) {
14101 *ppObject = NULL;
14102 return E_NOINTERFACE;
14103 }
14104
14105 /* Getting here means the IID is IUnknown or IMMNotificationClient. */
14106 *ppObject = (void*)pThis;
14107 ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis);
14108 return S_OK;
14109}
14110
14111static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis)
14112{
14113 return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1;
14114}
14115
14116static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis)
14117{
14118 ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1;
14119 if (newRefCount == 0) {
14120 return 0; /* We don't free anything here because we never allocate the object on the heap. */
14121 }
14122
14123 return (ULONG)newRefCount;
14124}
14125
14126static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState)
14127{
14128 ma_bool32 isThisDevice = MA_FALSE;
14129 ma_bool32 isCapture = MA_FALSE;
14130 ma_bool32 isPlayback = MA_FALSE;
14131
14132#ifdef MA_DEBUG_OUTPUT
14133 /*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);*/
14134#endif
14135
14136 /*
14137 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
14138 that the device is disabled or has been unplugged.
14139 */
14140 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)) {
14141 isCapture = MA_TRUE;
14142 if (wcscmp(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) {
14143 isThisDevice = MA_TRUE;
14144 }
14145 }
14146
14147 if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) {
14148 isPlayback = MA_TRUE;
14149 if (wcscmp(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) {
14150 isThisDevice = MA_TRUE;
14151 }
14152 }
14153
14154
14155 /*
14156 If the device ID matches our device we need to mark our device as detached and stop it. When a
14157 device is added in OnDeviceAdded(), we'll restart it. We only mark it as detached if the device
14158 was started at the time of being removed.
14159 */
14160 if (isThisDevice) {
14161 if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) {
14162 /*
14163 Unplugged or otherwise unavailable. Mark as detached if we were in a playing state. We'll
14164 use this to determine whether or not we need to automatically start the device when it's
14165 plugged back in again.
14166 */
14167 if (ma_device_get_state(pThis->pDevice) == MA_STATE_STARTED) {
14168 if (isPlayback) {
14169 pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE;
14170 }
14171 if (isCapture) {
14172 pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE;
14173 }
14174
14175 ma_device_stop(pThis->pDevice);
14176 }
14177 }
14178
14179 if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) {
14180 /* The device was activated. If we were detached, we need to start it again. */
14181 ma_bool8 tryRestartingDevice = MA_FALSE;
14182
14183 if (isPlayback) {
14184 if (pThis->pDevice->wasapi.isDetachedPlayback) {
14185 pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
14186 ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback);
14187 tryRestartingDevice = MA_TRUE;
14188 }
14189 }
14190
14191 if (isCapture) {
14192 if (pThis->pDevice->wasapi.isDetachedCapture) {
14193 pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
14194 ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
14195 tryRestartingDevice = MA_TRUE;
14196 }
14197 }
14198
14199 if (tryRestartingDevice) {
14200 if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) {
14201 ma_device_start(pThis->pDevice);
14202 }
14203 }
14204 }
14205 }
14206
14207 return S_OK;
14208}
14209
14210static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
14211{
14212#ifdef MA_DEBUG_OUTPUT
14213 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
14214#endif
14215
14216 /* We don't need to worry about this event for our purposes. */
14217 (void)pThis;
14218 (void)pDeviceID;
14219 return S_OK;
14220}
14221
14222static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
14223{
14224#ifdef MA_DEBUG_OUTPUT
14225 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
14226#endif
14227
14228 /* We don't need to worry about this event for our purposes. */
14229 (void)pThis;
14230 (void)pDeviceID;
14231 return S_OK;
14232}
14233
14234static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID)
14235{
14236#ifdef MA_DEBUG_OUTPUT
14237 /*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)");*/
14238#endif
14239
14240 /* We only ever use the eConsole role in miniaudio. */
14241 if (role != ma_eConsole) {
14242 ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting: role != eConsole\n");
14243 return S_OK;
14244 }
14245
14246 /* We only care about devices with the same data flow and role as the current device. */
14247 if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) ||
14248 (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture)) {
14249 ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n");
14250 return S_OK;
14251 }
14252
14253 /* Don't do automatic stream routing if we're not allowed. */
14254 if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) ||
14255 (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) {
14256 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");
14257 return S_OK;
14258 }
14259
14260 /*
14261 Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to
14262 AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once
14263 it's fixed.
14264 */
14265 if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) ||
14266 (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) {
14267 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");
14268 return S_OK;
14269 }
14270
14271
14272
14273
14274 /*
14275 Second attempt at device rerouting. We're going to retrieve the device's state at the time of
14276 the route change. We're then going to stop the device, reinitialize the device, and then start
14277 it again if the state before stopping was MA_STATE_STARTED.
14278 */
14279 {
14280 ma_uint32 previousState = ma_device_get_state(pThis->pDevice);
14281 ma_bool8 restartDevice = MA_FALSE;
14282
14283 if (previousState == MA_STATE_STARTED) {
14284 ma_device_stop(pThis->pDevice);
14285 restartDevice = MA_TRUE;
14286 }
14287
14288 if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */
14289 if (dataFlow == ma_eRender) {
14290 ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback);
14291
14292 if (pThis->pDevice->wasapi.isDetachedPlayback) {
14293 pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
14294
14295 if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) {
14296 restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */
14297 } else {
14298 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. */
14299 }
14300 }
14301 } else {
14302 ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
14303
14304 if (pThis->pDevice->wasapi.isDetachedCapture) {
14305 pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
14306
14307 if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) {
14308 restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */
14309 } else {
14310 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. */
14311 }
14312 }
14313 }
14314
14315 if (restartDevice) {
14316 ma_device_start(pThis->pDevice);
14317 }
14318 }
14319 }
14320
14321 return S_OK;
14322}
14323
14324static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key)
14325{
14326#ifdef MA_DEBUG_OUTPUT
14327 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
14328#endif
14329
14330 (void)pThis;
14331 (void)pDeviceID;
14332 (void)key;
14333 return S_OK;
14334}
14335
14336static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = {
14337 ma_IMMNotificationClient_QueryInterface,
14338 ma_IMMNotificationClient_AddRef,
14339 ma_IMMNotificationClient_Release,
14340 ma_IMMNotificationClient_OnDeviceStateChanged,
14341 ma_IMMNotificationClient_OnDeviceAdded,
14342 ma_IMMNotificationClient_OnDeviceRemoved,
14343 ma_IMMNotificationClient_OnDefaultDeviceChanged,
14344 ma_IMMNotificationClient_OnPropertyValueChanged
14345};
14346#endif /* MA_WIN32_DESKTOP */
14347
14348#ifdef MA_WIN32_DESKTOP
14349typedef ma_IMMDevice ma_WASAPIDeviceInterface;
14350#else
14351typedef ma_IUnknown ma_WASAPIDeviceInterface;
14352#endif
14353
14354
14355#define MA_CONTEXT_COMMAND_QUIT__WASAPI 1
14356#define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI 2
14357#define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3
14358
14359static ma_context_command__wasapi ma_context_init_command__wasapi(int code)
14360{
14361 ma_context_command__wasapi cmd;
14362
14363 MA_ZERO_OBJECT(&cmd);
14364 cmd.code = code;
14365
14366 return cmd;
14367}
14368
14369static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd)
14370{
14371 /* For now we are doing everything synchronously, but I might relax this later if the need arises. */
14372 ma_result result;
14373 ma_bool32 isUsingLocalEvent = MA_FALSE;
14374 ma_event localEvent;
14375
14376 MA_ASSERT(pContext != NULL);
14377 MA_ASSERT(pCmd != NULL);
14378
14379 if (pCmd->pEvent == NULL) {
14380 isUsingLocalEvent = MA_TRUE;
14381
14382 result = ma_event_init(&localEvent);
14383 if (result != MA_SUCCESS) {
14384 return result; /* Failed to create the event for this command. */
14385 }
14386 }
14387
14388 /* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */
14389 ma_mutex_lock(&pContext->wasapi.commandLock);
14390 {
14391 ma_uint32 index;
14392
14393 /* Spin until we've got some space available. */
14394 while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) {
14395 ma_yield();
14396 }
14397
14398 /* Space is now available. Can safely add to the list. */
14399 index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands);
14400 pContext->wasapi.commands[index] = *pCmd;
14401 pContext->wasapi.commands[index].pEvent = &localEvent;
14402 pContext->wasapi.commandCount += 1;
14403
14404 /* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */
14405 ma_semaphore_release(&pContext->wasapi.commandSem);
14406 }
14407 ma_mutex_unlock(&pContext->wasapi.commandLock);
14408
14409 if (isUsingLocalEvent) {
14410 ma_event_wait(&localEvent);
14411 ma_event_uninit(&localEvent);
14412 }
14413
14414 return MA_SUCCESS;
14415}
14416
14417static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd)
14418{
14419 ma_result result = MA_SUCCESS;
14420
14421 MA_ASSERT(pContext != NULL);
14422 MA_ASSERT(pCmd != NULL);
14423
14424 result = ma_semaphore_wait(&pContext->wasapi.commandSem);
14425 if (result == MA_SUCCESS) {
14426 ma_mutex_lock(&pContext->wasapi.commandLock);
14427 {
14428 *pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex];
14429 pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands);
14430 pContext->wasapi.commandCount -= 1;
14431 }
14432 ma_mutex_unlock(&pContext->wasapi.commandLock);
14433 }
14434
14435 return result;
14436}
14437
14438static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData)
14439{
14440 ma_result result;
14441 ma_context* pContext = (ma_context*)pUserData;
14442 MA_ASSERT(pContext != NULL);
14443
14444 for (;;) {
14445 ma_context_command__wasapi cmd;
14446 result = ma_context_next_command__wasapi(pContext, &cmd);
14447 if (result != MA_SUCCESS) {
14448 break;
14449 }
14450
14451 switch (cmd.code)
14452 {
14453 case MA_CONTEXT_COMMAND_QUIT__WASAPI:
14454 {
14455 /* Do nothing. Handled after the switch. */
14456 } break;
14457
14458 case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI:
14459 {
14460 if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) {
14461 *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService));
14462 } else {
14463 *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService));
14464 }
14465 } break;
14466
14467 case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI:
14468 {
14469 if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) {
14470 if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) {
14471 ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback);
14472 cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL;
14473 }
14474 }
14475
14476 if (cmd.data.releaseAudioClient.deviceType == ma_device_type_capture) {
14477 if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) {
14478 ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture);
14479 cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL;
14480 }
14481 }
14482 } break;
14483
14484 default:
14485 {
14486 /* Unknown command. Ignore it, but trigger an assert in debug mode so we're aware of it. */
14487 MA_ASSERT(MA_FALSE);
14488 } break;
14489 }
14490
14491 if (cmd.pEvent != NULL) {
14492 ma_event_signal(cmd.pEvent);
14493 }
14494
14495 if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) {
14496 break; /* Received a quit message. Get out of here. */
14497 }
14498 }
14499
14500 return (ma_thread_result)0;
14501}
14502
14503static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService)
14504{
14505 ma_result result;
14506 ma_result cmdResult;
14507 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI);
14508 cmd.data.createAudioClient.deviceType = deviceType;
14509 cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient;
14510 cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService;
14511 cmd.data.createAudioClient.pResult = &cmdResult; /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */
14512
14513 result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */
14514 if (result != MA_SUCCESS) {
14515 return result;
14516 }
14517
14518 return *cmd.data.createAudioClient.pResult;
14519}
14520
14521#if 0 /* Not used at the moment, but leaving here for future use. */
14522static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType)
14523{
14524 ma_result result;
14525 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI);
14526 cmd.data.releaseAudioClient.pDevice = pDevice;
14527 cmd.data.releaseAudioClient.deviceType = deviceType;
14528
14529 result = ma_context_post_command__wasapi(pDevice->pContext, &cmd); /* This will not return until the command has actually been run. */
14530 if (result != MA_SUCCESS) {
14531 return result;
14532 }
14533
14534 return MA_SUCCESS;
14535}
14536#endif
14537
14538
14539static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo)
14540{
14541 MA_ASSERT(pWF != NULL);
14542 MA_ASSERT(pInfo != NULL);
14543
14544 if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) {
14545 return; /* Too many data formats. Need to ignore this one. Don't think this should ever happen with WASAPI. */
14546 }
14547
14548 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format = ma_format_from_WAVEFORMATEX(pWF);
14549 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels = pWF->nChannels;
14550 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec;
14551 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].flags = (shareMode == ma_share_mode_exclusive) ? MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE : 0;
14552 pInfo->nativeDataFormatCount += 1;
14553}
14554
14555static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo)
14556{
14557 HRESULT hr;
14558 WAVEFORMATEX* pWF = NULL;
14559
14560 MA_ASSERT(pAudioClient != NULL);
14561 MA_ASSERT(pInfo != NULL);
14562
14563 /* Shared Mode. We use GetMixFormat() here. */
14564 hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (WAVEFORMATEX**)&pWF);
14565 if (SUCCEEDED(hr)) {
14566 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo);
14567 } else {
14568 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval.", ma_result_from_HRESULT(hr));
14569 }
14570
14571 /*
14572 Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on
14573 UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on
14574 out, MA_SUCCESS is guaranteed to be returned.
14575 */
14576 #ifdef MA_WIN32_DESKTOP
14577 {
14578 ma_IPropertyStore *pProperties;
14579
14580 /*
14581 The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is
14582 correct which will simplify our searching.
14583 */
14584 hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties);
14585 if (SUCCEEDED(hr)) {
14586 PROPVARIANT var;
14587 ma_PropVariantInit(&var);
14588
14589 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var);
14590 if (SUCCEEDED(hr)) {
14591 pWF = (WAVEFORMATEX*)var.blob.pBlobData;
14592
14593 /*
14594 In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format
14595 first. If this fails, fall back to a search.
14596 */
14597 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL);
14598 if (SUCCEEDED(hr)) {
14599 /* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */
14600 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo);
14601 } else {
14602 /*
14603 The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel
14604 count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format.
14605 */
14606 ma_uint32 channels = pInfo->minChannels;
14607 ma_channel defaultChannelMap[MA_MAX_CHANNELS];
14608 WAVEFORMATEXTENSIBLE wf;
14609 ma_bool32 found;
14610 ma_uint32 iFormat;
14611
14612 /* Make sure we don't overflow the channel map. */
14613 if (channels > MA_MAX_CHANNELS) {
14614 channels = MA_MAX_CHANNELS;
14615 }
14616
14617 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, channels, defaultChannelMap);
14618
14619 MA_ZERO_OBJECT(&wf);
14620 wf.Format.cbSize = sizeof(wf);
14621 wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
14622 wf.Format.nChannels = (WORD)channels;
14623 wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels);
14624
14625 found = MA_FALSE;
14626 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) {
14627 ma_format format = g_maFormatPriorities[iFormat];
14628 ma_uint32 iSampleRate;
14629
14630 wf.Format.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8);
14631 wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8);
14632 wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
14633 wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.Format.wBitsPerSample;
14634 if (format == ma_format_f32) {
14635 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
14636 } else {
14637 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
14638 }
14639
14640 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) {
14641 wf.Format.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate];
14642
14643 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL);
14644 if (SUCCEEDED(hr)) {
14645 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo);
14646 found = MA_TRUE;
14647 break;
14648 }
14649 }
14650
14651 if (found) {
14652 break;
14653 }
14654 }
14655
14656 ma_PropVariantClear(pContext, &var);
14657
14658 if (!found) {
14659 ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to find suitable device format for device info retrieval.", MA_FORMAT_NOT_SUPPORTED);
14660 }
14661 }
14662 } else {
14663 ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to retrieve device format for device info retrieval.", ma_result_from_HRESULT(hr));
14664 }
14665
14666 ma_IPropertyStore_Release(pProperties);
14667 } else {
14668 ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval.", ma_result_from_HRESULT(hr));
14669 }
14670 }
14671 #endif
14672
14673 return MA_SUCCESS;
14674}
14675
14676#ifdef MA_WIN32_DESKTOP
14677static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType)
14678{
14679 if (deviceType == ma_device_type_playback) {
14680 return ma_eRender;
14681 } else if (deviceType == ma_device_type_capture) {
14682 return ma_eCapture;
14683 } else {
14684 MA_ASSERT(MA_FALSE);
14685 return ma_eRender; /* Should never hit this. */
14686 }
14687}
14688
14689static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator)
14690{
14691 HRESULT hr;
14692 ma_IMMDeviceEnumerator* pDeviceEnumerator;
14693
14694 MA_ASSERT(pContext != NULL);
14695 MA_ASSERT(ppDeviceEnumerator != NULL);
14696
14697 *ppDeviceEnumerator = NULL; /* Safety. */
14698
14699 hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
14700 if (FAILED(hr)) {
14701 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr));
14702 }
14703
14704 *ppDeviceEnumerator = pDeviceEnumerator;
14705
14706 return MA_SUCCESS;
14707}
14708
14709static LPWSTR ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType)
14710{
14711 HRESULT hr;
14712 ma_IMMDevice* pMMDefaultDevice = NULL;
14713 LPWSTR pDefaultDeviceID = NULL;
14714 ma_EDataFlow dataFlow;
14715 ma_ERole role;
14716
14717 MA_ASSERT(pContext != NULL);
14718 MA_ASSERT(pDeviceEnumerator != NULL);
14719
14720 (void)pContext;
14721
14722 /* Grab the EDataFlow type from the device type. */
14723 dataFlow = ma_device_type_to_EDataFlow(deviceType);
14724
14725 /* The role is always eConsole, but we may make this configurable later. */
14726 role = ma_eConsole;
14727
14728 hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice);
14729 if (FAILED(hr)) {
14730 return NULL;
14731 }
14732
14733 hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID);
14734
14735 ma_IMMDevice_Release(pMMDefaultDevice);
14736 pMMDefaultDevice = NULL;
14737
14738 if (FAILED(hr)) {
14739 return NULL;
14740 }
14741
14742 return pDefaultDeviceID;
14743}
14744
14745static LPWSTR ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */
14746{
14747 ma_result result;
14748 ma_IMMDeviceEnumerator* pDeviceEnumerator;
14749 LPWSTR pDefaultDeviceID = NULL;
14750
14751 MA_ASSERT(pContext != NULL);
14752
14753 result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator);
14754 if (result != MA_SUCCESS) {
14755 return NULL;
14756 }
14757
14758 pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
14759
14760 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
14761 return pDefaultDeviceID;
14762}
14763
14764static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice)
14765{
14766 ma_IMMDeviceEnumerator* pDeviceEnumerator;
14767 HRESULT hr;
14768
14769 MA_ASSERT(pContext != NULL);
14770 MA_ASSERT(ppMMDevice != NULL);
14771
14772 hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
14773 if (FAILED(hr)) {
14774 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.", ma_result_from_HRESULT(hr));
14775 }
14776
14777 if (pDeviceID == NULL) {
14778 hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice);
14779 } else {
14780 hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice);
14781 }
14782
14783 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
14784 if (FAILED(hr)) {
14785 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.", ma_result_from_HRESULT(hr));
14786 }
14787
14788 return MA_SUCCESS;
14789}
14790
14791static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID)
14792{
14793 LPWSTR pDeviceIDString;
14794 HRESULT hr;
14795
14796 MA_ASSERT(pDeviceID != NULL);
14797
14798 hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString);
14799 if (SUCCEEDED(hr)) {
14800 size_t idlen = wcslen(pDeviceIDString);
14801 if (idlen+1 > ma_countof(pDeviceID->wasapi)) {
14802 ma_CoTaskMemFree(pContext, pDeviceIDString);
14803 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. */
14804 return MA_ERROR;
14805 }
14806
14807 MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t));
14808 pDeviceID->wasapi[idlen] = '\0';
14809
14810 ma_CoTaskMemFree(pContext, pDeviceIDString);
14811
14812 return MA_SUCCESS;
14813 }
14814
14815 return MA_ERROR;
14816}
14817
14818static 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)
14819{
14820 ma_result result;
14821 HRESULT hr;
14822
14823 MA_ASSERT(pContext != NULL);
14824 MA_ASSERT(pMMDevice != NULL);
14825 MA_ASSERT(pInfo != NULL);
14826
14827 /* ID. */
14828 result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id);
14829 if (result == MA_SUCCESS) {
14830 if (pDefaultDeviceID != NULL) {
14831 if (wcscmp(pInfo->id.wasapi, pDefaultDeviceID) == 0) {
14832 pInfo->isDefault = MA_TRUE;
14833 }
14834 }
14835 }
14836
14837 /* Description / Friendly Name */
14838 {
14839 ma_IPropertyStore *pProperties;
14840 hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties);
14841 if (SUCCEEDED(hr)) {
14842 PROPVARIANT var;
14843
14844 ma_PropVariantInit(&var);
14845 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var);
14846 if (SUCCEEDED(hr)) {
14847 WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);
14848 ma_PropVariantClear(pContext, &var);
14849 }
14850
14851 ma_IPropertyStore_Release(pProperties);
14852 }
14853 }
14854
14855 /* Format */
14856 if (!onlySimpleInfo) {
14857 ma_IAudioClient* pAudioClient;
14858 hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
14859 if (SUCCEEDED(hr)) {
14860 result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo);
14861
14862 ma_IAudioClient_Release(pAudioClient);
14863 return result;
14864 } else {
14865 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval.", ma_result_from_HRESULT(hr));
14866 }
14867 }
14868
14869 return MA_SUCCESS;
14870}
14871
14872static 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)
14873{
14874 ma_result result = MA_SUCCESS;
14875 UINT deviceCount;
14876 HRESULT hr;
14877 ma_uint32 iDevice;
14878 LPWSTR pDefaultDeviceID = NULL;
14879 ma_IMMDeviceCollection* pDeviceCollection = NULL;
14880
14881 MA_ASSERT(pContext != NULL);
14882 MA_ASSERT(callback != NULL);
14883
14884 /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */
14885 pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
14886
14887 /* We need to enumerate the devices which returns a device collection. */
14888 hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection);
14889 if (SUCCEEDED(hr)) {
14890 hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount);
14891 if (FAILED(hr)) {
14892 result = ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.", ma_result_from_HRESULT(hr));
14893 goto done;
14894 }
14895
14896 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
14897 ma_device_info deviceInfo;
14898 ma_IMMDevice* pMMDevice;
14899
14900 MA_ZERO_OBJECT(&deviceInfo);
14901
14902 hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice);
14903 if (SUCCEEDED(hr)) {
14904 result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */
14905
14906 ma_IMMDevice_Release(pMMDevice);
14907 if (result == MA_SUCCESS) {
14908 ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
14909 if (cbResult == MA_FALSE) {
14910 break;
14911 }
14912 }
14913 }
14914 }
14915 }
14916
14917done:
14918 if (pDefaultDeviceID != NULL) {
14919 ma_CoTaskMemFree(pContext, pDefaultDeviceID);
14920 pDefaultDeviceID = NULL;
14921 }
14922
14923 if (pDeviceCollection != NULL) {
14924 ma_IMMDeviceCollection_Release(pDeviceCollection);
14925 pDeviceCollection = NULL;
14926 }
14927
14928 return result;
14929}
14930
14931static 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)
14932{
14933 ma_result result;
14934 HRESULT hr;
14935
14936 MA_ASSERT(pContext != NULL);
14937 MA_ASSERT(ppAudioClient != NULL);
14938 MA_ASSERT(ppMMDevice != NULL);
14939
14940 result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice);
14941 if (result != MA_SUCCESS) {
14942 return result;
14943 }
14944
14945 hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)ppAudioClient);
14946 if (FAILED(hr)) {
14947 return ma_result_from_HRESULT(hr);
14948 }
14949
14950 return MA_SUCCESS;
14951}
14952#else
14953static 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)
14954{
14955 ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
14956 ma_completion_handler_uwp completionHandler;
14957 IID iid;
14958 LPOLESTR iidStr;
14959 HRESULT hr;
14960 ma_result result;
14961 HRESULT activateResult;
14962 ma_IUnknown* pActivatedInterface;
14963
14964 MA_ASSERT(pContext != NULL);
14965 MA_ASSERT(ppAudioClient != NULL);
14966
14967 if (pDeviceID != NULL) {
14968 MA_COPY_MEMORY(&iid, pDeviceID->wasapi, sizeof(iid));
14969 } else {
14970 if (deviceType == ma_device_type_playback) {
14971 iid = MA_IID_DEVINTERFACE_AUDIO_RENDER;
14972 } else {
14973 iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE;
14974 }
14975 }
14976
14977#if defined(__cplusplus)
14978 hr = StringFromIID(iid, &iidStr);
14979#else
14980 hr = StringFromIID(&iid, &iidStr);
14981#endif
14982 if (FAILED(hr)) {
14983 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.", ma_result_from_HRESULT(hr));
14984 }
14985
14986 result = ma_completion_handler_uwp_init(&completionHandler);
14987 if (result != MA_SUCCESS) {
14988 ma_CoTaskMemFree(pContext, iidStr);
14989 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", result);
14990 }
14991
14992#if defined(__cplusplus)
14993 hr = ActivateAudioInterfaceAsync(iidStr, MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
14994#else
14995 hr = ActivateAudioInterfaceAsync(iidStr, &MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
14996#endif
14997 if (FAILED(hr)) {
14998 ma_completion_handler_uwp_uninit(&completionHandler);
14999 ma_CoTaskMemFree(pContext, iidStr);
15000 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.", ma_result_from_HRESULT(hr));
15001 }
15002
15003 ma_CoTaskMemFree(pContext, iidStr);
15004
15005 /* Wait for the async operation for finish. */
15006 ma_completion_handler_uwp_wait(&completionHandler);
15007 ma_completion_handler_uwp_uninit(&completionHandler);
15008
15009 hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);
15010 ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
15011
15012 if (FAILED(hr) || FAILED(activateResult)) {
15013 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.", FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult));
15014 }
15015
15016 /* Here is where we grab the IAudioClient interface. */
15017 hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient);
15018 if (FAILED(hr)) {
15019 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.", ma_result_from_HRESULT(hr));
15020 }
15021
15022 if (ppActivatedInterface) {
15023 *ppActivatedInterface = pActivatedInterface;
15024 } else {
15025 ma_IUnknown_Release(pActivatedInterface);
15026 }
15027
15028 return MA_SUCCESS;
15029}
15030#endif
15031
15032static 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)
15033{
15034#ifdef MA_WIN32_DESKTOP
15035 return ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface);
15036#else
15037 return ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface);
15038#endif
15039}
15040
15041
15042static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
15043{
15044 /* Different enumeration for desktop and UWP. */
15045#ifdef MA_WIN32_DESKTOP
15046 /* Desktop */
15047 HRESULT hr;
15048 ma_IMMDeviceEnumerator* pDeviceEnumerator;
15049
15050 hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
15051 if (FAILED(hr)) {
15052 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr));
15053 }
15054
15055 ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData);
15056 ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData);
15057
15058 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
15059#else
15060 /*
15061 UWP
15062
15063 The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate
15064 over devices without using MMDevice, I'm restricting devices to defaults.
15065
15066 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/
15067 */
15068 if (callback) {
15069 ma_bool32 cbResult = MA_TRUE;
15070
15071 /* Playback. */
15072 if (cbResult) {
15073 ma_device_info deviceInfo;
15074 MA_ZERO_OBJECT(&deviceInfo);
15075 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
15076 deviceInfo.isDefault = MA_TRUE;
15077 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
15078 }
15079
15080 /* Capture. */
15081 if (cbResult) {
15082 ma_device_info deviceInfo;
15083 MA_ZERO_OBJECT(&deviceInfo);
15084 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
15085 deviceInfo.isDefault = MA_TRUE;
15086 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
15087 }
15088 }
15089#endif
15090
15091 return MA_SUCCESS;
15092}
15093
15094static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
15095{
15096#ifdef MA_WIN32_DESKTOP
15097 ma_result result;
15098 ma_IMMDevice* pMMDevice = NULL;
15099 LPWSTR pDefaultDeviceID = NULL;
15100
15101 result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice);
15102 if (result != MA_SUCCESS) {
15103 return result;
15104 }
15105
15106 /* We need the default device ID so we can set the isDefault flag in the device info. */
15107 pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType);
15108
15109 result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */
15110
15111 if (pDefaultDeviceID != NULL) {
15112 ma_CoTaskMemFree(pContext, pDefaultDeviceID);
15113 pDefaultDeviceID = NULL;
15114 }
15115
15116 ma_IMMDevice_Release(pMMDevice);
15117
15118 return result;
15119#else
15120 ma_IAudioClient* pAudioClient;
15121 ma_result result;
15122
15123 /* UWP currently only uses default devices. */
15124 if (deviceType == ma_device_type_playback) {
15125 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
15126 } else {
15127 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
15128 }
15129
15130 result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, &pAudioClient, NULL);
15131 if (result != MA_SUCCESS) {
15132 return result;
15133 }
15134
15135 result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo);
15136
15137 pDeviceInfo->isDefault = MA_TRUE; /* UWP only supports default devices. */
15138
15139 ma_IAudioClient_Release(pAudioClient);
15140 return result;
15141#endif
15142}
15143
15144static ma_result ma_device_uninit__wasapi(ma_device* pDevice)
15145{
15146 MA_ASSERT(pDevice != NULL);
15147
15148#ifdef MA_WIN32_DESKTOP
15149 if (pDevice->wasapi.pDeviceEnumerator) {
15150 ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient);
15151 ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator);
15152 }
15153#endif
15154
15155 if (pDevice->wasapi.pRenderClient) {
15156 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
15157 }
15158 if (pDevice->wasapi.pCaptureClient) {
15159 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
15160 }
15161
15162 if (pDevice->wasapi.pAudioClientPlayback) {
15163 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
15164 }
15165 if (pDevice->wasapi.pAudioClientCapture) {
15166 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
15167 }
15168
15169 if (pDevice->wasapi.hEventPlayback) {
15170 CloseHandle(pDevice->wasapi.hEventPlayback);
15171 }
15172 if (pDevice->wasapi.hEventCapture) {
15173 CloseHandle(pDevice->wasapi.hEventCapture);
15174 }
15175
15176 return MA_SUCCESS;
15177}
15178
15179
15180typedef struct
15181{
15182 /* Input. */
15183 ma_format formatIn;
15184 ma_uint32 channelsIn;
15185 ma_uint32 sampleRateIn;
15186 ma_channel channelMapIn[MA_MAX_CHANNELS];
15187 ma_uint32 periodSizeInFramesIn;
15188 ma_uint32 periodSizeInMillisecondsIn;
15189 ma_uint32 periodsIn;
15190 ma_share_mode shareMode;
15191 ma_performance_profile performanceProfile;
15192 ma_bool32 noAutoConvertSRC;
15193 ma_bool32 noDefaultQualitySRC;
15194 ma_bool32 noHardwareOffloading;
15195
15196 /* Output. */
15197 ma_IAudioClient* pAudioClient;
15198 ma_IAudioRenderClient* pRenderClient;
15199 ma_IAudioCaptureClient* pCaptureClient;
15200 ma_format formatOut;
15201 ma_uint32 channelsOut;
15202 ma_uint32 sampleRateOut;
15203 ma_channel channelMapOut[MA_MAX_CHANNELS];
15204 ma_uint32 periodSizeInFramesOut;
15205 ma_uint32 periodsOut;
15206 ma_bool32 usingAudioClient3;
15207 char deviceName[256];
15208 ma_device_id id;
15209} ma_device_init_internal_data__wasapi;
15210
15211static 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)
15212{
15213 HRESULT hr;
15214 ma_result result = MA_SUCCESS;
15215 const char* errorMsg = "";
15216 MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
15217 DWORD streamFlags = 0;
15218 MA_REFERENCE_TIME periodDurationInMicroseconds;
15219 ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE;
15220 WAVEFORMATEXTENSIBLE wf;
15221 ma_WASAPIDeviceInterface* pDeviceInterface = NULL;
15222 ma_IAudioClient2* pAudioClient2;
15223 ma_uint32 nativeSampleRate;
15224
15225 MA_ASSERT(pContext != NULL);
15226 MA_ASSERT(pData != NULL);
15227
15228 /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */
15229 if (deviceType == ma_device_type_duplex) {
15230 return MA_INVALID_ARGS;
15231 }
15232
15233 pData->pAudioClient = NULL;
15234 pData->pRenderClient = NULL;
15235 pData->pCaptureClient = NULL;
15236
15237 streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
15238 if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */
15239 streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
15240 }
15241 if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) {
15242 streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
15243 }
15244 if (deviceType == ma_device_type_loopback) {
15245 streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK;
15246 }
15247
15248 result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, &pData->pAudioClient, &pDeviceInterface);
15249 if (result != MA_SUCCESS) {
15250 goto done;
15251 }
15252
15253 MA_ZERO_OBJECT(&wf);
15254
15255 /* Try enabling hardware offloading. */
15256 if (!pData->noHardwareOffloading) {
15257 hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2);
15258 if (SUCCEEDED(hr)) {
15259 BOOL isHardwareOffloadingSupported = 0;
15260 hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported);
15261 if (SUCCEEDED(hr) && isHardwareOffloadingSupported) {
15262 ma_AudioClientProperties clientProperties;
15263 MA_ZERO_OBJECT(&clientProperties);
15264 clientProperties.cbSize = sizeof(clientProperties);
15265 clientProperties.bIsOffload = 1;
15266 clientProperties.eCategory = MA_AudioCategory_Other;
15267 ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties);
15268 }
15269
15270 pAudioClient2->lpVtbl->Release(pAudioClient2);
15271 }
15272 }
15273
15274 /* 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. */
15275 result = MA_FORMAT_NOT_SUPPORTED;
15276 if (pData->shareMode == ma_share_mode_exclusive) {
15277 #ifdef MA_WIN32_DESKTOP
15278 /* In exclusive mode on desktop we always use the backend's native format. */
15279 ma_IPropertyStore* pStore = NULL;
15280 hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore);
15281 if (SUCCEEDED(hr)) {
15282 PROPVARIANT prop;
15283 ma_PropVariantInit(&prop);
15284 hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop);
15285 if (SUCCEEDED(hr)) {
15286 WAVEFORMATEX* pActualFormat = (WAVEFORMATEX*)prop.blob.pBlobData;
15287 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL);
15288 if (SUCCEEDED(hr)) {
15289 MA_COPY_MEMORY(&wf, pActualFormat, sizeof(WAVEFORMATEXTENSIBLE));
15290 }
15291
15292 ma_PropVariantClear(pContext, &prop);
15293 }
15294
15295 ma_IPropertyStore_Release(pStore);
15296 }
15297 #else
15298 /*
15299 I do not know how to query the device's native format on UWP so for now I'm just disabling support for
15300 exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported()
15301 until you find one that works.
15302
15303 TODO: Add support for exclusive mode to UWP.
15304 */
15305 hr = S_FALSE;
15306 #endif
15307
15308 if (hr == S_OK) {
15309 shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE;
15310 result = MA_SUCCESS;
15311 } else {
15312 result = MA_SHARE_MODE_NOT_SUPPORTED;
15313 }
15314 } else {
15315 /* In shared mode we are always using the format reported by the operating system. */
15316 WAVEFORMATEXTENSIBLE* pNativeFormat = NULL;
15317 hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (WAVEFORMATEX**)&pNativeFormat);
15318 if (hr != S_OK) {
15319 result = MA_FORMAT_NOT_SUPPORTED;
15320 } else {
15321 MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(wf));
15322 result = MA_SUCCESS;
15323 }
15324
15325 ma_CoTaskMemFree(pContext, pNativeFormat);
15326
15327 shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
15328 }
15329
15330 /* Return an error if we still haven't found a format. */
15331 if (result != MA_SUCCESS) {
15332 errorMsg = "[WASAPI] Failed to find best device mix format.";
15333 goto done;
15334 }
15335
15336 /*
15337 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
15338 WASAPI to perform the sample rate conversion.
15339 */
15340 nativeSampleRate = wf.Format.nSamplesPerSec;
15341 if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) {
15342 wf.Format.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE;
15343 wf.Format.nAvgBytesPerSec = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign;
15344 }
15345
15346 pData->formatOut = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)&wf);
15347 if (pData->formatOut == ma_format_unknown) {
15348 /*
15349 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
15350 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
15351 completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED.
15352 */
15353 if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
15354 result = MA_SHARE_MODE_NOT_SUPPORTED;
15355 } else {
15356 result = MA_FORMAT_NOT_SUPPORTED;
15357 }
15358
15359 errorMsg = "[WASAPI] Native format not supported.";
15360 goto done;
15361 }
15362
15363 pData->channelsOut = wf.Format.nChannels;
15364 pData->sampleRateOut = wf.Format.nSamplesPerSec;
15365
15366 /* Get the internal channel map based on the channel mask. */
15367 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut);
15368
15369 /* Period size. */
15370 pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS;
15371 pData->periodSizeInFramesOut = pData->periodSizeInFramesIn;
15372 if (pData->periodSizeInFramesOut == 0) {
15373 if (pData->periodSizeInMillisecondsIn == 0) {
15374 if (pData->performanceProfile == ma_performance_profile_low_latency) {
15375 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.Format.nSamplesPerSec);
15376 } else {
15377 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.Format.nSamplesPerSec);
15378 }
15379 } else {
15380 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.Format.nSamplesPerSec);
15381 }
15382 }
15383
15384 periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.Format.nSamplesPerSec;
15385
15386
15387 /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */
15388 if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
15389 MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * 10;
15390
15391 /*
15392 If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing
15393 it and trying it again.
15394 */
15395 hr = E_FAIL;
15396 for (;;) {
15397 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
15398 if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) {
15399 if (bufferDuration > 500*10000) {
15400 break;
15401 } else {
15402 if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */
15403 break;
15404 }
15405
15406 bufferDuration = bufferDuration * 2;
15407 continue;
15408 }
15409 } else {
15410 break;
15411 }
15412 }
15413
15414 if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
15415 ma_uint32 bufferSizeInFrames;
15416 hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
15417 if (SUCCEEDED(hr)) {
15418 bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.Format.nSamplesPerSec * bufferSizeInFrames) + 0.5);
15419
15420 /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */
15421 ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
15422
15423 #ifdef MA_WIN32_DESKTOP
15424 hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient);
15425 #else
15426 hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient);
15427 #endif
15428
15429 if (SUCCEEDED(hr)) {
15430 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
15431 }
15432 }
15433 }
15434
15435 if (FAILED(hr)) {
15436 /* 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. */
15437 if (hr == E_ACCESSDENIED) {
15438 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED;
15439 } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
15440 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY;
15441 } else {
15442 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr);
15443 }
15444 goto done;
15445 }
15446 }
15447
15448 if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) {
15449 /*
15450 Low latency shared mode via IAudioClient3.
15451
15452 NOTE
15453 ====
15454 Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the
15455 use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using
15456 any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to
15457 that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM.
15458 */
15459 #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE
15460 {
15461 if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.Format.nSamplesPerSec) {
15462 ma_IAudioClient3* pAudioClient3 = NULL;
15463 hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3);
15464 if (SUCCEEDED(hr)) {
15465 ma_uint32 defaultPeriodInFrames;
15466 ma_uint32 fundamentalPeriodInFrames;
15467 ma_uint32 minPeriodInFrames;
15468 ma_uint32 maxPeriodInFrames;
15469 hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames);
15470 if (SUCCEEDED(hr)) {
15471 ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut;
15472 ma_uint32 actualPeriodInFrames = desiredPeriodInFrames;
15473
15474 /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */
15475 actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames;
15476 actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames;
15477
15478 /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */
15479 actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames);
15480
15481 #if defined(MA_DEBUG_OUTPUT)
15482 {
15483 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames);
15484 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames);
15485 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames);
15486 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames);
15487 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames);
15488 }
15489 #endif
15490
15491 /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */
15492 if (actualPeriodInFrames >= desiredPeriodInFrames) {
15493 /*
15494 MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified,
15495 IAudioClient3_InitializeSharedAudioStream() will fail.
15496 */
15497 hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (WAVEFORMATEX*)&wf, NULL);
15498 if (SUCCEEDED(hr)) {
15499 wasInitializedUsingIAudioClient3 = MA_TRUE;
15500 pData->periodSizeInFramesOut = actualPeriodInFrames;
15501 #if defined(MA_DEBUG_OUTPUT)
15502 {
15503 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n");
15504 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut);
15505 }
15506 #endif
15507 } else {
15508 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n");
15509 }
15510 } else {
15511 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");
15512 }
15513 } else {
15514 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n");
15515 }
15516
15517 ma_IAudioClient3_Release(pAudioClient3);
15518 pAudioClient3 = NULL;
15519 }
15520 }
15521 }
15522 #else
15523 {
15524 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");
15525 }
15526 #endif
15527
15528 /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */
15529 if (!wasInitializedUsingIAudioClient3) {
15530 MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */
15531 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL);
15532 if (FAILED(hr)) {
15533 if (hr == E_ACCESSDENIED) {
15534 errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED;
15535 } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
15536 errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY;
15537 } else {
15538 errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr);
15539 }
15540
15541 goto done;
15542 }
15543 }
15544 }
15545
15546 if (!wasInitializedUsingIAudioClient3) {
15547 ma_uint32 bufferSizeInFrames;
15548 hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
15549 if (FAILED(hr)) {
15550 errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr);
15551 goto done;
15552 }
15553
15554 pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut;
15555 }
15556
15557 pData->usingAudioClient3 = wasInitializedUsingIAudioClient3;
15558
15559
15560 if (deviceType == ma_device_type_playback) {
15561 result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient);
15562 } else {
15563 result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient);
15564 }
15565
15566 /*if (FAILED(hr)) {*/
15567 if (result != MA_SUCCESS) {
15568 errorMsg = "[WASAPI] Failed to get audio client service.";
15569 goto done;
15570 }
15571
15572
15573 /* Grab the name of the device. */
15574 #ifdef MA_WIN32_DESKTOP
15575 {
15576 ma_IPropertyStore *pProperties;
15577 hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties);
15578 if (SUCCEEDED(hr)) {
15579 PROPVARIANT varName;
15580 ma_PropVariantInit(&varName);
15581 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName);
15582 if (SUCCEEDED(hr)) {
15583 WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE);
15584 ma_PropVariantClear(pContext, &varName);
15585 }
15586
15587 ma_IPropertyStore_Release(pProperties);
15588 }
15589 }
15590 #endif
15591
15592 /*
15593 For the WASAPI backend we need to know the actual IDs of the device in order to do automatic
15594 stream routing so that IDs can be compared and we can determine which device has been detached
15595 and whether or not it matches with our ma_device.
15596 */
15597 #ifdef MA_WIN32_DESKTOP
15598 {
15599 /* Desktop */
15600 ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id);
15601 }
15602 #else
15603 {
15604 /* UWP */
15605 /* TODO: Implement me. Need to figure out how to get the ID of the default device. */
15606 }
15607 #endif
15608
15609done:
15610 /* Clean up. */
15611#ifdef MA_WIN32_DESKTOP
15612 if (pDeviceInterface != NULL) {
15613 ma_IMMDevice_Release(pDeviceInterface);
15614 }
15615#else
15616 if (pDeviceInterface != NULL) {
15617 ma_IUnknown_Release(pDeviceInterface);
15618 }
15619#endif
15620
15621 if (result != MA_SUCCESS) {
15622 if (pData->pRenderClient) {
15623 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient);
15624 pData->pRenderClient = NULL;
15625 }
15626 if (pData->pCaptureClient) {
15627 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient);
15628 pData->pCaptureClient = NULL;
15629 }
15630 if (pData->pAudioClient) {
15631 ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
15632 pData->pAudioClient = NULL;
15633 }
15634
15635 if (errorMsg != NULL && errorMsg[0] != '\0') {
15636 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_ERROR, errorMsg);
15637 }
15638
15639 return result;
15640 } else {
15641 return MA_SUCCESS;
15642 }
15643}
15644
15645static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType)
15646{
15647 ma_device_init_internal_data__wasapi data;
15648 ma_result result;
15649
15650 MA_ASSERT(pDevice != NULL);
15651
15652 /* We only re-initialize the playback or capture device. Never a full-duplex device. */
15653 if (deviceType == ma_device_type_duplex) {
15654 return MA_INVALID_ARGS;
15655 }
15656
15657
15658 /*
15659 Before reinitializing the device we need to free the previous audio clients.
15660
15661 There's a known memory leak here. We will be calling this from the routing change callback that
15662 is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion
15663 this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably
15664 need some system where we post an event, but delay the execution of it until the callback has
15665 returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for
15666 a command thread which might be useful for this.
15667 */
15668 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
15669 if (pDevice->wasapi.pCaptureClient) {
15670 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
15671 pDevice->wasapi.pCaptureClient = NULL;
15672 }
15673
15674 if (pDevice->wasapi.pAudioClientCapture) {
15675 /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/
15676 pDevice->wasapi.pAudioClientCapture = NULL;
15677 }
15678 }
15679
15680 if (deviceType == ma_device_type_playback) {
15681 if (pDevice->wasapi.pRenderClient) {
15682 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
15683 pDevice->wasapi.pRenderClient = NULL;
15684 }
15685
15686 if (pDevice->wasapi.pAudioClientPlayback) {
15687 /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/
15688 pDevice->wasapi.pAudioClientPlayback = NULL;
15689 }
15690 }
15691
15692
15693 if (deviceType == ma_device_type_playback) {
15694 data.formatIn = pDevice->playback.format;
15695 data.channelsIn = pDevice->playback.channels;
15696 MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
15697 data.shareMode = pDevice->playback.shareMode;
15698 } else {
15699 data.formatIn = pDevice->capture.format;
15700 data.channelsIn = pDevice->capture.channels;
15701 MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
15702 data.shareMode = pDevice->capture.shareMode;
15703 }
15704
15705 data.sampleRateIn = pDevice->sampleRate;
15706 data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames;
15707 data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds;
15708 data.periodsIn = pDevice->wasapi.originalPeriods;
15709 data.performanceProfile = pDevice->wasapi.originalPerformanceProfile;
15710 data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC;
15711 data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC;
15712 data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading;
15713 result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data);
15714 if (result != MA_SUCCESS) {
15715 return result;
15716 }
15717
15718 /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */
15719 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
15720 pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
15721 pDevice->wasapi.pCaptureClient = data.pCaptureClient;
15722
15723 pDevice->capture.internalFormat = data.formatOut;
15724 pDevice->capture.internalChannels = data.channelsOut;
15725 pDevice->capture.internalSampleRate = data.sampleRateOut;
15726 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
15727 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
15728 pDevice->capture.internalPeriods = data.periodsOut;
15729 ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName);
15730
15731 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture);
15732
15733 pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
15734 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualPeriodSizeInFramesCapture);
15735
15736 /* We must always have a valid ID. */
15737 ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);
15738 }
15739
15740 if (deviceType == ma_device_type_playback) {
15741 pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
15742 pDevice->wasapi.pRenderClient = data.pRenderClient;
15743
15744 pDevice->playback.internalFormat = data.formatOut;
15745 pDevice->playback.internalChannels = data.channelsOut;
15746 pDevice->playback.internalSampleRate = data.sampleRateOut;
15747 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
15748 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
15749 pDevice->playback.internalPeriods = data.periodsOut;
15750 ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName);
15751
15752 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback);
15753
15754 pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
15755 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualPeriodSizeInFramesPlayback);
15756
15757 /* We must always have a valid ID. */
15758 ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);
15759 }
15760
15761 return MA_SUCCESS;
15762}
15763
15764static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
15765{
15766 ma_result result = MA_SUCCESS;
15767
15768#ifdef MA_WIN32_DESKTOP
15769 HRESULT hr;
15770 ma_IMMDeviceEnumerator* pDeviceEnumerator;
15771#endif
15772
15773 MA_ASSERT(pDevice != NULL);
15774
15775 MA_ZERO_OBJECT(&pDevice->wasapi);
15776 pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
15777 pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
15778 pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
15779
15780 /* Exclusive mode is not allowed with loopback. */
15781 if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) {
15782 return MA_INVALID_DEVICE_CONFIG;
15783 }
15784
15785 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
15786 ma_device_init_internal_data__wasapi data;
15787 data.formatIn = pDescriptorCapture->format;
15788 data.channelsIn = pDescriptorCapture->channels;
15789 data.sampleRateIn = pDescriptorCapture->sampleRate;
15790 MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
15791 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
15792 data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds;
15793 data.periodsIn = pDescriptorCapture->periodCount;
15794 data.shareMode = pDescriptorCapture->shareMode;
15795 data.performanceProfile = pConfig->performanceProfile;
15796 data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
15797 data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
15798 data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
15799
15800 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);
15801 if (result != MA_SUCCESS) {
15802 return result;
15803 }
15804
15805 pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
15806 pDevice->wasapi.pCaptureClient = data.pCaptureClient;
15807 pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;
15808 pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
15809 pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount;
15810 pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile;
15811
15812 /*
15813 The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled,
15814 however, because we want to block until we actually have something for the first call to ma_device_read().
15815 */
15816 pDevice->wasapi.hEventCapture = CreateEventW(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */
15817 if (pDevice->wasapi.hEventCapture == NULL) {
15818 result = ma_result_from_GetLastError(GetLastError());
15819
15820 if (pDevice->wasapi.pCaptureClient != NULL) {
15821 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
15822 pDevice->wasapi.pCaptureClient = NULL;
15823 }
15824 if (pDevice->wasapi.pAudioClientCapture != NULL) {
15825 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
15826 pDevice->wasapi.pAudioClientCapture = NULL;
15827 }
15828
15829 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture.", result);
15830 }
15831 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture);
15832
15833 pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
15834 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualPeriodSizeInFramesCapture);
15835
15836 /* We must always have a valid ID. */
15837 ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);
15838
15839 /* The descriptor needs to be updated with actual values. */
15840 pDescriptorCapture->format = data.formatOut;
15841 pDescriptorCapture->channels = data.channelsOut;
15842 pDescriptorCapture->sampleRate = data.sampleRateOut;
15843 MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
15844 pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut;
15845 pDescriptorCapture->periodCount = data.periodsOut;
15846 }
15847
15848 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
15849 ma_device_init_internal_data__wasapi data;
15850 data.formatIn = pDescriptorPlayback->format;
15851 data.channelsIn = pDescriptorPlayback->channels;
15852 data.sampleRateIn = pDescriptorPlayback->sampleRate;
15853 MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
15854 data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames;
15855 data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
15856 data.periodsIn = pDescriptorPlayback->periodCount;
15857 data.shareMode = pDescriptorPlayback->shareMode;
15858 data.performanceProfile = pConfig->performanceProfile;
15859 data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
15860 data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
15861 data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
15862
15863 result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data);
15864 if (result != MA_SUCCESS) {
15865 if (pConfig->deviceType == ma_device_type_duplex) {
15866 if (pDevice->wasapi.pCaptureClient != NULL) {
15867 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
15868 pDevice->wasapi.pCaptureClient = NULL;
15869 }
15870 if (pDevice->wasapi.pAudioClientCapture != NULL) {
15871 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
15872 pDevice->wasapi.pAudioClientCapture = NULL;
15873 }
15874
15875 CloseHandle(pDevice->wasapi.hEventCapture);
15876 pDevice->wasapi.hEventCapture = NULL;
15877 }
15878 return result;
15879 }
15880
15881 pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
15882 pDevice->wasapi.pRenderClient = data.pRenderClient;
15883 pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
15884 pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
15885 pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount;
15886 pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile;
15887
15888 /*
15889 The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled
15890 only after the whole available space has been filled, never before.
15891
15892 The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able
15893 to get passed WaitForMultipleObjects().
15894 */
15895 pDevice->wasapi.hEventPlayback = CreateEventW(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */
15896 if (pDevice->wasapi.hEventPlayback == NULL) {
15897 result = ma_result_from_GetLastError(GetLastError());
15898
15899 if (pConfig->deviceType == ma_device_type_duplex) {
15900 if (pDevice->wasapi.pCaptureClient != NULL) {
15901 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
15902 pDevice->wasapi.pCaptureClient = NULL;
15903 }
15904 if (pDevice->wasapi.pAudioClientCapture != NULL) {
15905 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
15906 pDevice->wasapi.pAudioClientCapture = NULL;
15907 }
15908
15909 CloseHandle(pDevice->wasapi.hEventCapture);
15910 pDevice->wasapi.hEventCapture = NULL;
15911 }
15912
15913 if (pDevice->wasapi.pRenderClient != NULL) {
15914 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
15915 pDevice->wasapi.pRenderClient = NULL;
15916 }
15917 if (pDevice->wasapi.pAudioClientPlayback != NULL) {
15918 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
15919 pDevice->wasapi.pAudioClientPlayback = NULL;
15920 }
15921
15922 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback.", result);
15923 }
15924 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback);
15925
15926 pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
15927 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualPeriodSizeInFramesPlayback);
15928
15929 /* We must always have a valid ID. */
15930 ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);
15931
15932 /* The descriptor needs to be updated with actual values. */
15933 pDescriptorPlayback->format = data.formatOut;
15934 pDescriptorPlayback->channels = data.channelsOut;
15935 pDescriptorPlayback->sampleRate = data.sampleRateOut;
15936 MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
15937 pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut;
15938 pDescriptorPlayback->periodCount = data.periodsOut;
15939 }
15940
15941 /*
15942 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
15943 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
15944 stop the device outright and let the application handle it.
15945 */
15946#ifdef MA_WIN32_DESKTOP
15947 if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) {
15948 if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID == NULL) {
15949 pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE;
15950 }
15951 if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) {
15952 pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE;
15953 }
15954 }
15955
15956 hr = ma_CoCreateInstance(pDevice->pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
15957 if (FAILED(hr)) {
15958 ma_device_uninit__wasapi(pDevice);
15959 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr));
15960 }
15961
15962 pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl;
15963 pDevice->wasapi.notificationClient.counter = 1;
15964 pDevice->wasapi.notificationClient.pDevice = pDevice;
15965
15966 hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient);
15967 if (SUCCEEDED(hr)) {
15968 pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator;
15969 } else {
15970 /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */
15971 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
15972 }
15973#endif
15974
15975 c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE);
15976 c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
15977
15978 return MA_SUCCESS;
15979}
15980
15981static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount)
15982{
15983 ma_uint32 paddingFramesCount;
15984 HRESULT hr;
15985 ma_share_mode shareMode;
15986
15987 MA_ASSERT(pDevice != NULL);
15988 MA_ASSERT(pFrameCount != NULL);
15989
15990 *pFrameCount = 0;
15991
15992 if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) {
15993 return MA_INVALID_OPERATION;
15994 }
15995
15996 /*
15997 I've had a report that GetCurrentPadding() is returning a frame count of 0 which is preventing
15998 higher level function calls from doing anything because it thinks nothing is available. I have
15999 taken a look at the documentation and it looks like this is unnecessary in exclusive mode.
16000
16001 From Microsoft's documentation:
16002
16003 For an exclusive-mode rendering or capture stream that was initialized with the
16004 AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag, the client typically has no use for the padding
16005 value reported by GetCurrentPadding. Instead, the client accesses an entire buffer during
16006 each processing pass.
16007
16008 Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the
16009 entire buffer. This depends on the caller making sure they wait on the event handler.
16010 */
16011 shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode;
16012 if (shareMode == ma_share_mode_shared) {
16013 /* Shared mode. */
16014 hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount);
16015 if (FAILED(hr)) {
16016 return ma_result_from_HRESULT(hr);
16017 }
16018
16019 if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
16020 *pFrameCount = pDevice->wasapi.actualPeriodSizeInFramesPlayback - paddingFramesCount;
16021 } else {
16022 *pFrameCount = paddingFramesCount;
16023 }
16024 } else {
16025 /* Exclusive mode. */
16026 if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
16027 *pFrameCount = pDevice->wasapi.actualPeriodSizeInFramesPlayback;
16028 } else {
16029 *pFrameCount = pDevice->wasapi.actualPeriodSizeInFramesCapture;
16030 }
16031 }
16032
16033 return MA_SUCCESS;
16034}
16035
16036
16037static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType)
16038{
16039 ma_result result;
16040
16041 if (deviceType == ma_device_type_duplex) {
16042 return MA_INVALID_ARGS;
16043 }
16044
16045 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== CHANGING DEVICE ===\n");
16046
16047 result = ma_device_reinit__wasapi(pDevice, deviceType);
16048 if (result != MA_SUCCESS) {
16049 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Reinitializing device after route change failed.\n");
16050 return result;
16051 }
16052
16053 ma_device__post_init_setup(pDevice, deviceType);
16054
16055 return MA_SUCCESS;
16056}
16057
16058static ma_result ma_device_start__wasapi(ma_device* pDevice)
16059{
16060 HRESULT hr;
16061
16062 MA_ASSERT(pDevice != NULL);
16063
16064 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
16065 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
16066 if (FAILED(hr)) {
16067 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device.", ma_result_from_HRESULT(hr));
16068 }
16069
16070 c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_TRUE);
16071 }
16072
16073 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
16074 /* No need to do anything for playback as that'll be started automatically in the data loop. */
16075 }
16076
16077 return MA_SUCCESS;
16078}
16079
16080static ma_result ma_device_stop__wasapi(ma_device* pDevice)
16081{
16082 ma_result result;
16083 HRESULT hr;
16084
16085 MA_ASSERT(pDevice != NULL);
16086
16087 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
16088 hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
16089 if (FAILED(hr)) {
16090 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device.", ma_result_from_HRESULT(hr));
16091 }
16092
16093 /* The audio client needs to be reset otherwise restarting will fail. */
16094 hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
16095 if (FAILED(hr)) {
16096 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device.", ma_result_from_HRESULT(hr));
16097 }
16098
16099 c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE);
16100 }
16101
16102 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
16103 /*
16104 The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to
16105 the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played.
16106 */
16107 if (c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) {
16108 /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */
16109 DWORD waitTime = pDevice->wasapi.actualPeriodSizeInFramesPlayback / pDevice->playback.internalSampleRate;
16110
16111 if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
16112 WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime);
16113 } else {
16114 ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1;
16115 ma_uint32 framesAvailablePlayback;
16116 for (;;) {
16117 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
16118 if (result != MA_SUCCESS) {
16119 break;
16120 }
16121
16122 if (framesAvailablePlayback >= pDevice->wasapi.actualPeriodSizeInFramesPlayback) {
16123 break;
16124 }
16125
16126 /*
16127 Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames
16128 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.
16129 */
16130 if (framesAvailablePlayback == prevFramesAvaialablePlayback) {
16131 break;
16132 }
16133 prevFramesAvaialablePlayback = framesAvailablePlayback;
16134
16135 WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime);
16136 ResetEvent(pDevice->wasapi.hEventPlayback); /* Manual reset. */
16137 }
16138 }
16139 }
16140
16141 hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
16142 if (FAILED(hr)) {
16143 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device.", ma_result_from_HRESULT(hr));
16144 }
16145
16146 /* The audio client needs to be reset otherwise restarting will fail. */
16147 hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
16148 if (FAILED(hr)) {
16149 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device.", ma_result_from_HRESULT(hr));
16150 }
16151
16152 c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
16153 }
16154
16155 return MA_SUCCESS;
16156}
16157
16158
16159#ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS
16160#define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000
16161#endif
16162
16163static ma_result ma_device_data_loop__wasapi(ma_device* pDevice)
16164{
16165 ma_result result;
16166 HRESULT hr;
16167 ma_bool32 exitLoop = MA_FALSE;
16168 ma_uint32 framesWrittenToPlaybackDevice = 0;
16169 ma_uint32 mappedDeviceBufferSizeInFramesCapture = 0;
16170 ma_uint32 mappedDeviceBufferSizeInFramesPlayback = 0;
16171 ma_uint32 mappedDeviceBufferFramesRemainingCapture = 0;
16172 ma_uint32 mappedDeviceBufferFramesRemainingPlayback = 0;
16173 BYTE* pMappedDeviceBufferCapture = NULL;
16174 BYTE* pMappedDeviceBufferPlayback = NULL;
16175 ma_uint32 bpfCaptureDevice = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
16176 ma_uint32 bpfPlaybackDevice = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
16177 ma_uint32 bpfCaptureClient = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
16178 ma_uint32 bpfPlaybackClient = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
16179 ma_uint8 inputDataInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
16180 ma_uint32 inputDataInClientFormatCap = 0;
16181 ma_uint8 outputDataInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
16182 ma_uint32 outputDataInClientFormatCap = 0;
16183 ma_uint32 outputDataInClientFormatCount = 0;
16184 ma_uint32 outputDataInClientFormatConsumed = 0;
16185 ma_uint32 periodSizeInFramesCapture = 0;
16186
16187 MA_ASSERT(pDevice != NULL);
16188
16189 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
16190 periodSizeInFramesCapture = pDevice->capture.internalPeriodSizeInFrames;
16191 inputDataInClientFormatCap = sizeof(inputDataInClientFormat) / bpfCaptureClient;
16192 }
16193
16194 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
16195 outputDataInClientFormatCap = sizeof(outputDataInClientFormat) / bpfPlaybackClient;
16196 }
16197
16198 while (ma_device_get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
16199 switch (pDevice->type)
16200 {
16201 case ma_device_type_duplex:
16202 {
16203 ma_uint32 framesAvailableCapture;
16204 ma_uint32 framesAvailablePlayback;
16205 DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */
16206
16207 /* The process is to map the playback buffer and fill it as quickly as possible from input data. */
16208 if (pMappedDeviceBufferPlayback == NULL) {
16209 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
16210 if (result != MA_SUCCESS) {
16211 return result;
16212 }
16213
16214 /* In exclusive mode, the frame count needs to exactly match the value returned by GetCurrentPadding(). */
16215 if (pDevice->playback.shareMode != ma_share_mode_exclusive) {
16216 if (framesAvailablePlayback > pDevice->wasapi.periodSizeInFramesPlayback) {
16217 framesAvailablePlayback = pDevice->wasapi.periodSizeInFramesPlayback;
16218 }
16219 }
16220
16221 /* We're ready to map the playback device's buffer. We don't release this until it's been entirely filled. */
16222 hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, &pMappedDeviceBufferPlayback);
16223 if (FAILED(hr)) {
16224 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
16225 exitLoop = MA_TRUE;
16226 break;
16227 }
16228
16229 mappedDeviceBufferSizeInFramesPlayback = framesAvailablePlayback;
16230 mappedDeviceBufferFramesRemainingPlayback = framesAvailablePlayback;
16231 }
16232
16233 if (mappedDeviceBufferFramesRemainingPlayback > 0) {
16234 /* At this point we should have a buffer available for output. We need to keep writing input samples to it. */
16235 for (;;) {
16236 /* Try grabbing some captured data if we haven't already got a mapped buffer. */
16237 if (pMappedDeviceBufferCapture == NULL) {
16238 if (pDevice->capture.shareMode == ma_share_mode_shared) {
16239 if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
16240 return MA_ERROR; /* Wait failed. */
16241 }
16242 }
16243
16244 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture);
16245 if (result != MA_SUCCESS) {
16246 exitLoop = MA_TRUE;
16247 break;
16248 }
16249
16250 /* Wait for more if nothing is available. */
16251 if (framesAvailableCapture == 0) {
16252 /* In exclusive mode we waited at the top. */
16253 if (pDevice->capture.shareMode != ma_share_mode_shared) {
16254 if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
16255 return MA_ERROR; /* Wait failed. */
16256 }
16257 }
16258
16259 continue;
16260 }
16261
16262 /* Getting here means there's data available for writing to the output device. */
16263 mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture);
16264 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
16265 if (FAILED(hr)) {
16266 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
16267 exitLoop = MA_TRUE;
16268 break;
16269 }
16270
16271
16272 /* Overrun detection. */
16273 if ((flagsCapture & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
16274 /* Glitched. Probably due to an overrun. */
16275 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
16276
16277 /*
16278 Exeriment: If we get an overrun it probably means we're straddling the end of the buffer. In order to prevent a never-ending sequence of glitches let's experiment
16279 by dropping every frame until we're left with only a single period. To do this we just keep retrieving and immediately releasing buffers until we're down to the
16280 last period.
16281 */
16282 if (framesAvailableCapture >= pDevice->wasapi.actualPeriodSizeInFramesCapture) {
16283 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Synchronizing capture stream. ");
16284 do
16285 {
16286 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
16287 if (FAILED(hr)) {
16288 break;
16289 }
16290
16291 framesAvailableCapture -= mappedDeviceBufferSizeInFramesCapture;
16292
16293 if (framesAvailableCapture > 0) {
16294 mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture);
16295 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
16296 if (FAILED(hr)) {
16297 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
16298 exitLoop = MA_TRUE;
16299 break;
16300 }
16301 } else {
16302 pMappedDeviceBufferCapture = NULL;
16303 mappedDeviceBufferSizeInFramesCapture = 0;
16304 }
16305 } while (framesAvailableCapture > periodSizeInFramesCapture);
16306 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
16307 }
16308 } else {
16309 #ifdef MA_DEBUG_OUTPUT
16310 if (flagsCapture != 0) {
16311 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flagsCapture);
16312 }
16313 #endif
16314 }
16315
16316 mappedDeviceBufferFramesRemainingCapture = mappedDeviceBufferSizeInFramesCapture;
16317 }
16318
16319
16320 /* At this point we should have both input and output data available. We now need to convert the data and post it to the client. */
16321 for (;;) {
16322 BYTE* pRunningDeviceBufferCapture;
16323 BYTE* pRunningDeviceBufferPlayback;
16324 ma_uint32 framesToProcess;
16325 ma_uint32 framesProcessed;
16326
16327 pRunningDeviceBufferCapture = pMappedDeviceBufferCapture + ((mappedDeviceBufferSizeInFramesCapture - mappedDeviceBufferFramesRemainingCapture ) * bpfCaptureDevice);
16328 pRunningDeviceBufferPlayback = pMappedDeviceBufferPlayback + ((mappedDeviceBufferSizeInFramesPlayback - mappedDeviceBufferFramesRemainingPlayback) * bpfPlaybackDevice);
16329
16330 /* There may be some data sitting in the converter that needs to be processed first. Once this is exhaused, run the data callback again. */
16331 if (!pDevice->playback.converter.isPassthrough && outputDataInClientFormatConsumed < outputDataInClientFormatCount) {
16332 ma_uint64 convertedFrameCountClient = (outputDataInClientFormatCount - outputDataInClientFormatConsumed);
16333 ma_uint64 convertedFrameCountDevice = mappedDeviceBufferFramesRemainingPlayback;
16334 void* pConvertedFramesClient = outputDataInClientFormat + (outputDataInClientFormatConsumed * bpfPlaybackClient);
16335 void* pConvertedFramesDevice = pRunningDeviceBufferPlayback;
16336 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesClient, &convertedFrameCountClient, pConvertedFramesDevice, &convertedFrameCountDevice);
16337 if (result != MA_SUCCESS) {
16338 break;
16339 }
16340
16341 outputDataInClientFormatConsumed += (ma_uint32)convertedFrameCountClient; /* Safe cast. */
16342 mappedDeviceBufferFramesRemainingPlayback -= (ma_uint32)convertedFrameCountDevice; /* Safe cast. */
16343
16344 if (mappedDeviceBufferFramesRemainingPlayback == 0) {
16345 break;
16346 }
16347 }
16348
16349 /*
16350 Getting here means we need to fire the callback. If format conversion is unnecessary, we can optimize this by passing the pointers to the internal
16351 buffers directly to the callback.
16352 */
16353 if (pDevice->capture.converter.isPassthrough && pDevice->playback.converter.isPassthrough) {
16354 /* Optimal path. We can pass mapped pointers directly to the callback. */
16355 framesToProcess = ma_min(mappedDeviceBufferFramesRemainingCapture, mappedDeviceBufferFramesRemainingPlayback);
16356 framesProcessed = framesToProcess;
16357
16358 ma_device__on_data(pDevice, pRunningDeviceBufferPlayback, pRunningDeviceBufferCapture, framesToProcess);
16359
16360 mappedDeviceBufferFramesRemainingCapture -= framesProcessed;
16361 mappedDeviceBufferFramesRemainingPlayback -= framesProcessed;
16362
16363 if (mappedDeviceBufferFramesRemainingCapture == 0) {
16364 break; /* Exhausted input data. */
16365 }
16366 if (mappedDeviceBufferFramesRemainingPlayback == 0) {
16367 break; /* Exhausted output data. */
16368 }
16369 } else if (pDevice->capture.converter.isPassthrough) {
16370 /* The input buffer is a passthrough, but the playback buffer requires a conversion. */
16371 framesToProcess = ma_min(mappedDeviceBufferFramesRemainingCapture, outputDataInClientFormatCap);
16372 framesProcessed = framesToProcess;
16373
16374 ma_device__on_data(pDevice, outputDataInClientFormat, pRunningDeviceBufferCapture, framesToProcess);
16375 outputDataInClientFormatCount = framesProcessed;
16376 outputDataInClientFormatConsumed = 0;
16377
16378 mappedDeviceBufferFramesRemainingCapture -= framesProcessed;
16379 if (mappedDeviceBufferFramesRemainingCapture == 0) {
16380 break; /* Exhausted input data. */
16381 }
16382 } else if (pDevice->playback.converter.isPassthrough) {
16383 /* The input buffer requires conversion, the playback buffer is passthrough. */
16384 ma_uint64 capturedDeviceFramesToProcess = mappedDeviceBufferFramesRemainingCapture;
16385 ma_uint64 capturedClientFramesToProcess = ma_min(inputDataInClientFormatCap, mappedDeviceBufferFramesRemainingPlayback);
16386
16387 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningDeviceBufferCapture, &capturedDeviceFramesToProcess, inputDataInClientFormat, &capturedClientFramesToProcess);
16388 if (result != MA_SUCCESS) {
16389 break;
16390 }
16391
16392 if (capturedClientFramesToProcess == 0) {
16393 break;
16394 }
16395
16396 ma_device__on_data(pDevice, pRunningDeviceBufferPlayback, inputDataInClientFormat, (ma_uint32)capturedClientFramesToProcess); /* Safe cast. */
16397
16398 mappedDeviceBufferFramesRemainingCapture -= (ma_uint32)capturedDeviceFramesToProcess;
16399 mappedDeviceBufferFramesRemainingPlayback -= (ma_uint32)capturedClientFramesToProcess;
16400 } else {
16401 ma_uint64 capturedDeviceFramesToProcess = mappedDeviceBufferFramesRemainingCapture;
16402 ma_uint64 capturedClientFramesToProcess = ma_min(inputDataInClientFormatCap, outputDataInClientFormatCap);
16403
16404 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningDeviceBufferCapture, &capturedDeviceFramesToProcess, inputDataInClientFormat, &capturedClientFramesToProcess);
16405 if (result != MA_SUCCESS) {
16406 break;
16407 }
16408
16409 if (capturedClientFramesToProcess == 0) {
16410 break;
16411 }
16412
16413 ma_device__on_data(pDevice, outputDataInClientFormat, inputDataInClientFormat, (ma_uint32)capturedClientFramesToProcess);
16414
16415 mappedDeviceBufferFramesRemainingCapture -= (ma_uint32)capturedDeviceFramesToProcess;
16416 outputDataInClientFormatCount = (ma_uint32)capturedClientFramesToProcess;
16417 outputDataInClientFormatConsumed = 0;
16418 }
16419 }
16420
16421
16422 /* If at this point we've run out of capture data we need to release the buffer. */
16423 if (mappedDeviceBufferFramesRemainingCapture == 0 && pMappedDeviceBufferCapture != NULL) {
16424 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
16425 if (FAILED(hr)) {
16426 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from capture device after reading from the device.", ma_result_from_HRESULT(hr));
16427 exitLoop = MA_TRUE;
16428 break;
16429 }
16430
16431 pMappedDeviceBufferCapture = NULL;
16432 mappedDeviceBufferFramesRemainingCapture = 0;
16433 mappedDeviceBufferSizeInFramesCapture = 0;
16434 }
16435
16436 /* Get out of this loop if we're run out of room in the playback buffer. */
16437 if (mappedDeviceBufferFramesRemainingPlayback == 0) {
16438 break;
16439 }
16440 }
16441 }
16442
16443
16444 /* If at this point we've run out of data we need to release the buffer. */
16445 if (mappedDeviceBufferFramesRemainingPlayback == 0 && pMappedDeviceBufferPlayback != NULL) {
16446 hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, mappedDeviceBufferSizeInFramesPlayback, 0);
16447 if (FAILED(hr)) {
16448 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from playback device after writing to the device.", ma_result_from_HRESULT(hr));
16449 exitLoop = MA_TRUE;
16450 break;
16451 }
16452
16453 framesWrittenToPlaybackDevice += mappedDeviceBufferSizeInFramesPlayback;
16454
16455 pMappedDeviceBufferPlayback = NULL;
16456 mappedDeviceBufferFramesRemainingPlayback = 0;
16457 mappedDeviceBufferSizeInFramesPlayback = 0;
16458 }
16459
16460 if (!c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) {
16461 ma_uint32 startThreshold = pDevice->playback.internalPeriodSizeInFrames * 1;
16462
16463 /* Prevent a deadlock. If we don't clamp against the actual buffer size we'll never end up starting the playback device which will result in a deadlock. */
16464 if (startThreshold > pDevice->wasapi.actualPeriodSizeInFramesPlayback) {
16465 startThreshold = pDevice->wasapi.actualPeriodSizeInFramesPlayback;
16466 }
16467
16468 if (pDevice->playback.shareMode == ma_share_mode_exclusive || framesWrittenToPlaybackDevice >= startThreshold) {
16469 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
16470 if (FAILED(hr)) {
16471 ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
16472 ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
16473 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", ma_result_from_HRESULT(hr));
16474 }
16475
16476 c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
16477 }
16478 }
16479
16480 /* Make sure the device has started before waiting. */
16481 if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
16482 return MA_ERROR; /* Wait failed. */
16483 }
16484 } break;
16485
16486
16487
16488 case ma_device_type_capture:
16489 case ma_device_type_loopback:
16490 {
16491 ma_uint32 framesAvailableCapture;
16492 DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */
16493
16494 /* Wait for data to become available first. */
16495 if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
16496 /*
16497 For capture we can terminate here because it probably means the microphone just isn't delivering data for whatever reason, but
16498 for loopback is most likely means nothing is actually playing. We want to keep trying in this situation.
16499 */
16500 if (pDevice->type == ma_device_type_loopback) {
16501 continue; /* Keep waiting in loopback mode. */
16502 } else {
16503 exitLoop = MA_TRUE;
16504 break; /* Wait failed. */
16505 }
16506 }
16507
16508 /* See how many frames are available. Since we waited at the top, I don't think this should ever return 0. I'm checking for this anyway. */
16509 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture);
16510 if (result != MA_SUCCESS) {
16511 exitLoop = MA_TRUE;
16512 break;
16513 }
16514
16515 if (framesAvailableCapture < pDevice->wasapi.periodSizeInFramesCapture) {
16516 continue; /* Nothing available. Keep waiting. */
16517 }
16518
16519 /* Map the data buffer in preparation for sending to the client. */
16520 mappedDeviceBufferSizeInFramesCapture = framesAvailableCapture;
16521 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
16522 if (FAILED(hr)) {
16523 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
16524 exitLoop = MA_TRUE;
16525 break;
16526 }
16527
16528 /* Overrun detection. */
16529 if ((flagsCapture & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
16530 /* Glitched. Probably due to an overrun. */
16531 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
16532
16533 /*
16534 Exeriment: If we get an overrun it probably means we're straddling the end of the buffer. In order to prevent a never-ending sequence of glitches let's experiment
16535 by dropping every frame until we're left with only a single period. To do this we just keep retrieving and immediately releasing buffers until we're down to the
16536 last period.
16537 */
16538 if (framesAvailableCapture >= pDevice->wasapi.actualPeriodSizeInFramesCapture) {
16539 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Synchronizing capture stream. ");
16540 do
16541 {
16542 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
16543 if (FAILED(hr)) {
16544 break;
16545 }
16546
16547 framesAvailableCapture -= mappedDeviceBufferSizeInFramesCapture;
16548
16549 if (framesAvailableCapture > 0) {
16550 mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture);
16551 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
16552 if (FAILED(hr)) {
16553 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
16554 exitLoop = MA_TRUE;
16555 break;
16556 }
16557 } else {
16558 pMappedDeviceBufferCapture = NULL;
16559 mappedDeviceBufferSizeInFramesCapture = 0;
16560 }
16561 } while (framesAvailableCapture > periodSizeInFramesCapture);
16562 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
16563 }
16564 } else {
16565 #ifdef MA_DEBUG_OUTPUT
16566 if (flagsCapture != 0) {
16567 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flagsCapture);
16568 }
16569 #endif
16570 }
16571
16572 /* We should have a buffer at this point, but let's just do a sanity check anyway. */
16573 if (mappedDeviceBufferSizeInFramesCapture > 0 && pMappedDeviceBufferCapture != NULL) {
16574 ma_device__send_frames_to_client(pDevice, mappedDeviceBufferSizeInFramesCapture, pMappedDeviceBufferCapture);
16575
16576 /* At this point we're done with the buffer. */
16577 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
16578 pMappedDeviceBufferCapture = NULL; /* <-- Important. Not doing this can result in an error once we leave this loop because it will use this to know whether or not a final ReleaseBuffer() needs to be called. */
16579 mappedDeviceBufferSizeInFramesCapture = 0;
16580 if (FAILED(hr)) {
16581 ma_post_log_message(ma_device_get_context(pDevice), pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from capture device after reading from the device.");
16582 exitLoop = MA_TRUE;
16583 break;
16584 }
16585 }
16586 } break;
16587
16588
16589
16590 case ma_device_type_playback:
16591 {
16592 ma_uint32 framesAvailablePlayback;
16593
16594 /* Check how much space is available. If this returns 0 we just keep waiting. */
16595 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
16596 if (result != MA_SUCCESS) {
16597 exitLoop = MA_TRUE;
16598 break;
16599 }
16600
16601 if (framesAvailablePlayback >= pDevice->wasapi.periodSizeInFramesPlayback) {
16602 /* Map a the data buffer in preparation for the callback. */
16603 hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, &pMappedDeviceBufferPlayback);
16604 if (FAILED(hr)) {
16605 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
16606 exitLoop = MA_TRUE;
16607 break;
16608 }
16609
16610 /* We should have a buffer at this point. */
16611 ma_device__read_frames_from_client(pDevice, framesAvailablePlayback, pMappedDeviceBufferPlayback);
16612
16613 /* At this point we're done writing to the device and we just need to release the buffer. */
16614 hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, 0);
16615 pMappedDeviceBufferPlayback = NULL; /* <-- Important. Not doing this can result in an error once we leave this loop because it will use this to know whether or not a final ReleaseBuffer() needs to be called. */
16616 mappedDeviceBufferSizeInFramesPlayback = 0;
16617
16618 if (FAILED(hr)) {
16619 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from playback device after writing to the device.", ma_result_from_HRESULT(hr));
16620 exitLoop = MA_TRUE;
16621 break;
16622 }
16623
16624 framesWrittenToPlaybackDevice += framesAvailablePlayback;
16625 }
16626
16627 if (!c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) {
16628 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
16629 if (FAILED(hr)) {
16630 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", ma_result_from_HRESULT(hr));
16631 exitLoop = MA_TRUE;
16632 break;
16633 }
16634
16635 c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
16636 }
16637
16638 /* Make sure we don't wait on the event before we've started the device or we may end up deadlocking. */
16639 if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
16640 exitLoop = MA_TRUE;
16641 break; /* Wait failed. Probably timed out. */
16642 }
16643 } break;
16644
16645 default: return MA_INVALID_ARGS;
16646 }
16647 }
16648
16649 /* Here is where the device needs to be stopped. */
16650 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
16651 /* Any mapped buffers need to be released. */
16652 if (pMappedDeviceBufferCapture != NULL) {
16653 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
16654 }
16655 }
16656
16657 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
16658 /* Any mapped buffers need to be released. */
16659 if (pMappedDeviceBufferPlayback != NULL) {
16660 hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, mappedDeviceBufferSizeInFramesPlayback, 0);
16661 }
16662 }
16663
16664 return MA_SUCCESS;
16665}
16666
16667static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice)
16668{
16669 MA_ASSERT(pDevice != NULL);
16670
16671 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
16672 SetEvent((HANDLE)pDevice->wasapi.hEventCapture);
16673 }
16674
16675 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
16676 SetEvent((HANDLE)pDevice->wasapi.hEventPlayback);
16677 }
16678
16679 return MA_SUCCESS;
16680}
16681
16682
16683static ma_result ma_context_uninit__wasapi(ma_context* pContext)
16684{
16685 MA_ASSERT(pContext != NULL);
16686 MA_ASSERT(pContext->backend == ma_backend_wasapi);
16687
16688 if (pContext->wasapi.commandThread != NULL) {
16689 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI);
16690 ma_context_post_command__wasapi(pContext, &cmd);
16691 ma_thread_wait(&pContext->wasapi.commandThread);
16692
16693 /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */
16694 ma_semaphore_uninit(&pContext->wasapi.commandSem);
16695 ma_mutex_uninit(&pContext->wasapi.commandLock);
16696 }
16697
16698 return MA_SUCCESS;
16699}
16700
16701static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
16702{
16703 ma_result result = MA_SUCCESS;
16704
16705 MA_ASSERT(pContext != NULL);
16706
16707 (void)pConfig;
16708
16709#ifdef MA_WIN32_DESKTOP
16710 /*
16711 WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven
16712 exclusive mode does not work until SP1.
16713
16714 Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error.
16715 */
16716 {
16717 ma_OSVERSIONINFOEXW osvi;
16718 ma_handle kernel32DLL;
16719 ma_PFNVerifyVersionInfoW _VerifyVersionInfoW;
16720 ma_PFNVerSetConditionMask _VerSetConditionMask;
16721
16722 kernel32DLL = ma_dlopen(pContext, "kernel32.dll");
16723 if (kernel32DLL == NULL) {
16724 return MA_NO_BACKEND;
16725 }
16726
16727 _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(pContext, kernel32DLL, "VerifyVersionInfoW");
16728 _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(pContext, kernel32DLL, "VerSetConditionMask");
16729 if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) {
16730 ma_dlclose(pContext, kernel32DLL);
16731 return MA_NO_BACKEND;
16732 }
16733
16734 MA_ZERO_OBJECT(&osvi);
16735 osvi.dwOSVersionInfoSize = sizeof(osvi);
16736 osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF);
16737 osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF);
16738 osvi.wServicePackMajor = 1;
16739 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))) {
16740 result = MA_SUCCESS;
16741 } else {
16742 result = MA_NO_BACKEND;
16743 }
16744
16745 ma_dlclose(pContext, kernel32DLL);
16746 }
16747#endif
16748
16749 if (result != MA_SUCCESS) {
16750 return result;
16751 }
16752
16753 MA_ZERO_OBJECT(&pContext->wasapi);
16754
16755 /*
16756 Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread
16757 than the one that retrieved it with GetService(). This can result in a deadlock in two
16758 situations:
16759
16760 1) When calling ma_device_uninit() from a different thread to ma_device_init(); and
16761 2) When uninitializing and reinitializing the internal IAudioClient object in response to
16762 automatic stream routing.
16763
16764 We could define ma_device_uninit() such that it must be called on the same thread as
16765 ma_device_init(). We could also just not release the IAudioClient when performing automatic
16766 stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so
16767 we're going to have to work around this with a worker thread. This is not ideal, but I can't
16768 think of a better way to do this.
16769
16770 More information about this can be found here:
16771
16772 https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient
16773
16774 Note this section:
16775
16776 When releasing an IAudioRenderClient interface instance, the client must call the interface's
16777 Release method from the same thread as the call to IAudioClient::GetService that created the
16778 object.
16779 */
16780 {
16781 result = ma_mutex_init(&pContext->wasapi.commandLock);
16782 if (result != MA_SUCCESS) {
16783 return result;
16784 }
16785
16786 result = ma_semaphore_init(0, &pContext->wasapi.commandSem);
16787 if (result != MA_SUCCESS) {
16788 ma_mutex_uninit(&pContext->wasapi.commandLock);
16789 return result;
16790 }
16791
16792 result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks);
16793 if (result != MA_SUCCESS) {
16794 ma_semaphore_uninit(&pContext->wasapi.commandSem);
16795 ma_mutex_uninit(&pContext->wasapi.commandLock);
16796 return result;
16797 }
16798 }
16799
16800
16801 pCallbacks->onContextInit = ma_context_init__wasapi;
16802 pCallbacks->onContextUninit = ma_context_uninit__wasapi;
16803 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi;
16804 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi;
16805 pCallbacks->onDeviceInit = ma_device_init__wasapi;
16806 pCallbacks->onDeviceUninit = ma_device_uninit__wasapi;
16807 pCallbacks->onDeviceStart = ma_device_start__wasapi;
16808 pCallbacks->onDeviceStop = ma_device_stop__wasapi;
16809 pCallbacks->onDeviceRead = NULL; /* Not used. Reading is done manually in the audio thread. */
16810 pCallbacks->onDeviceWrite = NULL; /* Not used. Writing is done manually in the audio thread. */
16811 pCallbacks->onDeviceDataLoop = ma_device_data_loop__wasapi;
16812 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi;
16813
16814 return MA_SUCCESS;
16815}
16816#endif
16817
16818/******************************************************************************
16819
16820DirectSound Backend
16821
16822******************************************************************************/
16823#ifdef MA_HAS_DSOUND
16824/*#include <dsound.h>*/
16825
16826/*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/
16827
16828/* miniaudio only uses priority or exclusive modes. */
16829#define MA_DSSCL_NORMAL 1
16830#define MA_DSSCL_PRIORITY 2
16831#define MA_DSSCL_EXCLUSIVE 3
16832#define MA_DSSCL_WRITEPRIMARY 4
16833
16834#define MA_DSCAPS_PRIMARYMONO 0x00000001
16835#define MA_DSCAPS_PRIMARYSTEREO 0x00000002
16836#define MA_DSCAPS_PRIMARY8BIT 0x00000004
16837#define MA_DSCAPS_PRIMARY16BIT 0x00000008
16838#define MA_DSCAPS_CONTINUOUSRATE 0x00000010
16839#define MA_DSCAPS_EMULDRIVER 0x00000020
16840#define MA_DSCAPS_CERTIFIED 0x00000040
16841#define MA_DSCAPS_SECONDARYMONO 0x00000100
16842#define MA_DSCAPS_SECONDARYSTEREO 0x00000200
16843#define MA_DSCAPS_SECONDARY8BIT 0x00000400
16844#define MA_DSCAPS_SECONDARY16BIT 0x00000800
16845
16846#define MA_DSBCAPS_PRIMARYBUFFER 0x00000001
16847#define MA_DSBCAPS_STATIC 0x00000002
16848#define MA_DSBCAPS_LOCHARDWARE 0x00000004
16849#define MA_DSBCAPS_LOCSOFTWARE 0x00000008
16850#define MA_DSBCAPS_CTRL3D 0x00000010
16851#define MA_DSBCAPS_CTRLFREQUENCY 0x00000020
16852#define MA_DSBCAPS_CTRLPAN 0x00000040
16853#define MA_DSBCAPS_CTRLVOLUME 0x00000080
16854#define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100
16855#define MA_DSBCAPS_CTRLFX 0x00000200
16856#define MA_DSBCAPS_STICKYFOCUS 0x00004000
16857#define MA_DSBCAPS_GLOBALFOCUS 0x00008000
16858#define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000
16859#define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000
16860#define MA_DSBCAPS_LOCDEFER 0x00040000
16861#define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000
16862
16863#define MA_DSBPLAY_LOOPING 0x00000001
16864#define MA_DSBPLAY_LOCHARDWARE 0x00000002
16865#define MA_DSBPLAY_LOCSOFTWARE 0x00000004
16866#define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008
16867#define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010
16868#define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020
16869
16870#define MA_DSCBSTART_LOOPING 0x00000001
16871
16872typedef struct
16873{
16874 DWORD dwSize;
16875 DWORD dwFlags;
16876 DWORD dwBufferBytes;
16877 DWORD dwReserved;
16878 WAVEFORMATEX* lpwfxFormat;
16879 GUID guid3DAlgorithm;
16880} MA_DSBUFFERDESC;
16881
16882typedef struct
16883{
16884 DWORD dwSize;
16885 DWORD dwFlags;
16886 DWORD dwBufferBytes;
16887 DWORD dwReserved;
16888 WAVEFORMATEX* lpwfxFormat;
16889 DWORD dwFXCount;
16890 void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */
16891} MA_DSCBUFFERDESC;
16892
16893typedef struct
16894{
16895 DWORD dwSize;
16896 DWORD dwFlags;
16897 DWORD dwMinSecondarySampleRate;
16898 DWORD dwMaxSecondarySampleRate;
16899 DWORD dwPrimaryBuffers;
16900 DWORD dwMaxHwMixingAllBuffers;
16901 DWORD dwMaxHwMixingStaticBuffers;
16902 DWORD dwMaxHwMixingStreamingBuffers;
16903 DWORD dwFreeHwMixingAllBuffers;
16904 DWORD dwFreeHwMixingStaticBuffers;
16905 DWORD dwFreeHwMixingStreamingBuffers;
16906 DWORD dwMaxHw3DAllBuffers;
16907 DWORD dwMaxHw3DStaticBuffers;
16908 DWORD dwMaxHw3DStreamingBuffers;
16909 DWORD dwFreeHw3DAllBuffers;
16910 DWORD dwFreeHw3DStaticBuffers;
16911 DWORD dwFreeHw3DStreamingBuffers;
16912 DWORD dwTotalHwMemBytes;
16913 DWORD dwFreeHwMemBytes;
16914 DWORD dwMaxContigFreeHwMemBytes;
16915 DWORD dwUnlockTransferRateHwBuffers;
16916 DWORD dwPlayCpuOverheadSwBuffers;
16917 DWORD dwReserved1;
16918 DWORD dwReserved2;
16919} MA_DSCAPS;
16920
16921typedef struct
16922{
16923 DWORD dwSize;
16924 DWORD dwFlags;
16925 DWORD dwBufferBytes;
16926 DWORD dwUnlockTransferRate;
16927 DWORD dwPlayCpuOverhead;
16928} MA_DSBCAPS;
16929
16930typedef struct
16931{
16932 DWORD dwSize;
16933 DWORD dwFlags;
16934 DWORD dwFormats;
16935 DWORD dwChannels;
16936} MA_DSCCAPS;
16937
16938typedef struct
16939{
16940 DWORD dwSize;
16941 DWORD dwFlags;
16942 DWORD dwBufferBytes;
16943 DWORD dwReserved;
16944} MA_DSCBCAPS;
16945
16946typedef struct
16947{
16948 DWORD dwOffset;
16949 HANDLE hEventNotify;
16950} MA_DSBPOSITIONNOTIFY;
16951
16952typedef struct ma_IDirectSound ma_IDirectSound;
16953typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer;
16954typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture;
16955typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer;
16956typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify;
16957
16958
16959/*
16960COM objects. The way these work is that you have a vtable (a list of function pointers, kind of
16961like how C++ works internally), and then you have a structure with a single member, which is a
16962pointer to the vtable. The vtable is where the methods of the object are defined. Methods need
16963to be in a specific order, and parent classes need to have their methods declared first.
16964*/
16965
16966/* IDirectSound */
16967typedef struct
16968{
16969 /* IUnknown */
16970 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject);
16971 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis);
16972 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis);
16973
16974 /* IDirectSound */
16975 HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter);
16976 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps);
16977 HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate);
16978 HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel);
16979 HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis);
16980 HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig);
16981 HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig);
16982 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice);
16983} ma_IDirectSoundVtbl;
16984struct ma_IDirectSound
16985{
16986 ma_IDirectSoundVtbl* lpVtbl;
16987};
16988static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
16989static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); }
16990static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); }
16991static 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); }
16992static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); }
16993static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); }
16994static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); }
16995static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); }
16996static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); }
16997static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); }
16998static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
16999
17000
17001/* IDirectSoundBuffer */
17002typedef struct
17003{
17004 /* IUnknown */
17005 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject);
17006 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis);
17007 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis);
17008
17009 /* IDirectSoundBuffer */
17010 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps);
17011 HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor);
17012 HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
17013 HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume);
17014 HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan);
17015 HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency);
17016 HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus);
17017 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc);
17018 HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
17019 HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags);
17020 HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition);
17021 HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat);
17022 HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume);
17023 HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan);
17024 HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency);
17025 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis);
17026 HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
17027 HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis);
17028} ma_IDirectSoundBufferVtbl;
17029struct ma_IDirectSoundBuffer
17030{
17031 ma_IDirectSoundBufferVtbl* lpVtbl;
17032};
17033static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
17034static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
17035static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
17036static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); }
17037static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); }
17038static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
17039static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); }
17040static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); }
17041static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); }
17042static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
17043static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); }
17044static 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); }
17045static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); }
17046static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); }
17047static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); }
17048static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); }
17049static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); }
17050static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); }
17051static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
17052static 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); }
17053static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); }
17054
17055
17056/* IDirectSoundCapture */
17057typedef struct
17058{
17059 /* IUnknown */
17060 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject);
17061 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis);
17062 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis);
17063
17064 /* IDirectSoundCapture */
17065 HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter);
17066 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps);
17067 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice);
17068} ma_IDirectSoundCaptureVtbl;
17069struct ma_IDirectSoundCapture
17070{
17071 ma_IDirectSoundCaptureVtbl* lpVtbl;
17072};
17073static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
17074static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef(ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); }
17075static MA_INLINE ULONG ma_IDirectSoundCapture_Release(ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); }
17076static 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); }
17077static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); }
17078static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
17079
17080
17081/* IDirectSoundCaptureBuffer */
17082typedef struct
17083{
17084 /* IUnknown */
17085 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject);
17086 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis);
17087 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis);
17088
17089 /* IDirectSoundCaptureBuffer */
17090 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps);
17091 HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition);
17092 HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
17093 HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus);
17094 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc);
17095 HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
17096 HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags);
17097 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis);
17098 HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
17099} ma_IDirectSoundCaptureBufferVtbl;
17100struct ma_IDirectSoundCaptureBuffer
17101{
17102 ma_IDirectSoundCaptureBufferVtbl* lpVtbl;
17103};
17104static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
17105static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
17106static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
17107static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); }
17108static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); }
17109static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
17110static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
17111static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); }
17112static 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); }
17113static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); }
17114static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
17115static 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); }
17116
17117
17118/* IDirectSoundNotify */
17119typedef struct
17120{
17121 /* IUnknown */
17122 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject);
17123 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis);
17124 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis);
17125
17126 /* IDirectSoundNotify */
17127 HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies);
17128} ma_IDirectSoundNotifyVtbl;
17129struct ma_IDirectSoundNotify
17130{
17131 ma_IDirectSoundNotifyVtbl* lpVtbl;
17132};
17133static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
17134static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); }
17135static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); }
17136static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); }
17137
17138
17139typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (LPGUID pDeviceGUID, LPCSTR pDeviceDescription, LPCSTR pModule, LPVOID pContext);
17140typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, LPUNKNOWN pUnkOuter);
17141typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext);
17142typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, LPUNKNOWN pUnkOuter);
17143typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext);
17144
17145static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax)
17146{
17147 /* Normalize the range in case we were given something stupid. */
17148 if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) {
17149 sampleRateMin = (ma_uint32)ma_standard_sample_rate_min;
17150 }
17151 if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) {
17152 sampleRateMax = (ma_uint32)ma_standard_sample_rate_max;
17153 }
17154 if (sampleRateMin > sampleRateMax) {
17155 sampleRateMin = sampleRateMax;
17156 }
17157
17158 if (sampleRateMin == sampleRateMax) {
17159 return sampleRateMax;
17160 } else {
17161 size_t iStandardRate;
17162 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
17163 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
17164 if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) {
17165 return standardRate;
17166 }
17167 }
17168 }
17169
17170 /* Should never get here. */
17171 MA_ASSERT(MA_FALSE);
17172 return 0;
17173}
17174
17175/*
17176Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown,
17177the channel count and channel map will be left unmodified.
17178*/
17179static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut)
17180{
17181 WORD channels;
17182 DWORD channelMap;
17183
17184 channels = 0;
17185 if (pChannelsOut != NULL) {
17186 channels = *pChannelsOut;
17187 }
17188
17189 channelMap = 0;
17190 if (pChannelMapOut != NULL) {
17191 channelMap = *pChannelMapOut;
17192 }
17193
17194 /*
17195 The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper
17196 16 bits is for the geometry.
17197 */
17198 switch ((BYTE)(speakerConfig)) {
17199 case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
17200 case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break;
17201 case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
17202 case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
17203 case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break;
17204 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;
17205 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;
17206 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;
17207 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;
17208 default: break;
17209 }
17210
17211 if (pChannelsOut != NULL) {
17212 *pChannelsOut = channels;
17213 }
17214
17215 if (pChannelMapOut != NULL) {
17216 *pChannelMapOut = channelMap;
17217 }
17218}
17219
17220
17221static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound)
17222{
17223 ma_IDirectSound* pDirectSound;
17224 HWND hWnd;
17225 HRESULT hr;
17226
17227 MA_ASSERT(pContext != NULL);
17228 MA_ASSERT(ppDirectSound != NULL);
17229
17230 *ppDirectSound = NULL;
17231 pDirectSound = NULL;
17232
17233 if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) {
17234 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
17235 }
17236
17237 /* The cooperative level must be set before doing anything else. */
17238 hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
17239 if (hWnd == NULL) {
17240 hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
17241 }
17242
17243 hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY);
17244 if (FAILED(hr)) {
17245 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.", ma_result_from_HRESULT(hr));
17246 }
17247
17248 *ppDirectSound = pDirectSound;
17249 return MA_SUCCESS;
17250}
17251
17252static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture)
17253{
17254 ma_IDirectSoundCapture* pDirectSoundCapture;
17255 HRESULT hr;
17256
17257 MA_ASSERT(pContext != NULL);
17258 MA_ASSERT(ppDirectSoundCapture != NULL);
17259
17260 /* DirectSound does not support exclusive mode for capture. */
17261 if (shareMode == ma_share_mode_exclusive) {
17262 return MA_SHARE_MODE_NOT_SUPPORTED;
17263 }
17264
17265 *ppDirectSoundCapture = NULL;
17266 pDirectSoundCapture = NULL;
17267
17268 hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL);
17269 if (FAILED(hr)) {
17270 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.", ma_result_from_HRESULT(hr));
17271 }
17272
17273 *ppDirectSoundCapture = pDirectSoundCapture;
17274 return MA_SUCCESS;
17275}
17276
17277static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate)
17278{
17279 HRESULT hr;
17280 MA_DSCCAPS caps;
17281 WORD bitsPerSample;
17282 DWORD sampleRate;
17283
17284 MA_ASSERT(pContext != NULL);
17285 MA_ASSERT(pDirectSoundCapture != NULL);
17286
17287 if (pChannels) {
17288 *pChannels = 0;
17289 }
17290 if (pBitsPerSample) {
17291 *pBitsPerSample = 0;
17292 }
17293 if (pSampleRate) {
17294 *pSampleRate = 0;
17295 }
17296
17297 MA_ZERO_OBJECT(&caps);
17298 caps.dwSize = sizeof(caps);
17299 hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps);
17300 if (FAILED(hr)) {
17301 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device.", ma_result_from_HRESULT(hr));
17302 }
17303
17304 if (pChannels) {
17305 *pChannels = (WORD)caps.dwChannels;
17306 }
17307
17308 /* 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. */
17309 bitsPerSample = 16;
17310 sampleRate = 48000;
17311
17312 if (caps.dwChannels == 1) {
17313 if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) {
17314 sampleRate = 48000;
17315 } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) {
17316 sampleRate = 44100;
17317 } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) {
17318 sampleRate = 22050;
17319 } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) {
17320 sampleRate = 11025;
17321 } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) {
17322 sampleRate = 96000;
17323 } else {
17324 bitsPerSample = 8;
17325 if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) {
17326 sampleRate = 48000;
17327 } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) {
17328 sampleRate = 44100;
17329 } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) {
17330 sampleRate = 22050;
17331 } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) {
17332 sampleRate = 11025;
17333 } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) {
17334 sampleRate = 96000;
17335 } else {
17336 bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
17337 }
17338 }
17339 } else if (caps.dwChannels == 2) {
17340 if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) {
17341 sampleRate = 48000;
17342 } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) {
17343 sampleRate = 44100;
17344 } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) {
17345 sampleRate = 22050;
17346 } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) {
17347 sampleRate = 11025;
17348 } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) {
17349 sampleRate = 96000;
17350 } else {
17351 bitsPerSample = 8;
17352 if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) {
17353 sampleRate = 48000;
17354 } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) {
17355 sampleRate = 44100;
17356 } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) {
17357 sampleRate = 22050;
17358 } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) {
17359 sampleRate = 11025;
17360 } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) {
17361 sampleRate = 96000;
17362 } else {
17363 bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
17364 }
17365 }
17366 }
17367
17368 if (pBitsPerSample) {
17369 *pBitsPerSample = bitsPerSample;
17370 }
17371 if (pSampleRate) {
17372 *pSampleRate = sampleRate;
17373 }
17374
17375 return MA_SUCCESS;
17376}
17377
17378
17379typedef struct
17380{
17381 ma_context* pContext;
17382 ma_device_type deviceType;
17383 ma_enum_devices_callback_proc callback;
17384 void* pUserData;
17385 ma_bool32 terminated;
17386} ma_context_enumerate_devices_callback_data__dsound;
17387
17388static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
17389{
17390 ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext;
17391 ma_device_info deviceInfo;
17392
17393 (void)lpcstrModule;
17394
17395 MA_ZERO_OBJECT(&deviceInfo);
17396
17397 /* ID. */
17398 if (lpGuid != NULL) {
17399 MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16);
17400 } else {
17401 MA_ZERO_MEMORY(deviceInfo.id.dsound, 16);
17402 deviceInfo.isDefault = MA_TRUE;
17403 }
17404
17405 /* Name / Description */
17406 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1);
17407
17408
17409 /* Call the callback function, but make sure we stop enumerating if the callee requested so. */
17410 MA_ASSERT(pData != NULL);
17411 pData->terminated = !pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData);
17412 if (pData->terminated) {
17413 return FALSE; /* Stop enumeration. */
17414 } else {
17415 return TRUE; /* Continue enumeration. */
17416 }
17417}
17418
17419static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
17420{
17421 ma_context_enumerate_devices_callback_data__dsound data;
17422
17423 MA_ASSERT(pContext != NULL);
17424 MA_ASSERT(callback != NULL);
17425
17426 data.pContext = pContext;
17427 data.callback = callback;
17428 data.pUserData = pUserData;
17429 data.terminated = MA_FALSE;
17430
17431 /* Playback. */
17432 if (!data.terminated) {
17433 data.deviceType = ma_device_type_playback;
17434 ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
17435 }
17436
17437 /* Capture. */
17438 if (!data.terminated) {
17439 data.deviceType = ma_device_type_capture;
17440 ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
17441 }
17442
17443 return MA_SUCCESS;
17444}
17445
17446
17447typedef struct
17448{
17449 const ma_device_id* pDeviceID;
17450 ma_device_info* pDeviceInfo;
17451 ma_bool32 found;
17452} ma_context_get_device_info_callback_data__dsound;
17453
17454static BOOL CALLBACK ma_context_get_device_info_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
17455{
17456 ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext;
17457 MA_ASSERT(pData != NULL);
17458
17459 if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) {
17460 /* Default device. */
17461 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
17462 pData->pDeviceInfo->isDefault = MA_TRUE;
17463 pData->found = MA_TRUE;
17464 return FALSE; /* Stop enumeration. */
17465 } else {
17466 /* Not the default device. */
17467 if (lpGuid != NULL && pData->pDeviceID != NULL) {
17468 if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) {
17469 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
17470 pData->found = MA_TRUE;
17471 return FALSE; /* Stop enumeration. */
17472 }
17473 }
17474 }
17475
17476 (void)lpcstrModule;
17477 return TRUE;
17478}
17479
17480static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
17481{
17482 ma_result result;
17483 HRESULT hr;
17484
17485 if (pDeviceID != NULL) {
17486 ma_context_get_device_info_callback_data__dsound data;
17487
17488 /* ID. */
17489 MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16);
17490
17491 /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */
17492 data.pDeviceID = pDeviceID;
17493 data.pDeviceInfo = pDeviceInfo;
17494 data.found = MA_FALSE;
17495 if (deviceType == ma_device_type_playback) {
17496 ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
17497 } else {
17498 ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
17499 }
17500
17501 if (!data.found) {
17502 return MA_NO_DEVICE;
17503 }
17504 } else {
17505 /* 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. */
17506
17507 /* ID */
17508 MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16);
17509
17510 /* Name / Description */
17511 if (deviceType == ma_device_type_playback) {
17512 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
17513 } else {
17514 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
17515 }
17516
17517 pDeviceInfo->isDefault = MA_TRUE;
17518 }
17519
17520 /* Retrieving detailed information is slightly different depending on the device type. */
17521 if (deviceType == ma_device_type_playback) {
17522 /* Playback. */
17523 ma_IDirectSound* pDirectSound;
17524 MA_DSCAPS caps;
17525 WORD channels;
17526
17527 result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound);
17528 if (result != MA_SUCCESS) {
17529 return result;
17530 }
17531
17532 MA_ZERO_OBJECT(&caps);
17533 caps.dwSize = sizeof(caps);
17534 hr = ma_IDirectSound_GetCaps(pDirectSound, &caps);
17535 if (FAILED(hr)) {
17536 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", ma_result_from_HRESULT(hr));
17537 }
17538
17539
17540 /* Channels. Only a single channel count is reported for DirectSound. */
17541 if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
17542 /* It supports at least stereo, but could support more. */
17543 DWORD speakerConfig;
17544
17545 channels = 2;
17546
17547 /* Look at the speaker configuration to get a better idea on the channel count. */
17548 hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig);
17549 if (SUCCEEDED(hr)) {
17550 ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL);
17551 }
17552 } else {
17553 /* It does not support stereo, which means we are stuck with mono. */
17554 channels = 1;
17555 }
17556
17557
17558 /*
17559 In DirectSound, our native formats are centered around sample rates. All formats are supported, and we're only reporting a single channel
17560 count. However, DirectSound can report a range of supported sample rates. We're only going to include standard rates known by miniaudio
17561 in order to keep the size of this within reason.
17562 */
17563 if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
17564 /* Multiple sample rates are supported. We'll report in order of our preferred sample rates. */
17565 size_t iStandardSampleRate;
17566 for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {
17567 ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];
17568 if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) {
17569 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown;
17570 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
17571 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
17572 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
17573 pDeviceInfo->nativeDataFormatCount += 1;
17574 }
17575 }
17576 } else {
17577 /* Only a single sample rate is supported. */
17578 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown;
17579 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
17580 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate;
17581 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
17582 pDeviceInfo->nativeDataFormatCount += 1;
17583 }
17584
17585 ma_IDirectSound_Release(pDirectSound);
17586 } else {
17587 /*
17588 Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture
17589 devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just
17590 reporting the best format.
17591 */
17592 ma_IDirectSoundCapture* pDirectSoundCapture;
17593 WORD channels;
17594 WORD bitsPerSample;
17595 DWORD sampleRate;
17596
17597 result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture);
17598 if (result != MA_SUCCESS) {
17599 return result;
17600 }
17601
17602 result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate);
17603 if (result != MA_SUCCESS) {
17604 ma_IDirectSoundCapture_Release(pDirectSoundCapture);
17605 return result;
17606 }
17607
17608 ma_IDirectSoundCapture_Release(pDirectSoundCapture);
17609
17610 /* The format is always an integer format and is based on the bits per sample. */
17611 if (bitsPerSample == 8) {
17612 pDeviceInfo->formats[0] = ma_format_u8;
17613 } else if (bitsPerSample == 16) {
17614 pDeviceInfo->formats[0] = ma_format_s16;
17615 } else if (bitsPerSample == 24) {
17616 pDeviceInfo->formats[0] = ma_format_s24;
17617 } else if (bitsPerSample == 32) {
17618 pDeviceInfo->formats[0] = ma_format_s32;
17619 } else {
17620 return MA_FORMAT_NOT_SUPPORTED;
17621 }
17622
17623 pDeviceInfo->nativeDataFormats[0].channels = channels;
17624 pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate;
17625 pDeviceInfo->nativeDataFormats[0].flags = 0;
17626 pDeviceInfo->nativeDataFormatCount = 1;
17627 }
17628
17629 return MA_SUCCESS;
17630}
17631
17632
17633
17634static ma_result ma_device_uninit__dsound(ma_device* pDevice)
17635{
17636 MA_ASSERT(pDevice != NULL);
17637
17638 if (pDevice->dsound.pCaptureBuffer != NULL) {
17639 ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
17640 }
17641 if (pDevice->dsound.pCapture != NULL) {
17642 ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture);
17643 }
17644
17645 if (pDevice->dsound.pPlaybackBuffer != NULL) {
17646 ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
17647 }
17648 if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) {
17649 ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer);
17650 }
17651 if (pDevice->dsound.pPlayback != NULL) {
17652 ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback);
17653 }
17654
17655 return MA_SUCCESS;
17656}
17657
17658static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, WAVEFORMATEXTENSIBLE* pWF)
17659{
17660 GUID subformat;
17661
17662 if (format == ma_format_unknown) {
17663 format = MA_DEFAULT_FORMAT;
17664 }
17665
17666 if (channels == 0) {
17667 channels = MA_DEFAULT_CHANNELS;
17668 }
17669
17670 if (sampleRate == 0) {
17671 sampleRate = MA_DEFAULT_SAMPLE_RATE;
17672 }
17673
17674 switch (format)
17675 {
17676 case ma_format_u8:
17677 case ma_format_s16:
17678 case ma_format_s24:
17679 /*case ma_format_s24_32:*/
17680 case ma_format_s32:
17681 {
17682 subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
17683 } break;
17684
17685 case ma_format_f32:
17686 {
17687 subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
17688 } break;
17689
17690 default:
17691 return MA_FORMAT_NOT_SUPPORTED;
17692 }
17693
17694 MA_ZERO_OBJECT(pWF);
17695 pWF->Format.cbSize = sizeof(*pWF);
17696 pWF->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
17697 pWF->Format.nChannels = (WORD)channels;
17698 pWF->Format.nSamplesPerSec = (DWORD)sampleRate;
17699 pWF->Format.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8);
17700 pWF->Format.nBlockAlign = (WORD)(pWF->Format.nChannels * pWF->Format.wBitsPerSample / 8);
17701 pWF->Format.nAvgBytesPerSec = pWF->Format.nBlockAlign * pWF->Format.nSamplesPerSec;
17702 pWF->Samples.wValidBitsPerSample = pWF->Format.wBitsPerSample;
17703 pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels);
17704 pWF->SubFormat = subformat;
17705
17706 return MA_SUCCESS;
17707}
17708
17709static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
17710{
17711 /* DirectSound has a minimum period size of 20ms. */
17712 ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(20, nativeSampleRate);
17713 ma_uint32 periodSizeInFrames;
17714
17715 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);
17716 if (periodSizeInFrames < minPeriodSizeInFrames) {
17717 periodSizeInFrames = minPeriodSizeInFrames;
17718 }
17719
17720 return periodSizeInFrames;
17721}
17722
17723static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
17724{
17725 ma_result result;
17726 HRESULT hr;
17727
17728 MA_ASSERT(pDevice != NULL);
17729
17730 MA_ZERO_OBJECT(&pDevice->dsound);
17731
17732 if (pConfig->deviceType == ma_device_type_loopback) {
17733 return MA_DEVICE_TYPE_NOT_SUPPORTED;
17734 }
17735
17736 /*
17737 Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize
17738 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
17739 full-duplex mode.
17740 */
17741 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
17742 WAVEFORMATEXTENSIBLE wf;
17743 MA_DSCBUFFERDESC descDS;
17744 ma_uint32 periodSizeInFrames;
17745 ma_uint32 periodCount;
17746 char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
17747 WAVEFORMATEXTENSIBLE* pActualFormat;
17748
17749 result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf);
17750 if (result != MA_SUCCESS) {
17751 return result;
17752 }
17753
17754 result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture);
17755 if (result != MA_SUCCESS) {
17756 ma_device_uninit__dsound(pDevice);
17757 return result;
17758 }
17759
17760 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);
17761 if (result != MA_SUCCESS) {
17762 ma_device_uninit__dsound(pDevice);
17763 return result;
17764 }
17765
17766 wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8);
17767 wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
17768 wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample;
17769 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
17770
17771 /* The size of the buffer must be a clean multiple of the period count. */
17772 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.Format.nSamplesPerSec, pConfig->performanceProfile);
17773 periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS;
17774
17775 MA_ZERO_OBJECT(&descDS);
17776 descDS.dwSize = sizeof(descDS);
17777 descDS.dwFlags = 0;
17778 descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.Format.nBlockAlign;
17779 descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
17780 hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);
17781 if (FAILED(hr)) {
17782 ma_device_uninit__dsound(pDevice);
17783 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", ma_result_from_HRESULT(hr));
17784 }
17785
17786 /* Get the _actual_ properties of the buffer. */
17787 pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
17788 hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);
17789 if (FAILED(hr)) {
17790 ma_device_uninit__dsound(pDevice);
17791 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer.", ma_result_from_HRESULT(hr));
17792 }
17793
17794 /* We can now start setting the output data formats. */
17795 pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat);
17796 pDescriptorCapture->channels = pActualFormat->Format.nChannels;
17797 pDescriptorCapture->sampleRate = pActualFormat->Format.nSamplesPerSec;
17798
17799 /* Get the native channel map based on the channel mask. */
17800 if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
17801 ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
17802 } else {
17803 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
17804 }
17805
17806 /*
17807 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
17808 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.
17809 */
17810 if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) {
17811 descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount;
17812 ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
17813
17814 hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);
17815 if (FAILED(hr)) {
17816 ma_device_uninit__dsound(pDevice);
17817 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", ma_result_from_HRESULT(hr));
17818 }
17819 }
17820
17821 /* DirectSound should give us a buffer exactly the size we asked for. */
17822 pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
17823 pDescriptorCapture->periodCount = periodCount;
17824 }
17825
17826 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
17827 WAVEFORMATEXTENSIBLE wf;
17828 MA_DSBUFFERDESC descDSPrimary;
17829 MA_DSCAPS caps;
17830 char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
17831 WAVEFORMATEXTENSIBLE* pActualFormat;
17832 ma_uint32 periodSizeInFrames;
17833 ma_uint32 periodCount;
17834 MA_DSBUFFERDESC descDS;
17835
17836 result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf);
17837 if (result != MA_SUCCESS) {
17838 return result;
17839 }
17840
17841 result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback);
17842 if (result != MA_SUCCESS) {
17843 ma_device_uninit__dsound(pDevice);
17844 return result;
17845 }
17846
17847 MA_ZERO_OBJECT(&descDSPrimary);
17848 descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC);
17849 descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME;
17850 hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL);
17851 if (FAILED(hr)) {
17852 ma_device_uninit__dsound(pDevice);
17853 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.", ma_result_from_HRESULT(hr));
17854 }
17855
17856
17857 /* We may want to make some adjustments to the format if we are using defaults. */
17858 MA_ZERO_OBJECT(&caps);
17859 caps.dwSize = sizeof(caps);
17860 hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps);
17861 if (FAILED(hr)) {
17862 ma_device_uninit__dsound(pDevice);
17863 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", ma_result_from_HRESULT(hr));
17864 }
17865
17866 if (pDescriptorPlayback->channels == 0) {
17867 if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
17868 DWORD speakerConfig;
17869
17870 /* It supports at least stereo, but could support more. */
17871 wf.Format.nChannels = 2;
17872
17873 /* Look at the speaker configuration to get a better idea on the channel count. */
17874 if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) {
17875 ma_get_channels_from_speaker_config__dsound(speakerConfig, &wf.Format.nChannels, &wf.dwChannelMask);
17876 }
17877 } else {
17878 /* It does not support stereo, which means we are stuck with mono. */
17879 wf.Format.nChannels = 1;
17880 }
17881 }
17882
17883 if (pDescriptorPlayback->sampleRate == 0) {
17884 /* We base the sample rate on the values returned by GetCaps(). */
17885 if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
17886 wf.Format.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate);
17887 } else {
17888 wf.Format.nSamplesPerSec = caps.dwMaxSecondarySampleRate;
17889 }
17890 }
17891
17892 wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8);
17893 wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
17894
17895 /*
17896 From MSDN:
17897
17898 The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest
17899 supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer
17900 and compare the result with the format that was requested with the SetFormat method.
17901 */
17902 hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)&wf);
17903 if (FAILED(hr)) {
17904 ma_device_uninit__dsound(pDevice);
17905 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer.", ma_result_from_HRESULT(hr));
17906 }
17907
17908 /* Get the _actual_ properties of the buffer. */
17909 pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
17910 hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);
17911 if (FAILED(hr)) {
17912 ma_device_uninit__dsound(pDevice);
17913 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer.", ma_result_from_HRESULT(hr));
17914 }
17915
17916 /* We now have enough information to start setting some output properties. */
17917 pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat);
17918 pDescriptorPlayback->channels = pActualFormat->Format.nChannels;
17919 pDescriptorPlayback->sampleRate = pActualFormat->Format.nSamplesPerSec;
17920
17921 /* Get the internal channel map based on the channel mask. */
17922 if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
17923 ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
17924 } else {
17925 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
17926 }
17927
17928 /* The size of the buffer must be a clean multiple of the period count. */
17929 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
17930 periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS;
17931
17932 /*
17933 Meaning of dwFlags (from MSDN):
17934
17935 DSBCAPS_CTRLPOSITIONNOTIFY
17936 The buffer has position notification capability.
17937
17938 DSBCAPS_GLOBALFOCUS
17939 With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to
17940 another application, even if the new application uses DirectSound.
17941
17942 DSBCAPS_GETCURRENTPOSITION2
17943 In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated
17944 sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the
17945 application can get a more accurate play cursor.
17946 */
17947 MA_ZERO_OBJECT(&descDS);
17948 descDS.dwSize = sizeof(descDS);
17949 descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2;
17950 descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels);
17951 descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
17952 hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL);
17953 if (FAILED(hr)) {
17954 ma_device_uninit__dsound(pDevice);
17955 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.", ma_result_from_HRESULT(hr));
17956 }
17957
17958 /* DirectSound should give us a buffer exactly the size we asked for. */
17959 pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
17960 pDescriptorPlayback->periodCount = periodCount;
17961 }
17962
17963 return MA_SUCCESS;
17964}
17965
17966
17967static ma_result ma_device_data_loop__dsound(ma_device* pDevice)
17968{
17969 ma_result result = MA_SUCCESS;
17970 ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
17971 ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
17972 HRESULT hr;
17973 DWORD lockOffsetInBytesCapture;
17974 DWORD lockSizeInBytesCapture;
17975 DWORD mappedSizeInBytesCapture;
17976 DWORD mappedDeviceFramesProcessedCapture;
17977 void* pMappedDeviceBufferCapture;
17978 DWORD lockOffsetInBytesPlayback;
17979 DWORD lockSizeInBytesPlayback;
17980 DWORD mappedSizeInBytesPlayback;
17981 void* pMappedDeviceBufferPlayback;
17982 DWORD prevReadCursorInBytesCapture = 0;
17983 DWORD prevPlayCursorInBytesPlayback = 0;
17984 ma_bool32 physicalPlayCursorLoopFlagPlayback = 0;
17985 DWORD virtualWriteCursorInBytesPlayback = 0;
17986 ma_bool32 virtualWriteCursorLoopFlagPlayback = 0;
17987 ma_bool32 isPlaybackDeviceStarted = MA_FALSE;
17988 ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */
17989 ma_uint32 waitTimeInMilliseconds = 1;
17990
17991 MA_ASSERT(pDevice != NULL);
17992
17993 /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */
17994 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
17995 if (FAILED(ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING))) {
17996 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
17997 }
17998 }
17999
18000 while (ma_device_get_state(pDevice) == MA_STATE_STARTED) {
18001 switch (pDevice->type)
18002 {
18003 case ma_device_type_duplex:
18004 {
18005 DWORD physicalCaptureCursorInBytes;
18006 DWORD physicalReadCursorInBytes;
18007 hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
18008 if (FAILED(hr)) {
18009 return ma_result_from_HRESULT(hr);
18010 }
18011
18012 /* If nothing is available we just sleep for a bit and return from this iteration. */
18013 if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) {
18014 ma_sleep(waitTimeInMilliseconds);
18015 continue; /* Nothing is available in the capture buffer. */
18016 }
18017
18018 /*
18019 The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure
18020 we don't return until every frame has been copied over.
18021 */
18022 if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
18023 /* The capture position has not looped. This is the simple case. */
18024 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
18025 lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
18026 } else {
18027 /*
18028 The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
18029 do it again from the start.
18030 */
18031 if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
18032 /* Lock up to the end of the buffer. */
18033 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
18034 lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
18035 } else {
18036 /* Lock starting from the start of the buffer. */
18037 lockOffsetInBytesCapture = 0;
18038 lockSizeInBytesCapture = physicalReadCursorInBytes;
18039 }
18040 }
18041
18042 if (lockSizeInBytesCapture == 0) {
18043 ma_sleep(waitTimeInMilliseconds);
18044 continue; /* Nothing is available in the capture buffer. */
18045 }
18046
18047 hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
18048 if (FAILED(hr)) {
18049 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
18050 }
18051
18052
18053 /* 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. */
18054 mappedDeviceFramesProcessedCapture = 0;
18055
18056 for (;;) { /* Keep writing to the playback device. */
18057 ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18058 ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
18059 ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18060 ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
18061 ma_uint32 outputFramesInClientFormatCount;
18062 ma_uint32 outputFramesInClientFormatConsumed = 0;
18063 ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap);
18064 ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture;
18065 void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture);
18066
18067 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess);
18068 if (result != MA_SUCCESS) {
18069 break;
18070 }
18071
18072 outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess;
18073 mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess;
18074
18075 ma_device__on_data(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess);
18076
18077 /* 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. */
18078 for (;;) {
18079 ma_uint32 framesWrittenThisIteration;
18080 DWORD physicalPlayCursorInBytes;
18081 DWORD physicalWriteCursorInBytes;
18082 DWORD availableBytesPlayback;
18083 DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */
18084
18085 /* We need the physical play and write cursors. */
18086 if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
18087 break;
18088 }
18089
18090 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
18091 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
18092 }
18093 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
18094
18095 /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
18096 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
18097 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
18098 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
18099 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
18100 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
18101 } else {
18102 /* This is an error. */
18103 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Duplex/Playback) WARNING: Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
18104 availableBytesPlayback = 0;
18105 }
18106 } else {
18107 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
18108 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
18109 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
18110 } else {
18111 /* This is an error. */
18112 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Duplex/Playback) WARNING: Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
18113 availableBytesPlayback = 0;
18114 }
18115 }
18116
18117 /* If there's no room available for writing we need to wait for more. */
18118 if (availableBytesPlayback == 0) {
18119 /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
18120 if (!isPlaybackDeviceStarted) {
18121 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
18122 if (FAILED(hr)) {
18123 ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
18124 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr));
18125 }
18126 isPlaybackDeviceStarted = MA_TRUE;
18127 } else {
18128 ma_sleep(waitTimeInMilliseconds);
18129 continue;
18130 }
18131 }
18132
18133
18134 /* 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. */
18135 lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
18136 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
18137 /* Same loop iteration. Go up to the end of the buffer. */
18138 lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
18139 } else {
18140 /* Different loop iterations. Go up to the physical play cursor. */
18141 lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
18142 }
18143
18144 hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
18145 if (FAILED(hr)) {
18146 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
18147 break;
18148 }
18149
18150 /*
18151 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
18152 endless glitching due to it constantly running out of data.
18153 */
18154 if (isPlaybackDeviceStarted) {
18155 DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback;
18156 if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) {
18157 silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback;
18158 if (silentPaddingInBytes > lockSizeInBytesPlayback) {
18159 silentPaddingInBytes = lockSizeInBytesPlayback;
18160 }
18161
18162 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%ld, silentPaddingInBytes=%ld\n", availableBytesPlayback, silentPaddingInBytes);
18163 }
18164 }
18165
18166 /* At this point we have a buffer for output. */
18167 if (silentPaddingInBytes > 0) {
18168 MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes);
18169 framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback;
18170 } else {
18171 ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed);
18172 ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback;
18173 void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback);
18174 void* pConvertedFramesOut = pMappedDeviceBufferPlayback;
18175
18176 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut);
18177 if (result != MA_SUCCESS) {
18178 break;
18179 }
18180
18181 outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut;
18182 framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut;
18183 }
18184
18185
18186 hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0);
18187 if (FAILED(hr)) {
18188 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.", ma_result_from_HRESULT(hr));
18189 break;
18190 }
18191
18192 virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback;
18193 if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) {
18194 virtualWriteCursorInBytesPlayback = 0;
18195 virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
18196 }
18197
18198 /*
18199 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
18200 a bit of a buffer to prevent the playback buffer from getting starved.
18201 */
18202 framesWrittenToPlaybackDevice += framesWrittenThisIteration;
18203 if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) {
18204 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
18205 if (FAILED(hr)) {
18206 ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
18207 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr));
18208 }
18209 isPlaybackDeviceStarted = MA_TRUE;
18210 }
18211
18212 if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) {
18213 break; /* We're finished with the output data.*/
18214 }
18215 }
18216
18217 if (clientCapturedFramesToProcess == 0) {
18218 break; /* We just consumed every input sample. */
18219 }
18220 }
18221
18222
18223 /* At this point we're done with the mapped portion of the capture buffer. */
18224 hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
18225 if (FAILED(hr)) {
18226 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.", ma_result_from_HRESULT(hr));
18227 }
18228 prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture);
18229 } break;
18230
18231
18232
18233 case ma_device_type_capture:
18234 {
18235 DWORD physicalCaptureCursorInBytes;
18236 DWORD physicalReadCursorInBytes;
18237 hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
18238 if (FAILED(hr)) {
18239 return MA_ERROR;
18240 }
18241
18242 /* If the previous capture position is the same as the current position we need to wait a bit longer. */
18243 if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) {
18244 ma_sleep(waitTimeInMilliseconds);
18245 continue;
18246 }
18247
18248 /* Getting here means we have capture data available. */
18249 if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
18250 /* The capture position has not looped. This is the simple case. */
18251 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
18252 lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
18253 } else {
18254 /*
18255 The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
18256 do it again from the start.
18257 */
18258 if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
18259 /* Lock up to the end of the buffer. */
18260 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
18261 lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
18262 } else {
18263 /* Lock starting from the start of the buffer. */
18264 lockOffsetInBytesCapture = 0;
18265 lockSizeInBytesCapture = physicalReadCursorInBytes;
18266 }
18267 }
18268
18269 if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) {
18270 ma_sleep(waitTimeInMilliseconds);
18271 continue; /* Nothing is available in the capture buffer. */
18272 }
18273
18274 hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
18275 if (FAILED(hr)) {
18276 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
18277 }
18278
18279 #ifdef MA_DEBUG_OUTPUT
18280 if (lockSizeInBytesCapture != mappedSizeInBytesCapture) {
18281 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture);
18282 }
18283 #endif
18284
18285 ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture);
18286
18287 hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
18288 if (FAILED(hr)) {
18289 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.", ma_result_from_HRESULT(hr));
18290 }
18291 prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture;
18292
18293 if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) {
18294 prevReadCursorInBytesCapture = 0;
18295 }
18296 } break;
18297
18298
18299
18300 case ma_device_type_playback:
18301 {
18302 DWORD availableBytesPlayback;
18303 DWORD physicalPlayCursorInBytes;
18304 DWORD physicalWriteCursorInBytes;
18305 hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
18306 if (FAILED(hr)) {
18307 break;
18308 }
18309
18310 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
18311 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
18312 }
18313 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
18314
18315 /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
18316 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
18317 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
18318 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
18319 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
18320 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
18321 } else {
18322 /* This is an error. */
18323 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Playback) WARNING: Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
18324 availableBytesPlayback = 0;
18325 }
18326 } else {
18327 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
18328 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
18329 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
18330 } else {
18331 /* This is an error. */
18332 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Playback) WARNING: Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
18333 availableBytesPlayback = 0;
18334 }
18335 }
18336
18337 /* If there's no room available for writing we need to wait for more. */
18338 if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) {
18339 /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
18340 if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) {
18341 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
18342 if (FAILED(hr)) {
18343 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr));
18344 }
18345 isPlaybackDeviceStarted = MA_TRUE;
18346 } else {
18347 ma_sleep(waitTimeInMilliseconds);
18348 continue;
18349 }
18350 }
18351
18352 /* 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. */
18353 lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
18354 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
18355 /* Same loop iteration. Go up to the end of the buffer. */
18356 lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
18357 } else {
18358 /* Different loop iterations. Go up to the physical play cursor. */
18359 lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
18360 }
18361
18362 hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
18363 if (FAILED(hr)) {
18364 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
18365 break;
18366 }
18367
18368 /* At this point we have a buffer for output. */
18369 ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback);
18370
18371 hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0);
18372 if (FAILED(hr)) {
18373 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.", ma_result_from_HRESULT(hr));
18374 break;
18375 }
18376
18377 virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback;
18378 if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) {
18379 virtualWriteCursorInBytesPlayback = 0;
18380 virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
18381 }
18382
18383 /*
18384 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
18385 a bit of a buffer to prevent the playback buffer from getting starved.
18386 */
18387 framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback;
18388 if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) {
18389 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
18390 if (FAILED(hr)) {
18391 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr));
18392 }
18393 isPlaybackDeviceStarted = MA_TRUE;
18394 }
18395 } break;
18396
18397
18398 default: return MA_INVALID_ARGS; /* Invalid device type. */
18399 }
18400
18401 if (result != MA_SUCCESS) {
18402 return result;
18403 }
18404 }
18405
18406 /* Getting here means the device is being stopped. */
18407 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
18408 hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
18409 if (FAILED(hr)) {
18410 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.", ma_result_from_HRESULT(hr));
18411 }
18412 }
18413
18414 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
18415 /* 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. */
18416 if (isPlaybackDeviceStarted) {
18417 for (;;) {
18418 DWORD availableBytesPlayback = 0;
18419 DWORD physicalPlayCursorInBytes;
18420 DWORD physicalWriteCursorInBytes;
18421 hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
18422 if (FAILED(hr)) {
18423 break;
18424 }
18425
18426 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
18427 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
18428 }
18429 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
18430
18431 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
18432 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
18433 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
18434 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
18435 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
18436 } else {
18437 break;
18438 }
18439 } else {
18440 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
18441 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
18442 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
18443 } else {
18444 break;
18445 }
18446 }
18447
18448 if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) {
18449 break;
18450 }
18451
18452 ma_sleep(waitTimeInMilliseconds);
18453 }
18454 }
18455
18456 hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
18457 if (FAILED(hr)) {
18458 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed.", ma_result_from_HRESULT(hr));
18459 }
18460
18461 ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0);
18462 }
18463
18464 return MA_SUCCESS;
18465}
18466
18467static ma_result ma_context_uninit__dsound(ma_context* pContext)
18468{
18469 MA_ASSERT(pContext != NULL);
18470 MA_ASSERT(pContext->backend == ma_backend_dsound);
18471
18472 ma_dlclose(pContext, pContext->dsound.hDSoundDLL);
18473
18474 return MA_SUCCESS;
18475}
18476
18477static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
18478{
18479 MA_ASSERT(pContext != NULL);
18480
18481 (void)pConfig;
18482
18483 pContext->dsound.hDSoundDLL = ma_dlopen(pContext, "dsound.dll");
18484 if (pContext->dsound.hDSoundDLL == NULL) {
18485 return MA_API_NOT_FOUND;
18486 }
18487
18488 pContext->dsound.DirectSoundCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCreate");
18489 pContext->dsound.DirectSoundEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA");
18490 pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate");
18491 pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA");
18492
18493 pCallbacks->onContextInit = ma_context_init__dsound;
18494 pCallbacks->onContextUninit = ma_context_uninit__dsound;
18495 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound;
18496 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound;
18497 pCallbacks->onDeviceInit = ma_device_init__dsound;
18498 pCallbacks->onDeviceUninit = ma_device_uninit__dsound;
18499 pCallbacks->onDeviceStart = NULL; /* Not used. Started in onDeviceDataLoop. */
18500 pCallbacks->onDeviceStop = NULL; /* Not used. Stopped in onDeviceDataLoop. */
18501 pCallbacks->onDeviceRead = NULL; /* Not used. Data is read directly in onDeviceDataLoop. */
18502 pCallbacks->onDeviceWrite = NULL; /* Not used. Data is written directly in onDeviceDataLoop. */
18503 pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound;
18504
18505 return MA_SUCCESS;
18506}
18507#endif
18508
18509
18510
18511/******************************************************************************
18512
18513WinMM Backend
18514
18515******************************************************************************/
18516#ifdef MA_HAS_WINMM
18517
18518/*
18519Some older compilers don't have WAVEOUTCAPS2A and WAVEINCAPS2A, so we'll need to write this ourselves. These structures
18520are exactly the same as the older ones but they have a few GUIDs for manufacturer/product/name identification. I'm keeping
18521the names the same as the Win32 library for consistency, but namespaced to avoid naming conflicts with the Win32 version.
18522*/
18523typedef struct
18524{
18525 WORD wMid;
18526 WORD wPid;
18527 MMVERSION vDriverVersion;
18528 CHAR szPname[MAXPNAMELEN];
18529 DWORD dwFormats;
18530 WORD wChannels;
18531 WORD wReserved1;
18532 DWORD dwSupport;
18533 GUID ManufacturerGuid;
18534 GUID ProductGuid;
18535 GUID NameGuid;
18536} MA_WAVEOUTCAPS2A;
18537typedef struct
18538{
18539 WORD wMid;
18540 WORD wPid;
18541 MMVERSION vDriverVersion;
18542 CHAR szPname[MAXPNAMELEN];
18543 DWORD dwFormats;
18544 WORD wChannels;
18545 WORD wReserved1;
18546 GUID ManufacturerGuid;
18547 GUID ProductGuid;
18548 GUID NameGuid;
18549} MA_WAVEINCAPS2A;
18550
18551typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void);
18552typedef MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc);
18553typedef MMRESULT (WINAPI * MA_PFN_waveOutOpen)(LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
18554typedef MMRESULT (WINAPI * MA_PFN_waveOutClose)(HWAVEOUT hwo);
18555typedef MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
18556typedef MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
18557typedef MMRESULT (WINAPI * MA_PFN_waveOutWrite)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
18558typedef MMRESULT (WINAPI * MA_PFN_waveOutReset)(HWAVEOUT hwo);
18559typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void);
18560typedef MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic);
18561typedef MMRESULT (WINAPI * MA_PFN_waveInOpen)(LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
18562typedef MMRESULT (WINAPI * MA_PFN_waveInClose)(HWAVEIN hwi);
18563typedef MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
18564typedef MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
18565typedef MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
18566typedef MMRESULT (WINAPI * MA_PFN_waveInStart)(HWAVEIN hwi);
18567typedef MMRESULT (WINAPI * MA_PFN_waveInReset)(HWAVEIN hwi);
18568
18569static ma_result ma_result_from_MMRESULT(MMRESULT resultMM)
18570{
18571 switch (resultMM) {
18572 case MMSYSERR_NOERROR: return MA_SUCCESS;
18573 case MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS;
18574 case MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS;
18575 case MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY;
18576 case MMSYSERR_INVALFLAG: return MA_INVALID_ARGS;
18577 case MMSYSERR_INVALPARAM: return MA_INVALID_ARGS;
18578 case MMSYSERR_HANDLEBUSY: return MA_BUSY;
18579 case MMSYSERR_ERROR: return MA_ERROR;
18580 default: return MA_ERROR;
18581 }
18582}
18583
18584static char* ma_find_last_character(char* str, char ch)
18585{
18586 char* last;
18587
18588 if (str == NULL) {
18589 return NULL;
18590 }
18591
18592 last = NULL;
18593 while (*str != '\0') {
18594 if (*str == ch) {
18595 last = str;
18596 }
18597
18598 str += 1;
18599 }
18600
18601 return last;
18602}
18603
18604static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels)
18605{
18606 return periodSizeInFrames * ma_get_bytes_per_frame(format, channels);
18607}
18608
18609
18610/*
18611Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so
18612we can do things generically and typesafely. Names are being kept the same for consistency.
18613*/
18614typedef struct
18615{
18616 CHAR szPname[MAXPNAMELEN];
18617 DWORD dwFormats;
18618 WORD wChannels;
18619 GUID NameGuid;
18620} MA_WAVECAPSA;
18621
18622static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate)
18623{
18624 WORD bitsPerSample = 0;
18625 DWORD sampleRate = 0;
18626
18627 if (pBitsPerSample) {
18628 *pBitsPerSample = 0;
18629 }
18630 if (pSampleRate) {
18631 *pSampleRate = 0;
18632 }
18633
18634 if (channels == 1) {
18635 bitsPerSample = 16;
18636 if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
18637 sampleRate = 48000;
18638 } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
18639 sampleRate = 44100;
18640 } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
18641 sampleRate = 22050;
18642 } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
18643 sampleRate = 11025;
18644 } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
18645 sampleRate = 96000;
18646 } else {
18647 bitsPerSample = 8;
18648 if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
18649 sampleRate = 48000;
18650 } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
18651 sampleRate = 44100;
18652 } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
18653 sampleRate = 22050;
18654 } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
18655 sampleRate = 11025;
18656 } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
18657 sampleRate = 96000;
18658 } else {
18659 return MA_FORMAT_NOT_SUPPORTED;
18660 }
18661 }
18662 } else {
18663 bitsPerSample = 16;
18664 if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
18665 sampleRate = 48000;
18666 } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
18667 sampleRate = 44100;
18668 } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
18669 sampleRate = 22050;
18670 } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
18671 sampleRate = 11025;
18672 } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
18673 sampleRate = 96000;
18674 } else {
18675 bitsPerSample = 8;
18676 if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
18677 sampleRate = 48000;
18678 } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
18679 sampleRate = 44100;
18680 } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
18681 sampleRate = 22050;
18682 } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
18683 sampleRate = 11025;
18684 } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
18685 sampleRate = 96000;
18686 } else {
18687 return MA_FORMAT_NOT_SUPPORTED;
18688 }
18689 }
18690 }
18691
18692 if (pBitsPerSample) {
18693 *pBitsPerSample = bitsPerSample;
18694 }
18695 if (pSampleRate) {
18696 *pSampleRate = sampleRate;
18697 }
18698
18699 return MA_SUCCESS;
18700}
18701
18702static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, WAVEFORMATEX* pWF)
18703{
18704 ma_result result;
18705
18706 MA_ASSERT(pWF != NULL);
18707
18708 MA_ZERO_OBJECT(pWF);
18709 pWF->cbSize = sizeof(*pWF);
18710 pWF->wFormatTag = WAVE_FORMAT_PCM;
18711 pWF->nChannels = (WORD)channels;
18712 if (pWF->nChannels > 2) {
18713 pWF->nChannels = 2;
18714 }
18715
18716 result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec);
18717 if (result != MA_SUCCESS) {
18718 return result;
18719 }
18720
18721 pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8);
18722 pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec;
18723
18724 return MA_SUCCESS;
18725}
18726
18727static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo)
18728{
18729 WORD bitsPerSample;
18730 DWORD sampleRate;
18731 ma_result result;
18732
18733 MA_ASSERT(pContext != NULL);
18734 MA_ASSERT(pCaps != NULL);
18735 MA_ASSERT(pDeviceInfo != NULL);
18736
18737 /*
18738 Name / Description
18739
18740 Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking
18741 situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try
18742 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.
18743 */
18744
18745 /* Set the default to begin with. */
18746 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1);
18747
18748 /*
18749 Now try the registry. There's a few things to consider here:
18750 - The name GUID can be null, in which we case we just need to stick to the original 31 characters.
18751 - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters.
18752 - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The
18753 problem, however is that WASAPI and DirectSound use "<component> (<name>)" format (such as "Speakers (High Definition Audio)"),
18754 but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to
18755 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
18756 name, and then concatenate the name from the registry.
18757 */
18758 if (!ma_is_guid_null(&pCaps->NameGuid)) {
18759 wchar_t guidStrW[256];
18760 if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) {
18761 char guidStr[256];
18762 char keyStr[1024];
18763 HKEY hKey;
18764
18765 WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE);
18766
18767 ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\");
18768 ma_strcat_s(keyStr, sizeof(keyStr), guidStr);
18769
18770 if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
18771 BYTE nameFromReg[512];
18772 DWORD nameFromRegSize = sizeof(nameFromReg);
18773 result = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize);
18774 ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey);
18775
18776 if (result == ERROR_SUCCESS) {
18777 /* We have the value from the registry, so now we need to construct the name string. */
18778 char name[1024];
18779 if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) {
18780 char* nameBeg = ma_find_last_character(name, '(');
18781 if (nameBeg != NULL) {
18782 size_t leadingLen = (nameBeg - name);
18783 ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1);
18784
18785 /* The closing ")", if it can fit. */
18786 if (leadingLen + nameFromRegSize < sizeof(name)-1) {
18787 ma_strcat_s(name, sizeof(name), ")");
18788 }
18789
18790 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1);
18791 }
18792 }
18793 }
18794 }
18795 }
18796 }
18797
18798
18799 result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate);
18800 if (result != MA_SUCCESS) {
18801 return result;
18802 }
18803
18804 if (bitsPerSample == 8) {
18805 pDeviceInfo->nativeDataFormats[0].format = ma_format_u8;
18806 } else if (bitsPerSample == 16) {
18807 pDeviceInfo->nativeDataFormats[0].format = ma_format_s16;
18808 } else if (bitsPerSample == 24) {
18809 pDeviceInfo->nativeDataFormats[0].format = ma_format_s24;
18810 } else if (bitsPerSample == 32) {
18811 pDeviceInfo->nativeDataFormats[0].format = ma_format_s32;
18812 } else {
18813 return MA_FORMAT_NOT_SUPPORTED;
18814 }
18815 pDeviceInfo->nativeDataFormats[0].channels = pCaps->wChannels;
18816 pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate;
18817 pDeviceInfo->nativeDataFormats[0].flags = 0;
18818 pDeviceInfo->nativeDataFormatCount = 1;
18819
18820 return MA_SUCCESS;
18821}
18822
18823static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo)
18824{
18825 MA_WAVECAPSA caps;
18826
18827 MA_ASSERT(pContext != NULL);
18828 MA_ASSERT(pCaps != NULL);
18829 MA_ASSERT(pDeviceInfo != NULL);
18830
18831 MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
18832 caps.dwFormats = pCaps->dwFormats;
18833 caps.wChannels = pCaps->wChannels;
18834 caps.NameGuid = pCaps->NameGuid;
18835 return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
18836}
18837
18838static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo)
18839{
18840 MA_WAVECAPSA caps;
18841
18842 MA_ASSERT(pContext != NULL);
18843 MA_ASSERT(pCaps != NULL);
18844 MA_ASSERT(pDeviceInfo != NULL);
18845
18846 MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
18847 caps.dwFormats = pCaps->dwFormats;
18848 caps.wChannels = pCaps->wChannels;
18849 caps.NameGuid = pCaps->NameGuid;
18850 return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
18851}
18852
18853
18854static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
18855{
18856 UINT playbackDeviceCount;
18857 UINT captureDeviceCount;
18858 UINT iPlaybackDevice;
18859 UINT iCaptureDevice;
18860
18861 MA_ASSERT(pContext != NULL);
18862 MA_ASSERT(callback != NULL);
18863
18864 /* Playback. */
18865 playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)();
18866 for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) {
18867 MMRESULT result;
18868 MA_WAVEOUTCAPS2A caps;
18869
18870 MA_ZERO_OBJECT(&caps);
18871
18872 result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (WAVEOUTCAPSA*)&caps, sizeof(caps));
18873 if (result == MMSYSERR_NOERROR) {
18874 ma_device_info deviceInfo;
18875
18876 MA_ZERO_OBJECT(&deviceInfo);
18877 deviceInfo.id.winmm = iPlaybackDevice;
18878
18879 /* The first enumerated device is the default device. */
18880 if (iPlaybackDevice == 0) {
18881 deviceInfo.isDefault = MA_TRUE;
18882 }
18883
18884 if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
18885 ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
18886 if (cbResult == MA_FALSE) {
18887 return MA_SUCCESS; /* Enumeration was stopped. */
18888 }
18889 }
18890 }
18891 }
18892
18893 /* Capture. */
18894 captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)();
18895 for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) {
18896 MMRESULT result;
18897 MA_WAVEINCAPS2A caps;
18898
18899 MA_ZERO_OBJECT(&caps);
18900
18901 result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (WAVEINCAPSA*)&caps, sizeof(caps));
18902 if (result == MMSYSERR_NOERROR) {
18903 ma_device_info deviceInfo;
18904
18905 MA_ZERO_OBJECT(&deviceInfo);
18906 deviceInfo.id.winmm = iCaptureDevice;
18907
18908 /* The first enumerated device is the default device. */
18909 if (iCaptureDevice == 0) {
18910 deviceInfo.isDefault = MA_TRUE;
18911 }
18912
18913 if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
18914 ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
18915 if (cbResult == MA_FALSE) {
18916 return MA_SUCCESS; /* Enumeration was stopped. */
18917 }
18918 }
18919 }
18920 }
18921
18922 return MA_SUCCESS;
18923}
18924
18925static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
18926{
18927 UINT winMMDeviceID;
18928
18929 MA_ASSERT(pContext != NULL);
18930
18931 winMMDeviceID = 0;
18932 if (pDeviceID != NULL) {
18933 winMMDeviceID = (UINT)pDeviceID->winmm;
18934 }
18935
18936 pDeviceInfo->id.winmm = winMMDeviceID;
18937
18938 /* The first ID is the default device. */
18939 if (winMMDeviceID == 0) {
18940 pDeviceInfo->isDefault = MA_TRUE;
18941 }
18942
18943 if (deviceType == ma_device_type_playback) {
18944 MMRESULT result;
18945 MA_WAVEOUTCAPS2A caps;
18946
18947 MA_ZERO_OBJECT(&caps);
18948
18949 result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (WAVEOUTCAPSA*)&caps, sizeof(caps));
18950 if (result == MMSYSERR_NOERROR) {
18951 return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo);
18952 }
18953 } else {
18954 MMRESULT result;
18955 MA_WAVEINCAPS2A caps;
18956
18957 MA_ZERO_OBJECT(&caps);
18958
18959 result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (WAVEINCAPSA*)&caps, sizeof(caps));
18960 if (result == MMSYSERR_NOERROR) {
18961 return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo);
18962 }
18963 }
18964
18965 return MA_NO_DEVICE;
18966}
18967
18968
18969static ma_result ma_device_uninit__winmm(ma_device* pDevice)
18970{
18971 MA_ASSERT(pDevice != NULL);
18972
18973 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
18974 ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture);
18975 CloseHandle((HANDLE)pDevice->winmm.hEventCapture);
18976 }
18977
18978 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
18979 ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
18980 ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
18981 CloseHandle((HANDLE)pDevice->winmm.hEventPlayback);
18982 }
18983
18984 ma__free_from_callbacks(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
18985
18986 MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */
18987
18988 return MA_SUCCESS;
18989}
18990
18991static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
18992{
18993 /* WinMM has a minimum period size of 40ms. */
18994 ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate);
18995 ma_uint32 periodSizeInFrames;
18996
18997 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);
18998 if (periodSizeInFrames < minPeriodSizeInFrames) {
18999 periodSizeInFrames = minPeriodSizeInFrames;
19000 }
19001
19002 return periodSizeInFrames;
19003}
19004
19005static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
19006{
19007 const char* errorMsg = "";
19008 ma_result errorCode = MA_ERROR;
19009 ma_result result = MA_SUCCESS;
19010 ma_uint32 heapSize;
19011 UINT winMMDeviceIDPlayback = 0;
19012 UINT winMMDeviceIDCapture = 0;
19013
19014 MA_ASSERT(pDevice != NULL);
19015
19016 MA_ZERO_OBJECT(&pDevice->winmm);
19017
19018 if (pConfig->deviceType == ma_device_type_loopback) {
19019 return MA_DEVICE_TYPE_NOT_SUPPORTED;
19020 }
19021
19022 /* No exlusive mode with WinMM. */
19023 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
19024 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
19025 return MA_SHARE_MODE_NOT_SUPPORTED;
19026 }
19027
19028 if (pDescriptorPlayback->pDeviceID != NULL) {
19029 winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm;
19030 }
19031 if (pDescriptorCapture->pDeviceID != NULL) {
19032 winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm;
19033 }
19034
19035 /* The capture device needs to be initialized first. */
19036 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
19037 WAVEINCAPSA caps;
19038 WAVEFORMATEX wf;
19039 MMRESULT resultMM;
19040
19041 /* We use an event to know when a new fragment needs to be enqueued. */
19042 pDevice->winmm.hEventCapture = (ma_handle)CreateEventW(NULL, TRUE, TRUE, NULL);
19043 if (pDevice->winmm.hEventCapture == NULL) {
19044 errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError());
19045 goto on_error;
19046 }
19047
19048 /* The format should be based on the device's actual format. */
19049 if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MMSYSERR_NOERROR) {
19050 errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
19051 goto on_error;
19052 }
19053
19054 result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
19055 if (result != MA_SUCCESS) {
19056 errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
19057 goto on_error;
19058 }
19059
19060 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);
19061 if (resultMM != MMSYSERR_NOERROR) {
19062 errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
19063 goto on_error;
19064 }
19065
19066 pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf);
19067 pDescriptorCapture->channels = wf.nChannels;
19068 pDescriptorCapture->sampleRate = wf.nSamplesPerSec;
19069 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
19070 pDescriptorCapture->periodCount = pDescriptorCapture->periodCount;
19071 pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
19072 }
19073
19074 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
19075 WAVEOUTCAPSA caps;
19076 WAVEFORMATEX wf;
19077 MMRESULT resultMM;
19078
19079 /* We use an event to know when a new fragment needs to be enqueued. */
19080 pDevice->winmm.hEventPlayback = (ma_handle)CreateEvent(NULL, TRUE, TRUE, NULL);
19081 if (pDevice->winmm.hEventPlayback == NULL) {
19082 errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError());
19083 goto on_error;
19084 }
19085
19086 /* The format should be based on the device's actual format. */
19087 if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MMSYSERR_NOERROR) {
19088 errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
19089 goto on_error;
19090 }
19091
19092 result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
19093 if (result != MA_SUCCESS) {
19094 errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
19095 goto on_error;
19096 }
19097
19098 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);
19099 if (resultMM != MMSYSERR_NOERROR) {
19100 errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
19101 goto on_error;
19102 }
19103
19104 pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf);
19105 pDescriptorPlayback->channels = wf.nChannels;
19106 pDescriptorPlayback->sampleRate = wf.nSamplesPerSec;
19107 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
19108 pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount;
19109 pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
19110 }
19111
19112 /*
19113 The heap allocated data is allocated like so:
19114
19115 [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer]
19116 */
19117 heapSize = 0;
19118 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
19119 heapSize += sizeof(WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels));
19120 }
19121 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
19122 heapSize += sizeof(WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels));
19123 }
19124
19125 pDevice->winmm._pHeapData = (ma_uint8*)ma__calloc_from_callbacks(heapSize, &pDevice->pContext->allocationCallbacks);
19126 if (pDevice->winmm._pHeapData == NULL) {
19127 errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY;
19128 goto on_error;
19129 }
19130
19131 MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize);
19132
19133 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
19134 ma_uint32 iPeriod;
19135
19136 if (pConfig->deviceType == ma_device_type_capture) {
19137 pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
19138 pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount));
19139 } else {
19140 pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
19141 pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount));
19142 }
19143
19144 /* Prepare headers. */
19145 for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) {
19146 ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels);
19147
19148 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod));
19149 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes;
19150 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L;
19151 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L;
19152 ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
19153
19154 /*
19155 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
19156 it's unlocked and available for writing. A value of 1 means it's locked.
19157 */
19158 ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0;
19159 }
19160 }
19161
19162 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
19163 ma_uint32 iPeriod;
19164
19165 if (pConfig->deviceType == ma_device_type_playback) {
19166 pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData;
19167 pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*pDescriptorPlayback->periodCount);
19168 } else {
19169 pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount));
19170 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));
19171 }
19172
19173 /* Prepare headers. */
19174 for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) {
19175 ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels);
19176
19177 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod));
19178 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes;
19179 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L;
19180 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L;
19181 ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR));
19182
19183 /*
19184 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
19185 it's unlocked and available for writing. A value of 1 means it's locked.
19186 */
19187 ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0;
19188 }
19189 }
19190
19191 return MA_SUCCESS;
19192
19193on_error:
19194 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
19195 if (pDevice->winmm.pWAVEHDRCapture != NULL) {
19196 ma_uint32 iPeriod;
19197 for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) {
19198 ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
19199 }
19200 }
19201
19202 ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture);
19203 }
19204
19205 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
19206 if (pDevice->winmm.pWAVEHDRCapture != NULL) {
19207 ma_uint32 iPeriod;
19208 for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) {
19209 ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR));
19210 }
19211 }
19212
19213 ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
19214 }
19215
19216 ma__free_from_callbacks(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
19217 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, errorMsg, errorCode);
19218}
19219
19220static ma_result ma_device_start__winmm(ma_device* pDevice)
19221{
19222 MA_ASSERT(pDevice != NULL);
19223
19224 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
19225 MMRESULT resultMM;
19226 WAVEHDR* pWAVEHDR;
19227 ma_uint32 iPeriod;
19228
19229 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
19230
19231 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
19232 ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
19233
19234 /* 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. */
19235 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
19236 resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
19237 if (resultMM != MMSYSERR_NOERROR) {
19238 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture.", ma_result_from_MMRESULT(resultMM));
19239 }
19240
19241 /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */
19242 pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */
19243 }
19244
19245 /* Capture devices need to be explicitly started, unlike playback devices. */
19246 resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((HWAVEIN)pDevice->winmm.hDeviceCapture);
19247 if (resultMM != MMSYSERR_NOERROR) {
19248 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device.", ma_result_from_MMRESULT(resultMM));
19249 }
19250 }
19251
19252 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
19253 /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */
19254 }
19255
19256 return MA_SUCCESS;
19257}
19258
19259static ma_result ma_device_stop__winmm(ma_device* pDevice)
19260{
19261 MMRESULT resultMM;
19262
19263 MA_ASSERT(pDevice != NULL);
19264
19265 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
19266 if (pDevice->winmm.hDeviceCapture == NULL) {
19267 return MA_INVALID_ARGS;
19268 }
19269
19270 resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((HWAVEIN)pDevice->winmm.hDeviceCapture);
19271 if (resultMM != MMSYSERR_NOERROR) {
19272 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset capture device.", ma_result_from_MMRESULT(resultMM));
19273 }
19274 }
19275
19276 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
19277 ma_uint32 iPeriod;
19278 WAVEHDR* pWAVEHDR;
19279
19280 if (pDevice->winmm.hDevicePlayback == NULL) {
19281 return MA_INVALID_ARGS;
19282 }
19283
19284 /* 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. */
19285 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
19286 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) {
19287 if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */
19288 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
19289 break; /* An error occurred so just abandon ship and stop the device without draining. */
19290 }
19291
19292 pWAVEHDR[iPeriod].dwUser = 0;
19293 }
19294 }
19295
19296 resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
19297 if (resultMM != MMSYSERR_NOERROR) {
19298 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset playback device.", ma_result_from_MMRESULT(resultMM));
19299 }
19300 }
19301
19302 return MA_SUCCESS;
19303}
19304
19305static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
19306{
19307 ma_result result = MA_SUCCESS;
19308 MMRESULT resultMM;
19309 ma_uint32 totalFramesWritten;
19310 WAVEHDR* pWAVEHDR;
19311
19312 MA_ASSERT(pDevice != NULL);
19313 MA_ASSERT(pPCMFrames != NULL);
19314
19315 if (pFramesWritten != NULL) {
19316 *pFramesWritten = 0;
19317 }
19318
19319 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
19320
19321 /* Keep processing as much data as possible. */
19322 totalFramesWritten = 0;
19323 while (totalFramesWritten < frameCount) {
19324 /* If the current header has some space available we need to write part of it. */
19325 if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */
19326 /*
19327 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
19328 write it out and move on to the next iteration.
19329 */
19330 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
19331 ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback;
19332
19333 ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten));
19334 const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf);
19335 void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf);
19336 MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
19337
19338 pDevice->winmm.headerFramesConsumedPlayback += framesToCopy;
19339 totalFramesWritten += framesToCopy;
19340
19341 /* If we've consumed the buffer entirely we need to write it out to the device. */
19342 if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) {
19343 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */
19344 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
19345
19346 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
19347 ResetEvent((HANDLE)pDevice->winmm.hEventPlayback);
19348
19349 /* The device will be started here. */
19350 resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(WAVEHDR));
19351 if (resultMM != MMSYSERR_NOERROR) {
19352 result = ma_result_from_MMRESULT(resultMM);
19353 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed.", result);
19354 break;
19355 }
19356
19357 /* Make sure we move to the next header. */
19358 pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods;
19359 pDevice->winmm.headerFramesConsumedPlayback = 0;
19360 }
19361
19362 /* If at this point we have consumed the entire input buffer we can return. */
19363 MA_ASSERT(totalFramesWritten <= frameCount);
19364 if (totalFramesWritten == frameCount) {
19365 break;
19366 }
19367
19368 /* Getting here means there's more to process. */
19369 continue;
19370 }
19371
19372 /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */
19373 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
19374 result = MA_ERROR;
19375 break;
19376 }
19377
19378 /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
19379 if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & WHDR_DONE) != 0) {
19380 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */
19381 pDevice->winmm.headerFramesConsumedPlayback = 0;
19382 }
19383
19384 /* If the device has been stopped we need to break. */
19385 if (ma_device_get_state(pDevice) != MA_STATE_STARTED) {
19386 break;
19387 }
19388 }
19389
19390 if (pFramesWritten != NULL) {
19391 *pFramesWritten = totalFramesWritten;
19392 }
19393
19394 return result;
19395}
19396
19397static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
19398{
19399 ma_result result = MA_SUCCESS;
19400 MMRESULT resultMM;
19401 ma_uint32 totalFramesRead;
19402 WAVEHDR* pWAVEHDR;
19403
19404 MA_ASSERT(pDevice != NULL);
19405 MA_ASSERT(pPCMFrames != NULL);
19406
19407 if (pFramesRead != NULL) {
19408 *pFramesRead = 0;
19409 }
19410
19411 pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
19412
19413 /* Keep processing as much data as possible. */
19414 totalFramesRead = 0;
19415 while (totalFramesRead < frameCount) {
19416 /* If the current header has some space available we need to write part of it. */
19417 if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */
19418 /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */
19419 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
19420 ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture;
19421
19422 ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead));
19423 const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf);
19424 void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf);
19425 MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
19426
19427 pDevice->winmm.headerFramesConsumedCapture += framesToCopy;
19428 totalFramesRead += framesToCopy;
19429
19430 /* If we've consumed the buffer entirely we need to add it back to the device. */
19431 if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) {
19432 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */
19433 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
19434
19435 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
19436 ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
19437
19438 /* The device will be started here. */
19439 resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(WAVEHDR));
19440 if (resultMM != MMSYSERR_NOERROR) {
19441 result = ma_result_from_MMRESULT(resultMM);
19442 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed.", result);
19443 break;
19444 }
19445
19446 /* Make sure we move to the next header. */
19447 pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods;
19448 pDevice->winmm.headerFramesConsumedCapture = 0;
19449 }
19450
19451 /* If at this point we have filled the entire input buffer we can return. */
19452 MA_ASSERT(totalFramesRead <= frameCount);
19453 if (totalFramesRead == frameCount) {
19454 break;
19455 }
19456
19457 /* Getting here means there's more to process. */
19458 continue;
19459 }
19460
19461 /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */
19462 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
19463 result = MA_ERROR;
19464 break;
19465 }
19466
19467 /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
19468 if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & WHDR_DONE) != 0) {
19469 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */
19470 pDevice->winmm.headerFramesConsumedCapture = 0;
19471 }
19472
19473 /* If the device has been stopped we need to break. */
19474 if (ma_device_get_state(pDevice) != MA_STATE_STARTED) {
19475 break;
19476 }
19477 }
19478
19479 if (pFramesRead != NULL) {
19480 *pFramesRead = totalFramesRead;
19481 }
19482
19483 return result;
19484}
19485
19486static ma_result ma_context_uninit__winmm(ma_context* pContext)
19487{
19488 MA_ASSERT(pContext != NULL);
19489 MA_ASSERT(pContext->backend == ma_backend_winmm);
19490
19491 ma_dlclose(pContext, pContext->winmm.hWinMM);
19492 return MA_SUCCESS;
19493}
19494
19495static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
19496{
19497 MA_ASSERT(pContext != NULL);
19498
19499 (void)pConfig;
19500
19501 pContext->winmm.hWinMM = ma_dlopen(pContext, "winmm.dll");
19502 if (pContext->winmm.hWinMM == NULL) {
19503 return MA_NO_BACKEND;
19504 }
19505
19506 pContext->winmm.waveOutGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetNumDevs");
19507 pContext->winmm.waveOutGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetDevCapsA");
19508 pContext->winmm.waveOutOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutOpen");
19509 pContext->winmm.waveOutClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutClose");
19510 pContext->winmm.waveOutPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutPrepareHeader");
19511 pContext->winmm.waveOutUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutUnprepareHeader");
19512 pContext->winmm.waveOutWrite = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutWrite");
19513 pContext->winmm.waveOutReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutReset");
19514 pContext->winmm.waveInGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetNumDevs");
19515 pContext->winmm.waveInGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetDevCapsA");
19516 pContext->winmm.waveInOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInOpen");
19517 pContext->winmm.waveInClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInClose");
19518 pContext->winmm.waveInPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInPrepareHeader");
19519 pContext->winmm.waveInUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInUnprepareHeader");
19520 pContext->winmm.waveInAddBuffer = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInAddBuffer");
19521 pContext->winmm.waveInStart = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInStart");
19522 pContext->winmm.waveInReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInReset");
19523
19524 pCallbacks->onContextInit = ma_context_init__winmm;
19525 pCallbacks->onContextUninit = ma_context_uninit__winmm;
19526 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm;
19527 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm;
19528 pCallbacks->onDeviceInit = ma_device_init__winmm;
19529 pCallbacks->onDeviceUninit = ma_device_uninit__winmm;
19530 pCallbacks->onDeviceStart = ma_device_start__winmm;
19531 pCallbacks->onDeviceStop = ma_device_stop__winmm;
19532 pCallbacks->onDeviceRead = ma_device_read__winmm;
19533 pCallbacks->onDeviceWrite = ma_device_write__winmm;
19534 pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */
19535
19536 return MA_SUCCESS;
19537}
19538#endif
19539
19540
19541
19542
19543/******************************************************************************
19544
19545ALSA Backend
19546
19547******************************************************************************/
19548#ifdef MA_HAS_ALSA
19549
19550#include <poll.h> /* poll(), struct pollfd */
19551#include <sys/eventfd.h> /* eventfd() */
19552
19553#ifdef MA_NO_RUNTIME_LINKING
19554
19555/* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */
19556#if !defined(__cplusplus)
19557 #if defined(__STRICT_ANSI__)
19558 #if !defined(inline)
19559 #define inline __inline__ __attribute__((always_inline))
19560 #define MA_INLINE_DEFINED
19561 #endif
19562 #endif
19563#endif
19564#include <alsa/asoundlib.h>
19565#if defined(MA_INLINE_DEFINED)
19566 #undef inline
19567 #undef MA_INLINE_DEFINED
19568#endif
19569
19570typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t;
19571typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t;
19572typedef snd_pcm_stream_t ma_snd_pcm_stream_t;
19573typedef snd_pcm_format_t ma_snd_pcm_format_t;
19574typedef snd_pcm_access_t ma_snd_pcm_access_t;
19575typedef snd_pcm_t ma_snd_pcm_t;
19576typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
19577typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
19578typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
19579typedef snd_pcm_info_t ma_snd_pcm_info_t;
19580typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t;
19581typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t;
19582typedef snd_pcm_state_t ma_snd_pcm_state_t;
19583
19584/* snd_pcm_stream_t */
19585#define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK
19586#define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE
19587
19588/* snd_pcm_format_t */
19589#define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN
19590#define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8
19591#define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE
19592#define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE
19593#define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE
19594#define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE
19595#define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE
19596#define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE
19597#define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE
19598#define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE
19599#define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE
19600#define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE
19601#define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW
19602#define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW
19603#define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE
19604#define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE
19605
19606/* ma_snd_pcm_access_t */
19607#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED
19608#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED
19609#define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX
19610#define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED
19611#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED
19612
19613/* Channel positions. */
19614#define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN
19615#define MA_SND_CHMAP_NA SND_CHMAP_NA
19616#define MA_SND_CHMAP_MONO SND_CHMAP_MONO
19617#define MA_SND_CHMAP_FL SND_CHMAP_FL
19618#define MA_SND_CHMAP_FR SND_CHMAP_FR
19619#define MA_SND_CHMAP_RL SND_CHMAP_RL
19620#define MA_SND_CHMAP_RR SND_CHMAP_RR
19621#define MA_SND_CHMAP_FC SND_CHMAP_FC
19622#define MA_SND_CHMAP_LFE SND_CHMAP_LFE
19623#define MA_SND_CHMAP_SL SND_CHMAP_SL
19624#define MA_SND_CHMAP_SR SND_CHMAP_SR
19625#define MA_SND_CHMAP_RC SND_CHMAP_RC
19626#define MA_SND_CHMAP_FLC SND_CHMAP_FLC
19627#define MA_SND_CHMAP_FRC SND_CHMAP_FRC
19628#define MA_SND_CHMAP_RLC SND_CHMAP_RLC
19629#define MA_SND_CHMAP_RRC SND_CHMAP_RRC
19630#define MA_SND_CHMAP_FLW SND_CHMAP_FLW
19631#define MA_SND_CHMAP_FRW SND_CHMAP_FRW
19632#define MA_SND_CHMAP_FLH SND_CHMAP_FLH
19633#define MA_SND_CHMAP_FCH SND_CHMAP_FCH
19634#define MA_SND_CHMAP_FRH SND_CHMAP_FRH
19635#define MA_SND_CHMAP_TC SND_CHMAP_TC
19636#define MA_SND_CHMAP_TFL SND_CHMAP_TFL
19637#define MA_SND_CHMAP_TFR SND_CHMAP_TFR
19638#define MA_SND_CHMAP_TFC SND_CHMAP_TFC
19639#define MA_SND_CHMAP_TRL SND_CHMAP_TRL
19640#define MA_SND_CHMAP_TRR SND_CHMAP_TRR
19641#define MA_SND_CHMAP_TRC SND_CHMAP_TRC
19642#define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC
19643#define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC
19644#define MA_SND_CHMAP_TSL SND_CHMAP_TSL
19645#define MA_SND_CHMAP_TSR SND_CHMAP_TSR
19646#define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE
19647#define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE
19648#define MA_SND_CHMAP_BC SND_CHMAP_BC
19649#define MA_SND_CHMAP_BLC SND_CHMAP_BLC
19650#define MA_SND_CHMAP_BRC SND_CHMAP_BRC
19651
19652/* Open mode flags. */
19653#define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE
19654#define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS
19655#define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT
19656#else
19657#include <errno.h> /* For EPIPE, etc. */
19658typedef unsigned long ma_snd_pcm_uframes_t;
19659typedef long ma_snd_pcm_sframes_t;
19660typedef int ma_snd_pcm_stream_t;
19661typedef int ma_snd_pcm_format_t;
19662typedef int ma_snd_pcm_access_t;
19663typedef int ma_snd_pcm_state_t;
19664typedef struct ma_snd_pcm_t ma_snd_pcm_t;
19665typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
19666typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
19667typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
19668typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t;
19669typedef struct
19670{
19671 void* addr;
19672 unsigned int first;
19673 unsigned int step;
19674} ma_snd_pcm_channel_area_t;
19675typedef struct
19676{
19677 unsigned int channels;
19678 unsigned int pos[1];
19679} ma_snd_pcm_chmap_t;
19680
19681/* snd_pcm_state_t */
19682#define MA_SND_PCM_STATE_OPEN 0
19683#define MA_SND_PCM_STATE_SETUP 1
19684#define MA_SND_PCM_STATE_PREPARED 2
19685#define MA_SND_PCM_STATE_RUNNING 3
19686#define MA_SND_PCM_STATE_XRUN 4
19687#define MA_SND_PCM_STATE_DRAINING 5
19688#define MA_SND_PCM_STATE_PAUSED 6
19689#define MA_SND_PCM_STATE_SUSPENDED 7
19690#define MA_SND_PCM_STATE_DISCONNECTED 8
19691
19692/* snd_pcm_stream_t */
19693#define MA_SND_PCM_STREAM_PLAYBACK 0
19694#define MA_SND_PCM_STREAM_CAPTURE 1
19695
19696/* snd_pcm_format_t */
19697#define MA_SND_PCM_FORMAT_UNKNOWN -1
19698#define MA_SND_PCM_FORMAT_U8 1
19699#define MA_SND_PCM_FORMAT_S16_LE 2
19700#define MA_SND_PCM_FORMAT_S16_BE 3
19701#define MA_SND_PCM_FORMAT_S24_LE 6
19702#define MA_SND_PCM_FORMAT_S24_BE 7
19703#define MA_SND_PCM_FORMAT_S32_LE 10
19704#define MA_SND_PCM_FORMAT_S32_BE 11
19705#define MA_SND_PCM_FORMAT_FLOAT_LE 14
19706#define MA_SND_PCM_FORMAT_FLOAT_BE 15
19707#define MA_SND_PCM_FORMAT_FLOAT64_LE 16
19708#define MA_SND_PCM_FORMAT_FLOAT64_BE 17
19709#define MA_SND_PCM_FORMAT_MU_LAW 20
19710#define MA_SND_PCM_FORMAT_A_LAW 21
19711#define MA_SND_PCM_FORMAT_S24_3LE 32
19712#define MA_SND_PCM_FORMAT_S24_3BE 33
19713
19714/* snd_pcm_access_t */
19715#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0
19716#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1
19717#define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2
19718#define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3
19719#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4
19720
19721/* Channel positions. */
19722#define MA_SND_CHMAP_UNKNOWN 0
19723#define MA_SND_CHMAP_NA 1
19724#define MA_SND_CHMAP_MONO 2
19725#define MA_SND_CHMAP_FL 3
19726#define MA_SND_CHMAP_FR 4
19727#define MA_SND_CHMAP_RL 5
19728#define MA_SND_CHMAP_RR 6
19729#define MA_SND_CHMAP_FC 7
19730#define MA_SND_CHMAP_LFE 8
19731#define MA_SND_CHMAP_SL 9
19732#define MA_SND_CHMAP_SR 10
19733#define MA_SND_CHMAP_RC 11
19734#define MA_SND_CHMAP_FLC 12
19735#define MA_SND_CHMAP_FRC 13
19736#define MA_SND_CHMAP_RLC 14
19737#define MA_SND_CHMAP_RRC 15
19738#define MA_SND_CHMAP_FLW 16
19739#define MA_SND_CHMAP_FRW 17
19740#define MA_SND_CHMAP_FLH 18
19741#define MA_SND_CHMAP_FCH 19
19742#define MA_SND_CHMAP_FRH 20
19743#define MA_SND_CHMAP_TC 21
19744#define MA_SND_CHMAP_TFL 22
19745#define MA_SND_CHMAP_TFR 23
19746#define MA_SND_CHMAP_TFC 24
19747#define MA_SND_CHMAP_TRL 25
19748#define MA_SND_CHMAP_TRR 26
19749#define MA_SND_CHMAP_TRC 27
19750#define MA_SND_CHMAP_TFLC 28
19751#define MA_SND_CHMAP_TFRC 29
19752#define MA_SND_CHMAP_TSL 30
19753#define MA_SND_CHMAP_TSR 31
19754#define MA_SND_CHMAP_LLFE 32
19755#define MA_SND_CHMAP_RLFE 33
19756#define MA_SND_CHMAP_BC 34
19757#define MA_SND_CHMAP_BLC 35
19758#define MA_SND_CHMAP_BRC 36
19759
19760/* Open mode flags. */
19761#define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000
19762#define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000
19763#define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000
19764#endif
19765
19766typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode);
19767typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm);
19768typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void);
19769typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
19770typedef 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);
19771typedef 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);
19772typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask);
19773typedef int (* ma_snd_pcm_hw_params_set_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);
19774typedef 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);
19775typedef 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);
19776typedef 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);
19777typedef 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);
19778typedef 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);
19779typedef 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);
19780typedef 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);
19781typedef 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);
19782typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);
19783typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
19784typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
19785typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
19786typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
19787typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
19788typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
19789typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val);
19790typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
19791typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access);
19792typedef 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);
19793typedef int (* ma_snd_pcm_hw_params_test_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);
19794typedef 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);
19795typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
19796typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void);
19797typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
19798typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val);
19799typedef 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);
19800typedef 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);
19801typedef 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);
19802typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
19803typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void);
19804typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val);
19805typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm);
19806typedef ma_snd_pcm_state_t (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm);
19807typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm);
19808typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm);
19809typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm);
19810typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm);
19811typedef int (* ma_snd_pcm_reset_proc) (ma_snd_pcm_t *pcm);
19812typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints);
19813typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id);
19814typedef int (* ma_snd_card_get_index_proc) (const char *name);
19815typedef int (* ma_snd_device_name_free_hint_proc) (void **hints);
19816typedef 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);
19817typedef 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);
19818typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent);
19819typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size);
19820typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size);
19821typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm);
19822typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm);
19823typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout);
19824typedef int (* ma_snd_pcm_nonblock_proc) (ma_snd_pcm_t *pcm, int nonblock);
19825typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info);
19826typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void);
19827typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info);
19828typedef int (* ma_snd_pcm_poll_descriptors_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space);
19829typedef int (* ma_snd_pcm_poll_descriptors_count_proc) (ma_snd_pcm_t *pcm);
19830typedef int (* ma_snd_pcm_poll_descriptors_revents_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents);
19831typedef int (* ma_snd_config_update_free_global_proc) (void);
19832
19833/* This array specifies each of the common devices that can be used for both playback and capture. */
19834static const char* g_maCommonDeviceNamesALSA[] = {
19835 "default",
19836 "null",
19837 "pulse",
19838 "jack"
19839};
19840
19841/* This array allows us to blacklist specific playback devices. */
19842static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = {
19843 ""
19844};
19845
19846/* This array allows us to blacklist specific capture devices. */
19847static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = {
19848 ""
19849};
19850
19851
19852static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format)
19853{
19854 ma_snd_pcm_format_t ALSAFormats[] = {
19855 MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */
19856 MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */
19857 MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */
19858 MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */
19859 MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */
19860 MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */
19861 };
19862
19863 if (ma_is_big_endian()) {
19864 ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN;
19865 ALSAFormats[1] = MA_SND_PCM_FORMAT_U8;
19866 ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE;
19867 ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE;
19868 ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE;
19869 ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE;
19870 }
19871
19872 return ALSAFormats[format];
19873}
19874
19875static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA)
19876{
19877 if (ma_is_little_endian()) {
19878 switch (formatALSA) {
19879 case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16;
19880 case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24;
19881 case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32;
19882 case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32;
19883 default: break;
19884 }
19885 } else {
19886 switch (formatALSA) {
19887 case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16;
19888 case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24;
19889 case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32;
19890 case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32;
19891 default: break;
19892 }
19893 }
19894
19895 /* Endian agnostic. */
19896 switch (formatALSA) {
19897 case MA_SND_PCM_FORMAT_U8: return ma_format_u8;
19898 default: return ma_format_unknown;
19899 }
19900}
19901
19902static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos)
19903{
19904 switch (alsaChannelPos)
19905 {
19906 case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO;
19907 case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT;
19908 case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT;
19909 case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT;
19910 case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT;
19911 case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER;
19912 case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE;
19913 case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT;
19914 case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT;
19915 case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER;
19916 case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER;
19917 case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER;
19918 case MA_SND_CHMAP_RLC: return 0;
19919 case MA_SND_CHMAP_RRC: return 0;
19920 case MA_SND_CHMAP_FLW: return 0;
19921 case MA_SND_CHMAP_FRW: return 0;
19922 case MA_SND_CHMAP_FLH: return 0;
19923 case MA_SND_CHMAP_FCH: return 0;
19924 case MA_SND_CHMAP_FRH: return 0;
19925 case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER;
19926 case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT;
19927 case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT;
19928 case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER;
19929 case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT;
19930 case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT;
19931 case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER;
19932 default: break;
19933 }
19934
19935 return 0;
19936}
19937
19938static ma_bool32 ma_is_common_device_name__alsa(const char* name)
19939{
19940 size_t iName;
19941 for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) {
19942 if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) {
19943 return MA_TRUE;
19944 }
19945 }
19946
19947 return MA_FALSE;
19948}
19949
19950
19951static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name)
19952{
19953 size_t iName;
19954 for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) {
19955 if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) {
19956 return MA_TRUE;
19957 }
19958 }
19959
19960 return MA_FALSE;
19961}
19962
19963static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name)
19964{
19965 size_t iName;
19966 for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) {
19967 if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) {
19968 return MA_TRUE;
19969 }
19970 }
19971
19972 return MA_FALSE;
19973}
19974
19975static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name)
19976{
19977 if (deviceType == ma_device_type_playback) {
19978 return ma_is_playback_device_blacklisted__alsa(name);
19979 } else {
19980 return ma_is_capture_device_blacklisted__alsa(name);
19981 }
19982}
19983
19984
19985static const char* ma_find_char(const char* str, char c, int* index)
19986{
19987 int i = 0;
19988 for (;;) {
19989 if (str[i] == '\0') {
19990 if (index) *index = -1;
19991 return NULL;
19992 }
19993
19994 if (str[i] == c) {
19995 if (index) *index = i;
19996 return str + i;
19997 }
19998
19999 i += 1;
20000 }
20001
20002 /* Should never get here, but treat it as though the character was not found to make me feel better inside. */
20003 if (index) *index = -1;
20004 return NULL;
20005}
20006
20007static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid)
20008{
20009 /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */
20010
20011 int commaPos;
20012 const char* dev;
20013 int i;
20014
20015 if (hwid == NULL) {
20016 return MA_FALSE;
20017 }
20018
20019 if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') {
20020 return MA_FALSE;
20021 }
20022
20023 hwid += 3;
20024
20025 dev = ma_find_char(hwid, ',', &commaPos);
20026 if (dev == NULL) {
20027 return MA_FALSE;
20028 } else {
20029 dev += 1; /* Skip past the ",". */
20030 }
20031
20032 /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */
20033 for (i = 0; i < commaPos; ++i) {
20034 if (hwid[i] < '0' || hwid[i] > '9') {
20035 return MA_FALSE;
20036 }
20037 }
20038
20039 /* Check if everything after the "," is numeric. If not, return false. */
20040 i = 0;
20041 while (dev[i] != '\0') {
20042 if (dev[i] < '0' || dev[i] > '9') {
20043 return MA_FALSE;
20044 }
20045 i += 1;
20046 }
20047
20048 return MA_TRUE;
20049}
20050
20051static 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. */
20052{
20053 /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */
20054
20055 int colonPos;
20056 int commaPos;
20057 char card[256];
20058 const char* dev;
20059 int cardIndex;
20060
20061 if (dst == NULL) {
20062 return -1;
20063 }
20064 if (dstSize < 7) {
20065 return -1; /* Absolute minimum size of the output buffer is 7 bytes. */
20066 }
20067
20068 *dst = '\0'; /* Safety. */
20069 if (src == NULL) {
20070 return -1;
20071 }
20072
20073 /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */
20074 if (ma_is_device_name_in_hw_format__alsa(src)) {
20075 return ma_strcpy_s(dst, dstSize, src);
20076 }
20077
20078 src = ma_find_char(src, ':', &colonPos);
20079 if (src == NULL) {
20080 return -1; /* Couldn't find a colon */
20081 }
20082
20083 dev = ma_find_char(src, ',', &commaPos);
20084 if (dev == NULL) {
20085 dev = "0";
20086 ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */
20087 } else {
20088 dev = dev + 5; /* +5 = ",DEV=" */
20089 ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */
20090 }
20091
20092 cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card);
20093 if (cardIndex < 0) {
20094 return -2; /* Failed to retrieve the card index. */
20095 }
20096
20097
20098 /* Construction. */
20099 dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':';
20100 if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) {
20101 return -3;
20102 }
20103 if (ma_strcat_s(dst, dstSize, ",") != 0) {
20104 return -3;
20105 }
20106 if (ma_strcat_s(dst, dstSize, dev) != 0) {
20107 return -3;
20108 }
20109
20110 return 0;
20111}
20112
20113static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID)
20114{
20115 ma_uint32 i;
20116
20117 MA_ASSERT(pHWID != NULL);
20118
20119 for (i = 0; i < count; ++i) {
20120 if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) {
20121 return MA_TRUE;
20122 }
20123 }
20124
20125 return MA_FALSE;
20126}
20127
20128
20129static 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)
20130{
20131 ma_snd_pcm_t* pPCM;
20132 ma_snd_pcm_stream_t stream;
20133
20134 MA_ASSERT(pContext != NULL);
20135 MA_ASSERT(ppPCM != NULL);
20136
20137 *ppPCM = NULL;
20138 pPCM = NULL;
20139
20140 stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE;
20141
20142 if (pDeviceID == NULL) {
20143 ma_bool32 isDeviceOpen;
20144 size_t i;
20145
20146 /*
20147 We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes
20148 me feel better to try as hard as we can get to get _something_ working.
20149 */
20150 const char* defaultDeviceNames[] = {
20151 "default",
20152 NULL,
20153 NULL,
20154 NULL,
20155 NULL,
20156 NULL,
20157 NULL
20158 };
20159
20160 if (shareMode == ma_share_mode_exclusive) {
20161 defaultDeviceNames[1] = "hw";
20162 defaultDeviceNames[2] = "hw:0";
20163 defaultDeviceNames[3] = "hw:0,0";
20164 } else {
20165 if (deviceType == ma_device_type_playback) {
20166 defaultDeviceNames[1] = "dmix";
20167 defaultDeviceNames[2] = "dmix:0";
20168 defaultDeviceNames[3] = "dmix:0,0";
20169 } else {
20170 defaultDeviceNames[1] = "dsnoop";
20171 defaultDeviceNames[2] = "dsnoop:0";
20172 defaultDeviceNames[3] = "dsnoop:0,0";
20173 }
20174 defaultDeviceNames[4] = "hw";
20175 defaultDeviceNames[5] = "hw:0";
20176 defaultDeviceNames[6] = "hw:0,0";
20177 }
20178
20179 isDeviceOpen = MA_FALSE;
20180 for (i = 0; i < ma_countof(defaultDeviceNames); ++i) {
20181 if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') {
20182 if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) {
20183 isDeviceOpen = MA_TRUE;
20184 break;
20185 }
20186 }
20187 }
20188
20189 if (!isDeviceOpen) {
20190 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
20191 }
20192 } else {
20193 /*
20194 We're trying to open a specific device. There's a few things to consider here:
20195
20196 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
20197 an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it
20198 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").
20199 */
20200
20201 /* May end up needing to make small adjustments to the ID, so make a copy. */
20202 ma_device_id deviceID = *pDeviceID;
20203 int resultALSA = -ENODEV;
20204
20205 if (deviceID.alsa[0] != ':') {
20206 /* The ID is not in ":0,0" format. Use the ID exactly as-is. */
20207 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode);
20208 } else {
20209 char hwid[256];
20210
20211 /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */
20212 if (deviceID.alsa[1] == '\0') {
20213 deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */
20214 }
20215
20216 if (shareMode == ma_share_mode_shared) {
20217 if (deviceType == ma_device_type_playback) {
20218 ma_strcpy_s(hwid, sizeof(hwid), "dmix");
20219 } else {
20220 ma_strcpy_s(hwid, sizeof(hwid), "dsnoop");
20221 }
20222
20223 if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
20224 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
20225 }
20226 }
20227
20228 /* 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. */
20229 if (resultALSA != 0) {
20230 ma_strcpy_s(hwid, sizeof(hwid), "hw");
20231 if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
20232 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
20233 }
20234 }
20235 }
20236
20237 if (resultALSA < 0) {
20238 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed.", ma_result_from_errno(-resultALSA));
20239 }
20240 }
20241
20242 *ppPCM = pPCM;
20243 return MA_SUCCESS;
20244}
20245
20246
20247static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
20248{
20249 int resultALSA;
20250 ma_bool32 cbResult = MA_TRUE;
20251 char** ppDeviceHints;
20252 ma_device_id* pUniqueIDs = NULL;
20253 ma_uint32 uniqueIDCount = 0;
20254 char** ppNextDeviceHint;
20255
20256 MA_ASSERT(pContext != NULL);
20257 MA_ASSERT(callback != NULL);
20258
20259 ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock);
20260
20261 resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints);
20262 if (resultALSA < 0) {
20263 ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
20264 return ma_result_from_errno(-resultALSA);
20265 }
20266
20267 ppNextDeviceHint = ppDeviceHints;
20268 while (*ppNextDeviceHint != NULL) {
20269 char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME");
20270 char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC");
20271 char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID");
20272 ma_device_type deviceType = ma_device_type_playback;
20273 ma_bool32 stopEnumeration = MA_FALSE;
20274 char hwid[sizeof(pUniqueIDs->alsa)];
20275 ma_device_info deviceInfo;
20276
20277 if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) {
20278 deviceType = ma_device_type_playback;
20279 }
20280 if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) {
20281 deviceType = ma_device_type_capture;
20282 }
20283
20284 if (NAME != NULL) {
20285 if (pContext->alsa.useVerboseDeviceEnumeration) {
20286 /* Verbose mode. Use the name exactly as-is. */
20287 ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
20288 } else {
20289 /* Simplified mode. Use ":%d,%d" format. */
20290 if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) {
20291 /*
20292 At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the
20293 plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device
20294 initialization time and is used as an indicator to try and use the most appropriate plugin depending on the
20295 device type and sharing mode.
20296 */
20297 char* dst = hwid;
20298 char* src = hwid+2;
20299 while ((*dst++ = *src++));
20300 } else {
20301 /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */
20302 ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
20303 }
20304
20305 if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) {
20306 goto next_device; /* The device has already been enumerated. Move on to the next one. */
20307 } else {
20308 /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */
20309 size_t oldCapacity = sizeof(*pUniqueIDs) * uniqueIDCount;
20310 size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1);
20311 ma_device_id* pNewUniqueIDs = (ma_device_id*)ma__realloc_from_callbacks(pUniqueIDs, newCapacity, oldCapacity, &pContext->allocationCallbacks);
20312 if (pNewUniqueIDs == NULL) {
20313 goto next_device; /* Failed to allocate memory. */
20314 }
20315
20316 pUniqueIDs = pNewUniqueIDs;
20317 MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid));
20318 uniqueIDCount += 1;
20319 }
20320 }
20321 } else {
20322 MA_ZERO_MEMORY(hwid, sizeof(hwid));
20323 }
20324
20325 MA_ZERO_OBJECT(&deviceInfo);
20326 ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1);
20327
20328 /*
20329 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
20330 just use the name of "default" as the indicator.
20331 */
20332 if (ma_strcmp(deviceInfo.id.alsa, "default") == 0) {
20333 deviceInfo.isDefault = MA_TRUE;
20334 }
20335
20336
20337 /*
20338 DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose
20339 device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish
20340 between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the
20341 description.
20342
20343 The value in DESC seems to be split into two lines, with the first line being the name of the device and the
20344 second line being a description of the device. I don't like having the description be across two lines because
20345 it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line
20346 being put into parentheses. In simplified mode I'm just stripping the second line entirely.
20347 */
20348 if (DESC != NULL) {
20349 int lfPos;
20350 const char* line2 = ma_find_char(DESC, '\n', &lfPos);
20351 if (line2 != NULL) {
20352 line2 += 1; /* Skip past the new-line character. */
20353
20354 if (pContext->alsa.useVerboseDeviceEnumeration) {
20355 /* Verbose mode. Put the second line in brackets. */
20356 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
20357 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " (");
20358 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2);
20359 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")");
20360 } else {
20361 /* Simplified mode. Strip the second line entirely. */
20362 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
20363 }
20364 } else {
20365 /* There's no second line. Just copy the whole description. */
20366 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1);
20367 }
20368 }
20369
20370 if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) {
20371 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
20372 }
20373
20374 /*
20375 Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback
20376 again for the other device type in this case. We do this for known devices.
20377 */
20378 if (cbResult) {
20379 if (ma_is_common_device_name__alsa(NAME)) {
20380 if (deviceType == ma_device_type_playback) {
20381 if (!ma_is_capture_device_blacklisted__alsa(NAME)) {
20382 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
20383 }
20384 } else {
20385 if (!ma_is_playback_device_blacklisted__alsa(NAME)) {
20386 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
20387 }
20388 }
20389 }
20390 }
20391
20392 if (cbResult == MA_FALSE) {
20393 stopEnumeration = MA_TRUE;
20394 }
20395
20396 next_device:
20397 free(NAME);
20398 free(DESC);
20399 free(IOID);
20400 ppNextDeviceHint += 1;
20401
20402 /* We need to stop enumeration if the callback returned false. */
20403 if (stopEnumeration) {
20404 break;
20405 }
20406 }
20407
20408 ma__free_from_callbacks(pUniqueIDs, &pContext->allocationCallbacks);
20409 ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);
20410
20411 ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
20412
20413 return MA_SUCCESS;
20414}
20415
20416
20417typedef struct
20418{
20419 ma_device_type deviceType;
20420 const ma_device_id* pDeviceID;
20421 ma_share_mode shareMode;
20422 ma_device_info* pDeviceInfo;
20423 ma_bool32 foundDevice;
20424} ma_context_get_device_info_enum_callback_data__alsa;
20425
20426static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData)
20427{
20428 ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData;
20429 MA_ASSERT(pData != NULL);
20430
20431 (void)pContext;
20432
20433 if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
20434 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
20435 pData->foundDevice = MA_TRUE;
20436 } else {
20437 if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) {
20438 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
20439 pData->foundDevice = MA_TRUE;
20440 }
20441 }
20442
20443 /* Keep enumerating until we have found the device. */
20444 return !pData->foundDevice;
20445}
20446
20447static 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)
20448{
20449 MA_ASSERT(pPCM != NULL);
20450 MA_ASSERT(pHWParams != NULL);
20451 MA_ASSERT(pDeviceInfo != NULL);
20452
20453 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) {
20454 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
20455 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
20456 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
20457 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
20458 pDeviceInfo->nativeDataFormatCount += 1;
20459 }
20460}
20461
20462static 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)
20463{
20464 ma_uint32 iSampleRate;
20465 unsigned int minSampleRate;
20466 unsigned int maxSampleRate;
20467 int sampleRateDir; /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */
20468
20469 /* There could be a range. */
20470 ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir);
20471 ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir);
20472
20473 /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculus. */
20474 minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);
20475 maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);
20476
20477 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {
20478 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];
20479
20480 if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {
20481 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo);
20482 }
20483 }
20484
20485 /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */
20486 if (!ma_is_standard_sample_rate(minSampleRate)) {
20487 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo);
20488 }
20489
20490 if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) {
20491 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo);
20492 }
20493}
20494
20495static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
20496{
20497 ma_context_get_device_info_enum_callback_data__alsa data;
20498 ma_result result;
20499 int resultALSA;
20500 ma_snd_pcm_t* pPCM;
20501 ma_snd_pcm_hw_params_t* pHWParams;
20502 ma_uint32 iFormat;
20503 ma_uint32 iChannel;
20504
20505 MA_ASSERT(pContext != NULL);
20506
20507 /* We just enumerate to find basic information about the device. */
20508 data.deviceType = deviceType;
20509 data.pDeviceID = pDeviceID;
20510 data.pDeviceInfo = pDeviceInfo;
20511 data.foundDevice = MA_FALSE;
20512 result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data);
20513 if (result != MA_SUCCESS) {
20514 return result;
20515 }
20516
20517 if (!data.foundDevice) {
20518 return MA_NO_DEVICE;
20519 }
20520
20521 if (ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
20522 pDeviceInfo->isDefault = MA_TRUE;
20523 }
20524
20525 /* For detailed info we need to open the device. */
20526 result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM);
20527 if (result != MA_SUCCESS) {
20528 return result;
20529 }
20530
20531 /* We need to initialize a HW parameters object in order to know what formats are supported. */
20532 pHWParams = (ma_snd_pcm_hw_params_t*)ma__calloc_from_callbacks(((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)(), &pContext->allocationCallbacks);
20533 if (pHWParams == NULL) {
20534 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
20535 return MA_OUT_OF_MEMORY;
20536 }
20537
20538 resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
20539 if (resultALSA < 0) {
20540 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
20541 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
20542 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", ma_result_from_errno(-resultALSA));
20543 }
20544
20545 /*
20546 Some ALSA devices can support many permutations of formats, channels and rates. We only support
20547 a fixed number of permutations which means we need to employ some strategies to ensure the best
20548 combinations are returned. An example is the "pulse" device which can do it's own data conversion
20549 in software and as a result can support any combination of format, channels and rate.
20550
20551 We want to ensure the the first data formats are the best. We have a list of favored sample
20552 formats and sample rates, so these will be the basis of our iteration.
20553 */
20554
20555 /* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */
20556 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
20557 ma_format format = g_maFormatPriorities[iFormat];
20558
20559 /*
20560 For each format we need to make sure we reset the configuration space so we don't return
20561 channel counts and rates that aren't compatible with a format.
20562 */
20563 ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
20564
20565 /* Test the format first. If this fails it means the format is not supported and we can skip it. */
20566 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) {
20567 /* The format is supported. */
20568 unsigned int minChannels;
20569 unsigned int maxChannels;
20570
20571 /*
20572 The configuration space needs to be restricted to this format so we can get an accurate
20573 picture of which sample rates and channel counts are support with this format.
20574 */
20575 ((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));
20576
20577 /* Now we need to check for supported channels. */
20578 ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels);
20579 ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels);
20580
20581 if (minChannels > MA_MAX_CHANNELS) {
20582 continue; /* Too many channels. */
20583 }
20584 if (maxChannels < MA_MIN_CHANNELS) {
20585 continue; /* Not enough channels. */
20586 }
20587
20588 /*
20589 Make sure the channel count is clamped. This is mainly intended for the max channels
20590 because some devices can report an unbound maximum.
20591 */
20592 minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
20593 maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
20594
20595 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
20596 /* 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. */
20597 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. */
20598 } else {
20599 /* The device only supports a specific set of channels. We need to iterate over all of them. */
20600 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
20601 /* Test the channel before applying it to the configuration space. */
20602 unsigned int channels = iChannel;
20603
20604 /* Make sure our channel range is reset before testing again or else we'll always fail the test. */
20605 ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
20606 ((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));
20607
20608 if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) {
20609 /* The channel count is supported. */
20610
20611 /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */
20612 ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels);
20613
20614 /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */
20615 ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo);
20616 } else {
20617 /* The channel count is not supported. Skip. */
20618 }
20619 }
20620 }
20621 } else {
20622 /* The format is not supported. Skip. */
20623 }
20624 }
20625
20626 ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
20627
20628 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
20629 return MA_SUCCESS;
20630}
20631
20632static ma_result ma_device_uninit__alsa(ma_device* pDevice)
20633{
20634 MA_ASSERT(pDevice != NULL);
20635
20636 if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) {
20637 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
20638 close(pDevice->alsa.wakeupfdCapture);
20639 ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks);
20640 }
20641
20642 if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) {
20643 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
20644 close(pDevice->alsa.wakeupfdPlayback);
20645 ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks);
20646 }
20647
20648 return MA_SUCCESS;
20649}
20650
20651static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
20652{
20653 ma_result result;
20654 int resultALSA;
20655 ma_snd_pcm_t* pPCM;
20656 ma_bool32 isUsingMMap;
20657 ma_snd_pcm_format_t formatALSA;
20658 ma_format internalFormat;
20659 ma_uint32 internalChannels;
20660 ma_uint32 internalSampleRate;
20661 ma_channel internalChannelMap[MA_MAX_CHANNELS];
20662 ma_uint32 internalPeriodSizeInFrames;
20663 ma_uint32 internalPeriods;
20664 int openMode;
20665 ma_snd_pcm_hw_params_t* pHWParams;
20666 ma_snd_pcm_sw_params_t* pSWParams;
20667 ma_snd_pcm_uframes_t bufferBoundary;
20668 int pollDescriptorCount;
20669 struct pollfd* pPollDescriptors;
20670 int wakeupfd;
20671
20672 MA_ASSERT(pConfig != NULL);
20673 MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */
20674 MA_ASSERT(pDevice != NULL);
20675
20676 formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format);
20677
20678 openMode = 0;
20679 if (pConfig->alsa.noAutoResample) {
20680 openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE;
20681 }
20682 if (pConfig->alsa.noAutoChannels) {
20683 openMode |= MA_SND_PCM_NO_AUTO_CHANNELS;
20684 }
20685 if (pConfig->alsa.noAutoFormat) {
20686 openMode |= MA_SND_PCM_NO_AUTO_FORMAT;
20687 }
20688
20689 result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM);
20690 if (result != MA_SUCCESS) {
20691 return result;
20692 }
20693
20694
20695 /* Hardware parameters. */
20696 pHWParams = (ma_snd_pcm_hw_params_t*)ma__calloc_from_callbacks(((ma_snd_pcm_hw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_hw_params_sizeof)(), &pDevice->pContext->allocationCallbacks);
20697 if (pHWParams == NULL) {
20698 return MA_OUT_OF_MEMORY;
20699 }
20700
20701 resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
20702 if (resultALSA < 0) {
20703 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20704 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20705 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", ma_result_from_errno(-resultALSA));
20706 }
20707
20708 /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */
20709 isUsingMMap = MA_FALSE;
20710#if 0 /* NOTE: MMAP mode temporarily disabled. */
20711 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. */
20712 if (!pConfig->alsa.noMMap && ma_device__is_async(pDevice)) {
20713 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) {
20714 pDevice->alsa.isUsingMMap = MA_TRUE;
20715 }
20716 }
20717 }
20718#endif
20719
20720 if (!isUsingMMap) {
20721 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);
20722 if (resultALSA < 0) {
20723 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20724 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20725 return ma_post_error(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.", ma_result_from_errno(-resultALSA));
20726 }
20727 }
20728
20729 /*
20730 Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't
20731 find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS.
20732 */
20733
20734 /* Format. */
20735 {
20736 /*
20737 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
20738 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.
20739 */
20740 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) {
20741 /* We're either requesting the native format or the specified format is not supported. */
20742 size_t iFormat;
20743
20744 formatALSA = MA_SND_PCM_FORMAT_UNKNOWN;
20745 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) {
20746 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) {
20747 formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]);
20748 break;
20749 }
20750 }
20751
20752 if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) {
20753 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20754 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20755 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats.", MA_FORMAT_NOT_SUPPORTED);
20756 }
20757 }
20758
20759 resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA);
20760 if (resultALSA < 0) {
20761 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20762 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20763 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.", ma_result_from_errno(-resultALSA));
20764 }
20765
20766 internalFormat = ma_format_from_alsa(formatALSA);
20767 if (internalFormat == ma_format_unknown) {
20768 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20769 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20770 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
20771 }
20772 }
20773
20774 /* Channels. */
20775 {
20776 unsigned int channels = pDescriptor->channels;
20777 if (channels == 0) {
20778 channels = MA_DEFAULT_CHANNELS;
20779 }
20780
20781 resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels);
20782 if (resultALSA < 0) {
20783 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20784 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20785 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed.", ma_result_from_errno(-resultALSA));
20786 }
20787
20788 internalChannels = (ma_uint32)channels;
20789 }
20790
20791 /* Sample Rate */
20792 {
20793 unsigned int sampleRate;
20794
20795 /*
20796 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
20797 problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable
20798 resampling.
20799
20800 To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a
20801 sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling
20802 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
20803 faster rate.
20804
20805 miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine
20806 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
20807 good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion.
20808
20809 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
20810 this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins.
20811 */
20812 ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0);
20813
20814 sampleRate = pDescriptor->sampleRate;
20815 if (sampleRate == 0) {
20816 sampleRate = MA_DEFAULT_SAMPLE_RATE;
20817 }
20818
20819 resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0);
20820 if (resultALSA < 0) {
20821 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20822 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20823 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed.", ma_result_from_errno(-resultALSA));
20824 }
20825
20826 internalSampleRate = (ma_uint32)sampleRate;
20827 }
20828
20829 /* Periods. */
20830 {
20831 ma_uint32 periods = pDescriptor->periodCount;
20832
20833 resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL);
20834 if (resultALSA < 0) {
20835 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20836 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20837 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed.", ma_result_from_errno(-resultALSA));
20838 }
20839
20840 internalPeriods = periods;
20841 }
20842
20843 /* Buffer Size */
20844 {
20845 ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods;
20846
20847 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);
20848 if (resultALSA < 0) {
20849 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20850 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20851 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed.", ma_result_from_errno(-resultALSA));
20852 }
20853
20854 internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods;
20855 }
20856
20857 /* Apply hardware parameters. */
20858 resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams);
20859 if (resultALSA < 0) {
20860 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20861 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20862 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.", ma_result_from_errno(-resultALSA));
20863 }
20864
20865 ma__free_from_callbacks(pHWParams, &pDevice->pContext->allocationCallbacks);
20866 pHWParams = NULL;
20867
20868
20869 /* Software parameters. */
20870 pSWParams = (ma_snd_pcm_sw_params_t*)ma__calloc_from_callbacks(((ma_snd_pcm_sw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_sw_params_sizeof)(), &pDevice->pContext->allocationCallbacks);
20871 if (pSWParams == NULL) {
20872 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20873 return MA_OUT_OF_MEMORY;
20874 }
20875
20876 resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams);
20877 if (resultALSA < 0) {
20878 ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks);
20879 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20880 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed.", ma_result_from_errno(-resultALSA));
20881 }
20882
20883 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));
20884 if (resultALSA < 0) {
20885 ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks);
20886 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20887 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.", ma_result_from_errno(-resultALSA));
20888 }
20889
20890 resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary);
20891 if (resultALSA < 0) {
20892 bufferBoundary = internalPeriodSizeInFrames * internalPeriods;
20893 }
20894
20895 if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */
20896 /*
20897 Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to
20898 the size of a period. But for full-duplex we need to set it such that it is at least two periods.
20899 */
20900 resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2);
20901 if (resultALSA < 0) {
20902 ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks);
20903 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20904 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed.", ma_result_from_errno(-resultALSA));
20905 }
20906
20907 resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary);
20908 if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */
20909 ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks);
20910 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20911 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed.", ma_result_from_errno(-resultALSA));
20912 }
20913 }
20914
20915 resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams);
20916 if (resultALSA < 0) {
20917 ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks);
20918 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20919 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.", ma_result_from_errno(-resultALSA));
20920 }
20921
20922 ma__free_from_callbacks(pSWParams, &pDevice->pContext->allocationCallbacks);
20923 pSWParams = NULL;
20924
20925
20926 /* 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. */
20927 {
20928 ma_snd_pcm_chmap_t* pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM);
20929 if (pChmap != NULL) {
20930 ma_uint32 iChannel;
20931
20932 /* 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(). */
20933 if (pChmap->channels >= internalChannels) {
20934 /* Drop excess channels. */
20935 for (iChannel = 0; iChannel < internalChannels; ++iChannel) {
20936 internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
20937 }
20938 } else {
20939 ma_uint32 i;
20940
20941 /*
20942 Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate
20943 channels. If validation fails, fall back to defaults.
20944 */
20945 ma_bool32 isValid = MA_TRUE;
20946
20947 /* Fill with defaults. */
20948 ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
20949
20950 /* Overwrite first pChmap->channels channels. */
20951 for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) {
20952 internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
20953 }
20954
20955 /* Validate. */
20956 for (i = 0; i < internalChannels && isValid; ++i) {
20957 ma_uint32 j;
20958 for (j = i+1; j < internalChannels; ++j) {
20959 if (internalChannelMap[i] == internalChannelMap[j]) {
20960 isValid = MA_FALSE;
20961 break;
20962 }
20963 }
20964 }
20965
20966 /* If our channel map is invalid, fall back to defaults. */
20967 if (!isValid) {
20968 ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
20969 }
20970 }
20971
20972 free(pChmap);
20973 pChmap = NULL;
20974 } else {
20975 /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */
20976 ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
20977 }
20978 }
20979
20980
20981 /*
20982 We need to retrieve the poll descriptors so we can use poll() to wait for data to become
20983 available for reading or writing. There's no well defined maximum for this so we're just going
20984 to allocate this on the heap.
20985 */
20986 pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM);
20987 if (pollDescriptorCount <= 0) {
20988 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20989 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count.", MA_ERROR);
20990 }
20991
20992 pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks/*, MA_ALLOCATION_TYPE_GENERAL*/); /* +1 because we want room for the wakeup descriptor. */
20993 if (pPollDescriptors == NULL) {
20994 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
20995 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors.", MA_OUT_OF_MEMORY);
20996 }
20997
20998 /*
20999 We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver
21000 never returns from writei() and readi(). This has been observed with the "pulse" device.
21001 */
21002 wakeupfd = eventfd(0, 0);
21003 if (wakeupfd < 0) {
21004 ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
21005 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
21006 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup.", ma_result_from_errno(errno));
21007 }
21008
21009 /* We'll place the wakeup fd at the start of the buffer. */
21010 pPollDescriptors[0].fd = wakeupfd;
21011 pPollDescriptors[0].events = POLLIN; /* We only care about waiting to read from the wakeup file descriptor. */
21012 pPollDescriptors[0].revents = 0;
21013
21014 /* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */
21015 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. */
21016 if (pollDescriptorCount <= 0) {
21017 close(wakeupfd);
21018 ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
21019 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
21020 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors.", MA_ERROR);
21021 }
21022
21023 if (deviceType == ma_device_type_capture) {
21024 pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount;
21025 pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors;
21026 pDevice->alsa.wakeupfdCapture = wakeupfd;
21027 } else {
21028 pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount;
21029 pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors;
21030 pDevice->alsa.wakeupfdPlayback = wakeupfd;
21031 }
21032
21033
21034 /* We're done. Prepare the device. */
21035 resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM);
21036 if (resultALSA < 0) {
21037 close(wakeupfd);
21038 ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
21039 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
21040 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.", ma_result_from_errno(-resultALSA));
21041 }
21042
21043
21044 if (deviceType == ma_device_type_capture) {
21045 pDevice->alsa.pPCMCapture = (ma_ptr)pPCM;
21046 pDevice->alsa.isUsingMMapCapture = isUsingMMap;
21047 } else {
21048 pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM;
21049 pDevice->alsa.isUsingMMapPlayback = isUsingMMap;
21050 }
21051
21052 pDescriptor->format = internalFormat;
21053 pDescriptor->channels = internalChannels;
21054 pDescriptor->sampleRate = internalSampleRate;
21055 ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS));
21056 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
21057 pDescriptor->periodCount = internalPeriods;
21058
21059 return MA_SUCCESS;
21060}
21061
21062static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
21063{
21064 MA_ASSERT(pDevice != NULL);
21065
21066 MA_ZERO_OBJECT(&pDevice->alsa);
21067
21068 if (pConfig->deviceType == ma_device_type_loopback) {
21069 return MA_DEVICE_TYPE_NOT_SUPPORTED;
21070 }
21071
21072 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
21073 ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
21074 if (result != MA_SUCCESS) {
21075 return result;
21076 }
21077 }
21078
21079 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
21080 ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
21081 if (result != MA_SUCCESS) {
21082 return result;
21083 }
21084 }
21085
21086 return MA_SUCCESS;
21087}
21088
21089static ma_result ma_device_start__alsa(ma_device* pDevice)
21090{
21091 int resultALSA;
21092
21093 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
21094 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
21095 if (resultALSA < 0) {
21096 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device.", ma_result_from_errno(-resultALSA));
21097 }
21098 }
21099
21100 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
21101 /* Don't need to do anything for playback because it'll be started automatically when enough data has been written. */
21102 }
21103
21104 return MA_SUCCESS;
21105}
21106
21107static ma_result ma_device_stop__alsa(ma_device* pDevice)
21108{
21109 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
21110 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device... ");
21111 ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
21112 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Done\n");
21113
21114 /* We need to prepare the device again, otherwise we won't be able to restart the device. */
21115 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device... ");
21116 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
21117 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed\n");
21118 } else {
21119 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Done\n");
21120 }
21121 }
21122
21123 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
21124 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device... ");
21125 ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
21126 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Done\n");
21127
21128 /* We need to prepare the device again, otherwise we won't be able to restart the device. */
21129 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device... ");
21130 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {
21131 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed\n");
21132 } else {
21133 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Done\n");
21134 }
21135 }
21136
21137 return MA_SUCCESS;
21138}
21139
21140static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent)
21141{
21142 for (;;) {
21143 unsigned short revents;
21144 int resultALSA;
21145 int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1);
21146 if (resultPoll < 0) {
21147 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed.", ma_result_from_errno(errno));
21148 }
21149
21150 /*
21151 Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor
21152 has had it's POLLIN flag set. If so, we need to actually read the data and then exit
21153 function. The wakeup descriptor will be the first item in the descriptors buffer.
21154 */
21155 if ((pPollDescriptors[0].revents & POLLIN) != 0) {
21156 ma_uint64 t;
21157 read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */
21158
21159 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] POLLIN set for wakeupfd\n");
21160
21161 return MA_DEVICE_NOT_STARTED;
21162 }
21163
21164 /*
21165 Getting here means that some data should be able to be read. We need to use ALSA to
21166 translate the revents flags for us.
21167 */
21168 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. */
21169 if (resultALSA < 0) {
21170 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.", ma_result_from_errno(-resultALSA));
21171 }
21172
21173 if ((revents & POLLERR) != 0) {
21174 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] POLLERR detected.", ma_result_from_errno(errno));
21175 }
21176
21177 if ((revents & requiredEvent) == requiredEvent) {
21178 break; /* We're done. Data available for reading or writing. */
21179 }
21180 }
21181
21182 return MA_SUCCESS;
21183}
21184
21185static ma_result ma_device_wait_read__alsa(ma_device* pDevice)
21186{
21187 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. */
21188}
21189
21190static ma_result ma_device_wait_write__alsa(ma_device* pDevice)
21191{
21192 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. */
21193}
21194
21195static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead)
21196{
21197 ma_snd_pcm_sframes_t resultALSA;
21198
21199 MA_ASSERT(pDevice != NULL);
21200 MA_ASSERT(pFramesOut != NULL);
21201
21202 if (pFramesRead != NULL) {
21203 *pFramesRead = 0;
21204 }
21205
21206 while (ma_device_get_state(pDevice) == MA_STATE_STARTED) {
21207 ma_result result;
21208
21209 /* 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. */
21210 result = ma_device_wait_read__alsa(pDevice);
21211 if (result != MA_SUCCESS) {
21212 return result;
21213 }
21214
21215 /* Getting here means we should have data available. */
21216 resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
21217 if (resultALSA >= 0) {
21218 break; /* Success. */
21219 } else {
21220 if (resultALSA == -EAGAIN) {
21221 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: EGAIN (read)\n");*/
21222 continue; /* Try again. */
21223 } else if (resultALSA == -EPIPE) {
21224 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: EPIPE (read)\n");
21225
21226 /* Overrun. Recover and try again. If this fails we need to return an error. */
21227 resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE);
21228 if (resultALSA < 0) {
21229 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.", ma_result_from_errno((int)-resultALSA));
21230 }
21231
21232 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
21233 if (resultALSA < 0) {
21234 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", ma_result_from_errno((int)-resultALSA));
21235 }
21236
21237 continue; /* Try reading again. */
21238 }
21239 }
21240 }
21241
21242 if (pFramesRead != NULL) {
21243 *pFramesRead = resultALSA;
21244 }
21245
21246 return MA_SUCCESS;
21247}
21248
21249static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
21250{
21251 ma_snd_pcm_sframes_t resultALSA;
21252
21253 MA_ASSERT(pDevice != NULL);
21254 MA_ASSERT(pFrames != NULL);
21255
21256 if (pFramesWritten != NULL) {
21257 *pFramesWritten = 0;
21258 }
21259
21260 while (ma_device_get_state(pDevice) == MA_STATE_STARTED) {
21261 ma_result result;
21262
21263 /* 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. */
21264 result = ma_device_wait_write__alsa(pDevice);
21265 if (result != MA_SUCCESS) {
21266 return result;
21267 }
21268
21269 resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
21270 if (resultALSA >= 0) {
21271 break; /* Success. */
21272 } else {
21273 if (resultALSA == -EAGAIN) {
21274 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: EGAIN (write)\n");*/
21275 continue; /* Try again. */
21276 } else if (resultALSA == -EPIPE) {
21277 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: EPIPE (write)\n");
21278
21279 /* Underrun. Recover and try again. If this fails we need to return an error. */
21280 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). */
21281 if (resultALSA < 0) {
21282 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.", ma_result_from_errno((int)-resultALSA));
21283 }
21284
21285 /*
21286 In my testing I have had a situation where writei() does not automatically restart the device even though I've set it
21287 up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of
21288 frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure
21289 if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't
21290 quite right here.
21291 */
21292 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
21293 if (resultALSA < 0) {
21294 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", ma_result_from_errno((int)-resultALSA));
21295 }
21296
21297 continue; /* Try writing again. */
21298 }
21299 }
21300 }
21301
21302 if (pFramesWritten != NULL) {
21303 *pFramesWritten = resultALSA;
21304 }
21305
21306 return MA_SUCCESS;
21307}
21308
21309static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice)
21310{
21311 ma_uint64 t = 1;
21312
21313 MA_ASSERT(pDevice != NULL);
21314
21315 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up... ");
21316
21317 /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */
21318 if (pDevice->alsa.pPollDescriptorsCapture != NULL) {
21319 write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t));
21320 }
21321 if (pDevice->alsa.pPollDescriptorsPlayback != NULL) {
21322 write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t));
21323 }
21324
21325 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Done\n");
21326
21327 return MA_SUCCESS;
21328}
21329
21330static ma_result ma_context_uninit__alsa(ma_context* pContext)
21331{
21332 MA_ASSERT(pContext != NULL);
21333 MA_ASSERT(pContext->backend == ma_backend_alsa);
21334
21335 /* Clean up memory for memory leak checkers. */
21336 ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)();
21337
21338#ifndef MA_NO_RUNTIME_LINKING
21339 ma_dlclose(pContext, pContext->alsa.asoundSO);
21340#endif
21341
21342 ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock);
21343
21344 return MA_SUCCESS;
21345}
21346
21347static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
21348{
21349#ifndef MA_NO_RUNTIME_LINKING
21350 const char* libasoundNames[] = {
21351 "libasound.so.2",
21352 "libasound.so"
21353 };
21354 size_t i;
21355
21356 for (i = 0; i < ma_countof(libasoundNames); ++i) {
21357 pContext->alsa.asoundSO = ma_dlopen(pContext, libasoundNames[i]);
21358 if (pContext->alsa.asoundSO != NULL) {
21359 break;
21360 }
21361 }
21362
21363 if (pContext->alsa.asoundSO == NULL) {
21364 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n");
21365 return MA_NO_BACKEND;
21366 }
21367
21368 pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_open");
21369 pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_close");
21370 pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof");
21371 pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_any");
21372 pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format");
21373 pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first");
21374 pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask");
21375 pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels");
21376 pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near");
21377 pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax");
21378 pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample");
21379 pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate");
21380 pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near");
21381 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");
21382 pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near");
21383 pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access");
21384 pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format");
21385 pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels");
21386 pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min");
21387 pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max");
21388 pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate");
21389 pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min");
21390 pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max");
21391 pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size");
21392 pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods");
21393 pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access");
21394 pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format");
21395 pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels");
21396 pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate");
21397 pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params");
21398 pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof");
21399 pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_current");
21400 pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary");
21401 pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min");
21402 pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold");
21403 pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold");
21404 pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params");
21405 pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof");
21406 pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_test");
21407 pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_get_chmap");
21408 pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_state");
21409 pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_prepare");
21410 pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start");
21411 pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop");
21412 pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain");
21413 pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_reset");
21414 pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint");
21415 pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint");
21416 pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index");
21417 pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_free_hint");
21418 pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_begin");
21419 pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_commit");
21420 pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_recover");
21421 pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_readi");
21422 pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_writei");
21423 pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail");
21424 pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update");
21425 pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait");
21426 pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_nonblock");
21427 pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info");
21428 pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof");
21429 pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name");
21430 pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors");
21431 pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count");
21432 pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents");
21433 pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global");
21434#else
21435 /* The system below is just for type safety. */
21436 ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open;
21437 ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close;
21438 ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof;
21439 ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any;
21440 ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format;
21441 ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first;
21442 ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask;
21443 ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels;
21444 ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near;
21445 ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample;
21446 ma_snd_pcm_hw_params_set_rate_near _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate;
21447 ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near;
21448 ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax;
21449 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;
21450 ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near;
21451 ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access;
21452 ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format;
21453 ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels;
21454 ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min;
21455 ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max;
21456 ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate;
21457 ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min;
21458 ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max;
21459 ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size;
21460 ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods;
21461 ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access;
21462 ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format;
21463 ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels;
21464 ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate;
21465 ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params;
21466 ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof;
21467 ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current;
21468 ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary;
21469 ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min;
21470 ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold;
21471 ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold;
21472 ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params;
21473 ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof;
21474 ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test;
21475 ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap;
21476 ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state;
21477 ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare;
21478 ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start;
21479 ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop;
21480 ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain;
21481 ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset;
21482 ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint;
21483 ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint;
21484 ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index;
21485 ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint;
21486 ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin;
21487 ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit;
21488 ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover;
21489 ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi;
21490 ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei;
21491 ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail;
21492 ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update;
21493 ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait;
21494 ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock;
21495 ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info;
21496 ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof;
21497 ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name;
21498 ma_snd_pcm_poll_descriptors _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors;
21499 ma_snd_pcm_poll_descriptors_count _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count;
21500 ma_snd_pcm_poll_descriptors_revents _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents;
21501 ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global;
21502
21503 pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open;
21504 pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close;
21505 pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof;
21506 pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any;
21507 pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format;
21508 pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first;
21509 pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask;
21510 pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels;
21511 pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near;
21512 pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax;
21513 pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample;
21514 pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate;
21515 pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near;
21516 pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near;
21517 pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near;
21518 pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access;
21519 pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format;
21520 pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels;
21521 pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min;
21522 pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max;
21523 pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate;
21524 pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min;
21525 pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max;
21526 pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size;
21527 pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods;
21528 pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access;
21529 pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format;
21530 pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels;
21531 pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate;
21532 pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params;
21533 pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof;
21534 pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current;
21535 pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary;
21536 pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min;
21537 pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold;
21538 pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold;
21539 pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params;
21540 pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof;
21541 pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test;
21542 pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap;
21543 pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state;
21544 pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare;
21545 pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start;
21546 pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop;
21547 pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain;
21548 pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset;
21549 pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint;
21550 pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint;
21551 pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index;
21552 pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint;
21553 pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin;
21554 pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit;
21555 pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover;
21556 pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi;
21557 pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei;
21558 pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail;
21559 pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update;
21560 pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait;
21561 pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock;
21562 pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info;
21563 pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof;
21564 pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name;
21565 pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors;
21566 pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count;
21567 pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents;
21568 pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global;
21569#endif
21570
21571 pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration;
21572
21573 if (ma_mutex_init(&pContext->alsa.internalDeviceEnumLock) != MA_SUCCESS) {
21574 ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration.", MA_ERROR);
21575 }
21576
21577 pCallbacks->onContextInit = ma_context_init__alsa;
21578 pCallbacks->onContextUninit = ma_context_uninit__alsa;
21579 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa;
21580 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa;
21581 pCallbacks->onDeviceInit = ma_device_init__alsa;
21582 pCallbacks->onDeviceUninit = ma_device_uninit__alsa;
21583 pCallbacks->onDeviceStart = ma_device_start__alsa;
21584 pCallbacks->onDeviceStop = ma_device_stop__alsa;
21585 pCallbacks->onDeviceRead = ma_device_read__alsa;
21586 pCallbacks->onDeviceWrite = ma_device_write__alsa;
21587 pCallbacks->onDeviceDataLoop = NULL;
21588 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa;
21589
21590 return MA_SUCCESS;
21591}
21592#endif /* ALSA */
21593
21594
21595
21596/******************************************************************************
21597
21598PulseAudio Backend
21599
21600******************************************************************************/
21601#ifdef MA_HAS_PULSEAUDIO
21602/*
21603The 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
21604in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion.
21605
21606PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it
21607allows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it
21608appears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or
21609write functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the
21610simple API has some smarts to do this automatically, but I'm not sure. Another limitation with the simple API is that it seems inefficient
21611when you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API.
21612
21613Since 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
21614get fun, and I don't mean that in a good way...
21615
21616The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands
21617don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is
21618enabled 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
21619all of PulseAudio's problems stem from.
21620
21621When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own
21622vtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called
21623pa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop
21624because it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use
21625it to implement a worker thread which runs in a loop. The main loop is where operations are actually executed.
21626
21627To 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
21628to the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded
21629main loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely
21630specialized such as if you want to integrate it into your application's existing main loop infrastructure.
21631
21632(EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262.
21633It is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.)
21634
21635Once you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to
21636miniaudio'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
21637one `ma_context` to many `ma_device`s. Here's where it starts to get annoying, however. When you first create the PulseAudio context, which
21638is done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if
21639you remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()`
21640has 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
21641set 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.
21642All objects from here out are created from the context, and, I believe, you can't be creating these objects until the context is connected.
21643This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before
21644attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`.
21645
21646The reason for this asynchronous design is to support cases where you're connecting to a remote server, say through a local network or an
21647internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the
21648host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind.
21649
21650Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device.
21651The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call
21652`pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get
21653information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object
21654is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to
21655run 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
21656context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up.
21657All of that just to retrieve basic information about a device!
21658
21659Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the
21660context, 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
21661choices in PulseAudio.
21662
21663PulseAudio 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
21664because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for
21665writing 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
21666set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices
21667straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified,
21668PulseAudio will immediately fire it's write or read callback. This is *technically* correct (based on the wording in the documentation)
21669because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback
21670would 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
21671requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at
21672that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the
21673stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data
21674callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio
21675doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been
21676started. The device state is used for this - if the state is anything other than `MA_STATE_STARTING` or `MA_STATE_STARTED`, the main data
21677callback is not fired.
21678
21679This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will
21680continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device
21681is running, and will be fired regardless of whether or not the user actually wrote anything to the device/stream. This not the case in
21682PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call
21683`pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always
21684writing 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
21685you're continuously writing data to the stream, the stream will never get drained! That means in order to drain the stream, you need to
21686*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
21687important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained
21688before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write
21689data 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!
21690
21691This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not*
21692write 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
21693resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This
21694disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the
21695callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.)
21696
21697Once you've created the stream, you need to connect it which involves the whole waiting procedure. This is the same process as the context,
21698only this time you'll poll for the state with `pa_stream_get_status()`. The starting and stopping of a streaming is referred to as
21699"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
21700it'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
21701guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is
21702absolutely beyond me. Would it really be that hard to just make it run synchronously?
21703
21704Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that
21705they were initialized in.
21706
21707That'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
21708embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to
21709run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche
21710requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is
21711constantly fired, regardless of whether or not `pa_stream_write()` has been called, and it needs to take a pointer to a buffer as a
21712parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These
21713changes alone will change PulseAudio from one of the worst audio APIs to one of the best.
21714*/
21715
21716
21717/*
21718It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header
21719to check for type safety. We cannot do this when linking at run time because the header might not be available.
21720*/
21721#ifdef MA_NO_RUNTIME_LINKING
21722
21723/* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */
21724#if !defined(__cplusplus)
21725 #if defined(__STRICT_ANSI__)
21726 #if !defined(inline)
21727 #define inline __inline__ __attribute__((always_inline))
21728 #define MA_INLINE_DEFINED
21729 #endif
21730 #endif
21731#endif
21732#include <pulse/pulseaudio.h>
21733#if defined(MA_INLINE_DEFINED)
21734 #undef inline
21735 #undef MA_INLINE_DEFINED
21736#endif
21737
21738#define MA_PA_OK PA_OK
21739#define MA_PA_ERR_ACCESS PA_ERR_ACCESS
21740#define MA_PA_ERR_INVALID PA_ERR_INVALID
21741#define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY
21742#define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED
21743
21744#define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX
21745#define MA_PA_RATE_MAX PA_RATE_MAX
21746
21747typedef pa_context_flags_t ma_pa_context_flags_t;
21748#define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS
21749#define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN
21750#define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL
21751
21752typedef pa_stream_flags_t ma_pa_stream_flags_t;
21753#define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS
21754#define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED
21755#define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING
21756#define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC
21757#define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE
21758#define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS
21759#define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS
21760#define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT
21761#define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE
21762#define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS
21763#define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE
21764#define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE
21765#define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT
21766#define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED
21767#define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY
21768#define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS
21769#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
21770#define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
21771#define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
21772#define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
21773#define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
21774
21775typedef pa_sink_flags_t ma_pa_sink_flags_t;
21776#define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS
21777#define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL
21778#define MA_PA_SINK_LATENCY PA_SINK_LATENCY
21779#define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE
21780#define MA_PA_SINK_NETWORK PA_SINK_NETWORK
21781#define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL
21782#define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
21783#define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
21784#define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY
21785#define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS
21786
21787typedef pa_source_flags_t ma_pa_source_flags_t;
21788#define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS
21789#define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL
21790#define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY
21791#define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE
21792#define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK
21793#define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL
21794#define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME
21795#define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY
21796#define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME
21797
21798typedef pa_context_state_t ma_pa_context_state_t;
21799#define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED
21800#define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING
21801#define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING
21802#define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME
21803#define MA_PA_CONTEXT_READY PA_CONTEXT_READY
21804#define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED
21805#define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED
21806
21807typedef pa_stream_state_t ma_pa_stream_state_t;
21808#define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED
21809#define MA_PA_STREAM_CREATING PA_STREAM_CREATING
21810#define MA_PA_STREAM_READY PA_STREAM_READY
21811#define MA_PA_STREAM_FAILED PA_STREAM_FAILED
21812#define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED
21813
21814typedef pa_operation_state_t ma_pa_operation_state_t;
21815#define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING
21816#define MA_PA_OPERATION_DONE PA_OPERATION_DONE
21817#define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED
21818
21819typedef pa_sink_state_t ma_pa_sink_state_t;
21820#define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE
21821#define MA_PA_SINK_RUNNING PA_SINK_RUNNING
21822#define MA_PA_SINK_IDLE PA_SINK_IDLE
21823#define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED
21824
21825typedef pa_source_state_t ma_pa_source_state_t;
21826#define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE
21827#define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING
21828#define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE
21829#define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED
21830
21831typedef pa_seek_mode_t ma_pa_seek_mode_t;
21832#define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE
21833#define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE
21834#define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ
21835#define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END
21836
21837typedef pa_channel_position_t ma_pa_channel_position_t;
21838#define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID
21839#define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO
21840#define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT
21841#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT
21842#define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER
21843#define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER
21844#define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT
21845#define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT
21846#define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE
21847#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
21848#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
21849#define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT
21850#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT
21851#define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0
21852#define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1
21853#define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2
21854#define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3
21855#define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4
21856#define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5
21857#define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6
21858#define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7
21859#define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8
21860#define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9
21861#define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10
21862#define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11
21863#define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12
21864#define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13
21865#define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14
21866#define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15
21867#define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16
21868#define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17
21869#define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18
21870#define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19
21871#define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20
21872#define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21
21873#define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22
21874#define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23
21875#define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24
21876#define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25
21877#define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26
21878#define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27
21879#define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28
21880#define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29
21881#define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30
21882#define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31
21883#define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER
21884#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT
21885#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
21886#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER
21887#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT
21888#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT
21889#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER
21890#define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT
21891#define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT
21892#define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER
21893#define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER
21894
21895typedef pa_channel_map_def_t ma_pa_channel_map_def_t;
21896#define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF
21897#define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA
21898#define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX
21899#define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX
21900#define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS
21901#define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT
21902
21903typedef pa_sample_format_t ma_pa_sample_format_t;
21904#define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID
21905#define MA_PA_SAMPLE_U8 PA_SAMPLE_U8
21906#define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW
21907#define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW
21908#define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE
21909#define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE
21910#define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE
21911#define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE
21912#define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE
21913#define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE
21914#define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE
21915#define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE
21916#define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE
21917#define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE
21918
21919typedef pa_mainloop ma_pa_mainloop;
21920typedef pa_threaded_mainloop ma_pa_threaded_mainloop;
21921typedef pa_mainloop_api ma_pa_mainloop_api;
21922typedef pa_context ma_pa_context;
21923typedef pa_operation ma_pa_operation;
21924typedef pa_stream ma_pa_stream;
21925typedef pa_spawn_api ma_pa_spawn_api;
21926typedef pa_buffer_attr ma_pa_buffer_attr;
21927typedef pa_channel_map ma_pa_channel_map;
21928typedef pa_cvolume ma_pa_cvolume;
21929typedef pa_sample_spec ma_pa_sample_spec;
21930typedef pa_sink_info ma_pa_sink_info;
21931typedef pa_source_info ma_pa_source_info;
21932
21933typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t;
21934typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t;
21935typedef pa_source_info_cb_t ma_pa_source_info_cb_t;
21936typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t;
21937typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t;
21938typedef pa_stream_notify_cb_t ma_pa_stream_notify_cb_t;
21939typedef pa_free_cb_t ma_pa_free_cb_t;
21940#else
21941#define MA_PA_OK 0
21942#define MA_PA_ERR_ACCESS 1
21943#define MA_PA_ERR_INVALID 2
21944#define MA_PA_ERR_NOENTITY 5
21945#define MA_PA_ERR_NOTSUPPORTED 19
21946
21947#define MA_PA_CHANNELS_MAX 32
21948#define MA_PA_RATE_MAX 384000
21949
21950typedef int ma_pa_context_flags_t;
21951#define MA_PA_CONTEXT_NOFLAGS 0x00000000
21952#define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001
21953#define MA_PA_CONTEXT_NOFAIL 0x00000002
21954
21955typedef int ma_pa_stream_flags_t;
21956#define MA_PA_STREAM_NOFLAGS 0x00000000
21957#define MA_PA_STREAM_START_CORKED 0x00000001
21958#define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002
21959#define MA_PA_STREAM_NOT_MONOTONIC 0x00000004
21960#define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008
21961#define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010
21962#define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020
21963#define MA_PA_STREAM_FIX_FORMAT 0x00000040
21964#define MA_PA_STREAM_FIX_RATE 0x00000080
21965#define MA_PA_STREAM_FIX_CHANNELS 0x00000100
21966#define MA_PA_STREAM_DONT_MOVE 0x00000200
21967#define MA_PA_STREAM_VARIABLE_RATE 0x00000400
21968#define MA_PA_STREAM_PEAK_DETECT 0x00000800
21969#define MA_PA_STREAM_START_MUTED 0x00001000
21970#define MA_PA_STREAM_ADJUST_LATENCY 0x00002000
21971#define MA_PA_STREAM_EARLY_REQUESTS 0x00004000
21972#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000
21973#define MA_PA_STREAM_START_UNMUTED 0x00010000
21974#define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000
21975#define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000
21976#define MA_PA_STREAM_PASSTHROUGH 0x00080000
21977
21978typedef int ma_pa_sink_flags_t;
21979#define MA_PA_SINK_NOFLAGS 0x00000000
21980#define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001
21981#define MA_PA_SINK_LATENCY 0x00000002
21982#define MA_PA_SINK_HARDWARE 0x00000004
21983#define MA_PA_SINK_NETWORK 0x00000008
21984#define MA_PA_SINK_HW_MUTE_CTRL 0x00000010
21985#define MA_PA_SINK_DECIBEL_VOLUME 0x00000020
21986#define MA_PA_SINK_FLAT_VOLUME 0x00000040
21987#define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080
21988#define MA_PA_SINK_SET_FORMATS 0x00000100
21989
21990typedef int ma_pa_source_flags_t;
21991#define MA_PA_SOURCE_NOFLAGS 0x00000000
21992#define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001
21993#define MA_PA_SOURCE_LATENCY 0x00000002
21994#define MA_PA_SOURCE_HARDWARE 0x00000004
21995#define MA_PA_SOURCE_NETWORK 0x00000008
21996#define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010
21997#define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020
21998#define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040
21999#define MA_PA_SOURCE_FLAT_VOLUME 0x00000080
22000
22001typedef int ma_pa_context_state_t;
22002#define MA_PA_CONTEXT_UNCONNECTED 0
22003#define MA_PA_CONTEXT_CONNECTING 1
22004#define MA_PA_CONTEXT_AUTHORIZING 2
22005#define MA_PA_CONTEXT_SETTING_NAME 3
22006#define MA_PA_CONTEXT_READY 4
22007#define MA_PA_CONTEXT_FAILED 5
22008#define MA_PA_CONTEXT_TERMINATED 6
22009
22010typedef int ma_pa_stream_state_t;
22011#define MA_PA_STREAM_UNCONNECTED 0
22012#define MA_PA_STREAM_CREATING 1
22013#define MA_PA_STREAM_READY 2
22014#define MA_PA_STREAM_FAILED 3
22015#define MA_PA_STREAM_TERMINATED 4
22016
22017typedef int ma_pa_operation_state_t;
22018#define MA_PA_OPERATION_RUNNING 0
22019#define MA_PA_OPERATION_DONE 1
22020#define MA_PA_OPERATION_CANCELLED 2
22021
22022typedef int ma_pa_sink_state_t;
22023#define MA_PA_SINK_INVALID_STATE -1
22024#define MA_PA_SINK_RUNNING 0
22025#define MA_PA_SINK_IDLE 1
22026#define MA_PA_SINK_SUSPENDED 2
22027
22028typedef int ma_pa_source_state_t;
22029#define MA_PA_SOURCE_INVALID_STATE -1
22030#define MA_PA_SOURCE_RUNNING 0
22031#define MA_PA_SOURCE_IDLE 1
22032#define MA_PA_SOURCE_SUSPENDED 2
22033
22034typedef int ma_pa_seek_mode_t;
22035#define MA_PA_SEEK_RELATIVE 0
22036#define MA_PA_SEEK_ABSOLUTE 1
22037#define MA_PA_SEEK_RELATIVE_ON_READ 2
22038#define MA_PA_SEEK_RELATIVE_END 3
22039
22040typedef int ma_pa_channel_position_t;
22041#define MA_PA_CHANNEL_POSITION_INVALID -1
22042#define MA_PA_CHANNEL_POSITION_MONO 0
22043#define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1
22044#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2
22045#define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3
22046#define MA_PA_CHANNEL_POSITION_REAR_CENTER 4
22047#define MA_PA_CHANNEL_POSITION_REAR_LEFT 5
22048#define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6
22049#define MA_PA_CHANNEL_POSITION_LFE 7
22050#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8
22051#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9
22052#define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10
22053#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11
22054#define MA_PA_CHANNEL_POSITION_AUX0 12
22055#define MA_PA_CHANNEL_POSITION_AUX1 13
22056#define MA_PA_CHANNEL_POSITION_AUX2 14
22057#define MA_PA_CHANNEL_POSITION_AUX3 15
22058#define MA_PA_CHANNEL_POSITION_AUX4 16
22059#define MA_PA_CHANNEL_POSITION_AUX5 17
22060#define MA_PA_CHANNEL_POSITION_AUX6 18
22061#define MA_PA_CHANNEL_POSITION_AUX7 19
22062#define MA_PA_CHANNEL_POSITION_AUX8 20
22063#define MA_PA_CHANNEL_POSITION_AUX9 21
22064#define MA_PA_CHANNEL_POSITION_AUX10 22
22065#define MA_PA_CHANNEL_POSITION_AUX11 23
22066#define MA_PA_CHANNEL_POSITION_AUX12 24
22067#define MA_PA_CHANNEL_POSITION_AUX13 25
22068#define MA_PA_CHANNEL_POSITION_AUX14 26
22069#define MA_PA_CHANNEL_POSITION_AUX15 27
22070#define MA_PA_CHANNEL_POSITION_AUX16 28
22071#define MA_PA_CHANNEL_POSITION_AUX17 29
22072#define MA_PA_CHANNEL_POSITION_AUX18 30
22073#define MA_PA_CHANNEL_POSITION_AUX19 31
22074#define MA_PA_CHANNEL_POSITION_AUX20 32
22075#define MA_PA_CHANNEL_POSITION_AUX21 33
22076#define MA_PA_CHANNEL_POSITION_AUX22 34
22077#define MA_PA_CHANNEL_POSITION_AUX23 35
22078#define MA_PA_CHANNEL_POSITION_AUX24 36
22079#define MA_PA_CHANNEL_POSITION_AUX25 37
22080#define MA_PA_CHANNEL_POSITION_AUX26 38
22081#define MA_PA_CHANNEL_POSITION_AUX27 39
22082#define MA_PA_CHANNEL_POSITION_AUX28 40
22083#define MA_PA_CHANNEL_POSITION_AUX29 41
22084#define MA_PA_CHANNEL_POSITION_AUX30 42
22085#define MA_PA_CHANNEL_POSITION_AUX31 43
22086#define MA_PA_CHANNEL_POSITION_TOP_CENTER 44
22087#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45
22088#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46
22089#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47
22090#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48
22091#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49
22092#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50
22093#define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT
22094#define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT
22095#define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER
22096#define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE
22097
22098typedef int ma_pa_channel_map_def_t;
22099#define MA_PA_CHANNEL_MAP_AIFF 0
22100#define MA_PA_CHANNEL_MAP_ALSA 1
22101#define MA_PA_CHANNEL_MAP_AUX 2
22102#define MA_PA_CHANNEL_MAP_WAVEEX 3
22103#define MA_PA_CHANNEL_MAP_OSS 4
22104#define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF
22105
22106typedef int ma_pa_sample_format_t;
22107#define MA_PA_SAMPLE_INVALID -1
22108#define MA_PA_SAMPLE_U8 0
22109#define MA_PA_SAMPLE_ALAW 1
22110#define MA_PA_SAMPLE_ULAW 2
22111#define MA_PA_SAMPLE_S16LE 3
22112#define MA_PA_SAMPLE_S16BE 4
22113#define MA_PA_SAMPLE_FLOAT32LE 5
22114#define MA_PA_SAMPLE_FLOAT32BE 6
22115#define MA_PA_SAMPLE_S32LE 7
22116#define MA_PA_SAMPLE_S32BE 8
22117#define MA_PA_SAMPLE_S24LE 9
22118#define MA_PA_SAMPLE_S24BE 10
22119#define MA_PA_SAMPLE_S24_32LE 11
22120#define MA_PA_SAMPLE_S24_32BE 12
22121
22122typedef struct ma_pa_mainloop ma_pa_mainloop;
22123typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop;
22124typedef struct ma_pa_mainloop_api ma_pa_mainloop_api;
22125typedef struct ma_pa_context ma_pa_context;
22126typedef struct ma_pa_operation ma_pa_operation;
22127typedef struct ma_pa_stream ma_pa_stream;
22128typedef struct ma_pa_spawn_api ma_pa_spawn_api;
22129
22130typedef struct
22131{
22132 ma_uint32 maxlength;
22133 ma_uint32 tlength;
22134 ma_uint32 prebuf;
22135 ma_uint32 minreq;
22136 ma_uint32 fragsize;
22137} ma_pa_buffer_attr;
22138
22139typedef struct
22140{
22141 ma_uint8 channels;
22142 ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX];
22143} ma_pa_channel_map;
22144
22145typedef struct
22146{
22147 ma_uint8 channels;
22148 ma_uint32 values[MA_PA_CHANNELS_MAX];
22149} ma_pa_cvolume;
22150
22151typedef struct
22152{
22153 ma_pa_sample_format_t format;
22154 ma_uint32 rate;
22155 ma_uint8 channels;
22156} ma_pa_sample_spec;
22157
22158typedef struct
22159{
22160 const char* name;
22161 ma_uint32 index;
22162 const char* description;
22163 ma_pa_sample_spec sample_spec;
22164 ma_pa_channel_map channel_map;
22165 ma_uint32 owner_module;
22166 ma_pa_cvolume volume;
22167 int mute;
22168 ma_uint32 monitor_source;
22169 const char* monitor_source_name;
22170 ma_uint64 latency;
22171 const char* driver;
22172 ma_pa_sink_flags_t flags;
22173 void* proplist;
22174 ma_uint64 configured_latency;
22175 ma_uint32 base_volume;
22176 ma_pa_sink_state_t state;
22177 ma_uint32 n_volume_steps;
22178 ma_uint32 card;
22179 ma_uint32 n_ports;
22180 void** ports;
22181 void* active_port;
22182 ma_uint8 n_formats;
22183 void** formats;
22184} ma_pa_sink_info;
22185
22186typedef struct
22187{
22188 const char *name;
22189 ma_uint32 index;
22190 const char *description;
22191 ma_pa_sample_spec sample_spec;
22192 ma_pa_channel_map channel_map;
22193 ma_uint32 owner_module;
22194 ma_pa_cvolume volume;
22195 int mute;
22196 ma_uint32 monitor_of_sink;
22197 const char *monitor_of_sink_name;
22198 ma_uint64 latency;
22199 const char *driver;
22200 ma_pa_source_flags_t flags;
22201 void* proplist;
22202 ma_uint64 configured_latency;
22203 ma_uint32 base_volume;
22204 ma_pa_source_state_t state;
22205 ma_uint32 n_volume_steps;
22206 ma_uint32 card;
22207 ma_uint32 n_ports;
22208 void** ports;
22209 void* active_port;
22210 ma_uint8 n_formats;
22211 void** formats;
22212} ma_pa_source_info;
22213
22214typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata);
22215typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata);
22216typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata);
22217typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata);
22218typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata);
22219typedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata);
22220typedef void (* ma_pa_free_cb_t) (void* p);
22221#endif
22222
22223
22224typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void);
22225typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m);
22226typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval);
22227typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m);
22228typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval);
22229typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m);
22230typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void);
22231typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m);
22232typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m);
22233typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m);
22234typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m);
22235typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m);
22236typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m);
22237typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept);
22238typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m);
22239typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m);
22240typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m);
22241typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m);
22242typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name);
22243typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name);
22244typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c);
22245typedef 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);
22246typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c);
22247typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata);
22248typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c);
22249typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata);
22250typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata);
22251typedef 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);
22252typedef 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);
22253typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o);
22254typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o);
22255typedef 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);
22256typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m);
22257typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss);
22258typedef 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);
22259typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s);
22260typedef 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);
22261typedef 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);
22262typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s);
22263typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s);
22264typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s);
22265typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s);
22266typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s);
22267typedef 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);
22268typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s);
22269typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
22270typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
22271typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);
22272typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s);
22273typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
22274typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
22275typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s);
22276typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata);
22277typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
22278typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes);
22279typedef 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);
22280typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes);
22281typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s);
22282typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s);
22283typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s);
22284
22285typedef struct
22286{
22287 ma_uint32 count;
22288 ma_uint32 capacity;
22289 ma_device_info* pInfo;
22290} ma_pulse_device_enum_data;
22291
22292static ma_result ma_result_from_pulse(int result)
22293{
22294 if (result < 0) {
22295 return MA_ERROR;
22296 }
22297
22298 switch (result) {
22299 case MA_PA_OK: return MA_SUCCESS;
22300 case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED;
22301 case MA_PA_ERR_INVALID: return MA_INVALID_ARGS;
22302 case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE;
22303 default: return MA_ERROR;
22304 }
22305}
22306
22307#if 0
22308static ma_pa_sample_format_t ma_format_to_pulse(ma_format format)
22309{
22310 if (ma_is_little_endian()) {
22311 switch (format) {
22312 case ma_format_s16: return MA_PA_SAMPLE_S16LE;
22313 case ma_format_s24: return MA_PA_SAMPLE_S24LE;
22314 case ma_format_s32: return MA_PA_SAMPLE_S32LE;
22315 case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE;
22316 default: break;
22317 }
22318 } else {
22319 switch (format) {
22320 case ma_format_s16: return MA_PA_SAMPLE_S16BE;
22321 case ma_format_s24: return MA_PA_SAMPLE_S24BE;
22322 case ma_format_s32: return MA_PA_SAMPLE_S32BE;
22323 case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE;
22324 default: break;
22325 }
22326 }
22327
22328 /* Endian agnostic. */
22329 switch (format) {
22330 case ma_format_u8: return MA_PA_SAMPLE_U8;
22331 default: return MA_PA_SAMPLE_INVALID;
22332 }
22333}
22334#endif
22335
22336static ma_format ma_format_from_pulse(ma_pa_sample_format_t format)
22337{
22338 if (ma_is_little_endian()) {
22339 switch (format) {
22340 case MA_PA_SAMPLE_S16LE: return ma_format_s16;
22341 case MA_PA_SAMPLE_S24LE: return ma_format_s24;
22342 case MA_PA_SAMPLE_S32LE: return ma_format_s32;
22343 case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32;
22344 default: break;
22345 }
22346 } else {
22347 switch (format) {
22348 case MA_PA_SAMPLE_S16BE: return ma_format_s16;
22349 case MA_PA_SAMPLE_S24BE: return ma_format_s24;
22350 case MA_PA_SAMPLE_S32BE: return ma_format_s32;
22351 case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32;
22352 default: break;
22353 }
22354 }
22355
22356 /* Endian agnostic. */
22357 switch (format) {
22358 case MA_PA_SAMPLE_U8: return ma_format_u8;
22359 default: return ma_format_unknown;
22360 }
22361}
22362
22363static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position)
22364{
22365 switch (position)
22366 {
22367 case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE;
22368 case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO;
22369 case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
22370 case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
22371 case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
22372 case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER;
22373 case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT;
22374 case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT;
22375 case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE;
22376 case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
22377 case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
22378 case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
22379 case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
22380 case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0;
22381 case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1;
22382 case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2;
22383 case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3;
22384 case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4;
22385 case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5;
22386 case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6;
22387 case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7;
22388 case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8;
22389 case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9;
22390 case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10;
22391 case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11;
22392 case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12;
22393 case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13;
22394 case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14;
22395 case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15;
22396 case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16;
22397 case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17;
22398 case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18;
22399 case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19;
22400 case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20;
22401 case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21;
22402 case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22;
22403 case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23;
22404 case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24;
22405 case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25;
22406 case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26;
22407 case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27;
22408 case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28;
22409 case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29;
22410 case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30;
22411 case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31;
22412 case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
22413 case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
22414 case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
22415 case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
22416 case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
22417 case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
22418 case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
22419 default: return MA_CHANNEL_NONE;
22420 }
22421}
22422
22423#if 0
22424static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position)
22425{
22426 switch (position)
22427 {
22428 case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID;
22429 case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT;
22430 case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT;
22431 case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER;
22432 case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE;
22433 case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT;
22434 case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT;
22435 case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
22436 case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
22437 case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER;
22438 case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT;
22439 case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT;
22440 case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER;
22441 case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
22442 case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
22443 case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
22444 case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT;
22445 case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER;
22446 case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
22447 case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18;
22448 case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19;
22449 case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20;
22450 case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21;
22451 case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22;
22452 case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23;
22453 case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24;
22454 case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25;
22455 case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26;
22456 case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27;
22457 case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28;
22458 case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29;
22459 case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30;
22460 case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31;
22461 default: return (ma_pa_channel_position_t)position;
22462 }
22463}
22464#endif
22465
22466static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_operation* pOP)
22467{
22468 int resultPA;
22469 ma_pa_operation_state_t state;
22470
22471 MA_ASSERT(pContext != NULL);
22472 MA_ASSERT(pOP != NULL);
22473
22474 for (;;) {
22475 state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP);
22476 if (state != MA_PA_OPERATION_RUNNING) {
22477 break; /* Done. */
22478 }
22479
22480 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pContext->pulse.pMainLoop, 1, NULL);
22481 if (resultPA < 0) {
22482 return ma_result_from_pulse(resultPA);
22483 }
22484 }
22485
22486 return MA_SUCCESS;
22487}
22488
22489static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_pa_operation* pOP)
22490{
22491 ma_result result;
22492
22493 if (pOP == NULL) {
22494 return MA_INVALID_ARGS;
22495 }
22496
22497 result = ma_wait_for_operation__pulse(pContext, pOP);
22498 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
22499
22500 return result;
22501}
22502
22503static ma_result ma_context_wait_for_pa_context_to_connect__pulse(ma_context* pContext)
22504{
22505 int resultPA;
22506 ma_pa_context_state_t state;
22507
22508 for (;;) {
22509 state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pContext->pulse.pPulseContext);
22510 if (state == MA_PA_CONTEXT_READY) {
22511 break; /* Done. */
22512 }
22513
22514 if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
22515 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context.", MA_ERROR);
22516 }
22517
22518 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pContext->pulse.pMainLoop, 1, NULL);
22519 if (resultPA < 0) {
22520 return ma_result_from_pulse(resultPA);
22521 }
22522 }
22523
22524 /* Should never get here. */
22525 return MA_SUCCESS;
22526}
22527
22528static ma_result ma_context_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_pa_stream* pStream)
22529{
22530 int resultPA;
22531 ma_pa_stream_state_t state;
22532
22533 for (;;) {
22534 state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)(pStream);
22535 if (state == MA_PA_STREAM_READY) {
22536 break; /* Done. */
22537 }
22538
22539 if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) {
22540 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream.", MA_ERROR);
22541 }
22542
22543 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pContext->pulse.pMainLoop, 1, NULL);
22544 if (resultPA < 0) {
22545 return ma_result_from_pulse(resultPA);
22546 }
22547 }
22548
22549 return MA_SUCCESS;
22550}
22551
22552
22553static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
22554{
22555 ma_pa_sink_info* pInfoOut;
22556
22557 if (endOfList > 0) {
22558 return;
22559 }
22560
22561 pInfoOut = (ma_pa_sink_info*)pUserData;
22562 MA_ASSERT(pInfoOut != NULL);
22563
22564 *pInfoOut = *pInfo;
22565
22566 (void)pPulseContext; /* Unused. */
22567}
22568
22569static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
22570{
22571 ma_pa_source_info* pInfoOut;
22572
22573 if (endOfList > 0) {
22574 return;
22575 }
22576
22577 pInfoOut = (ma_pa_source_info*)pUserData;
22578 MA_ASSERT(pInfoOut != NULL);
22579
22580 *pInfoOut = *pInfo;
22581
22582 (void)pPulseContext; /* Unused. */
22583}
22584
22585static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
22586{
22587 ma_device* pDevice;
22588
22589 if (endOfList > 0) {
22590 return;
22591 }
22592
22593 pDevice = (ma_device*)pUserData;
22594 MA_ASSERT(pDevice != NULL);
22595
22596 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1);
22597
22598 (void)pPulseContext; /* Unused. */
22599}
22600
22601static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
22602{
22603 ma_device* pDevice;
22604
22605 if (endOfList > 0) {
22606 return;
22607 }
22608
22609 pDevice = (ma_device*)pUserData;
22610 MA_ASSERT(pDevice != NULL);
22611
22612 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1);
22613
22614 (void)pPulseContext; /* Unused. */
22615}
22616
22617
22618static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo)
22619{
22620 ma_pa_operation* pOP;
22621
22622 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);
22623 if (pOP == NULL) {
22624 return MA_ERROR;
22625 }
22626
22627 return ma_wait_for_operation_and_unref__pulse(pContext, pOP);
22628}
22629
22630static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo)
22631{
22632 ma_pa_operation* pOP;
22633
22634 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);
22635 if (pOP == NULL) {
22636 return MA_ERROR;
22637 }
22638
22639 return ma_wait_for_operation_and_unref__pulse(pContext, pOP);;
22640}
22641
22642static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex)
22643{
22644 ma_result result;
22645
22646 MA_ASSERT(pContext != NULL);
22647 MA_ASSERT(pIndex != NULL);
22648
22649 if (pIndex != NULL) {
22650 *pIndex = (ma_uint32)-1;
22651 }
22652
22653 if (deviceType == ma_device_type_playback) {
22654 ma_pa_sink_info sinkInfo;
22655 result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo);
22656 if (result != MA_SUCCESS) {
22657 return result;
22658 }
22659
22660 if (pIndex != NULL) {
22661 *pIndex = sinkInfo.index;
22662 }
22663 }
22664
22665 if (deviceType == ma_device_type_capture) {
22666 ma_pa_source_info sourceInfo;
22667 result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo);
22668 if (result != MA_SUCCESS) {
22669 return result;
22670 }
22671
22672 if (pIndex != NULL) {
22673 *pIndex = sourceInfo.index;
22674 }
22675 }
22676
22677 return MA_SUCCESS;
22678}
22679
22680
22681typedef struct
22682{
22683 ma_context* pContext;
22684 ma_enum_devices_callback_proc callback;
22685 void* pUserData;
22686 ma_bool32 isTerminated;
22687 ma_uint32 defaultDeviceIndexPlayback;
22688 ma_uint32 defaultDeviceIndexCapture;
22689} ma_context_enumerate_devices_callback_data__pulse;
22690
22691static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData)
22692{
22693 ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
22694 ma_device_info deviceInfo;
22695
22696 MA_ASSERT(pData != NULL);
22697
22698 if (endOfList || pData->isTerminated) {
22699 return;
22700 }
22701
22702 MA_ZERO_OBJECT(&deviceInfo);
22703
22704 /* The name from PulseAudio is the ID for miniaudio. */
22705 if (pSinkInfo->name != NULL) {
22706 ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1);
22707 }
22708
22709 /* The description from PulseAudio is the name for miniaudio. */
22710 if (pSinkInfo->description != NULL) {
22711 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1);
22712 }
22713
22714 if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) {
22715 deviceInfo.isDefault = MA_TRUE;
22716 }
22717
22718 pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData);
22719
22720 (void)pPulseContext; /* Unused. */
22721}
22722
22723static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData)
22724{
22725 ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
22726 ma_device_info deviceInfo;
22727
22728 MA_ASSERT(pData != NULL);
22729
22730 if (endOfList || pData->isTerminated) {
22731 return;
22732 }
22733
22734 MA_ZERO_OBJECT(&deviceInfo);
22735
22736 /* The name from PulseAudio is the ID for miniaudio. */
22737 if (pSourceInfo->name != NULL) {
22738 ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1);
22739 }
22740
22741 /* The description from PulseAudio is the name for miniaudio. */
22742 if (pSourceInfo->description != NULL) {
22743 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1);
22744 }
22745
22746 if (pSourceInfo->index == pData->defaultDeviceIndexCapture) {
22747 deviceInfo.isDefault = MA_TRUE;
22748 }
22749
22750 pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData);
22751
22752 (void)pPulseContext; /* Unused. */
22753}
22754
22755static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
22756{
22757 ma_result result = MA_SUCCESS;
22758 ma_context_enumerate_devices_callback_data__pulse callbackData;
22759 ma_pa_operation* pOP = NULL;
22760
22761 MA_ASSERT(pContext != NULL);
22762 MA_ASSERT(callback != NULL);
22763
22764 callbackData.pContext = pContext;
22765 callbackData.callback = callback;
22766 callbackData.pUserData = pUserData;
22767 callbackData.isTerminated = MA_FALSE;
22768 callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1;
22769 callbackData.defaultDeviceIndexCapture = (ma_uint32)-1;
22770
22771 /* We need to get the index of the default devices. */
22772 ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback);
22773 ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture);
22774
22775 /* Playback. */
22776 if (!callbackData.isTerminated) {
22777 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);
22778 if (pOP == NULL) {
22779 result = MA_ERROR;
22780 goto done;
22781 }
22782
22783 result = ma_wait_for_operation__pulse(pContext, pOP);
22784 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
22785
22786 if (result != MA_SUCCESS) {
22787 goto done;
22788 }
22789 }
22790
22791
22792 /* Capture. */
22793 if (!callbackData.isTerminated) {
22794 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);
22795 if (pOP == NULL) {
22796 result = MA_ERROR;
22797 goto done;
22798 }
22799
22800 result = ma_wait_for_operation__pulse(pContext, pOP);
22801 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
22802
22803 if (result != MA_SUCCESS) {
22804 goto done;
22805 }
22806 }
22807
22808done:
22809 return result;
22810}
22811
22812
22813typedef struct
22814{
22815 ma_device_info* pDeviceInfo;
22816 ma_uint32 defaultDeviceIndex;
22817 ma_bool32 foundDevice;
22818} ma_context_get_device_info_callback_data__pulse;
22819
22820static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
22821{
22822 ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
22823
22824 if (endOfList > 0) {
22825 return;
22826 }
22827
22828 MA_ASSERT(pData != NULL);
22829 pData->foundDevice = MA_TRUE;
22830
22831 if (pInfo->name != NULL) {
22832 ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
22833 }
22834
22835 if (pInfo->description != NULL) {
22836 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
22837 }
22838
22839 /*
22840 We're just reporting a single data format here. I think technically PulseAudio might support
22841 all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to
22842 report the "native" device format.
22843 */
22844 pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format);
22845 pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels;
22846 pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate;
22847 pData->pDeviceInfo->nativeDataFormats[0].flags = 0;
22848 pData->pDeviceInfo->nativeDataFormatCount = 1;
22849
22850 if (pData->defaultDeviceIndex == pInfo->index) {
22851 pData->pDeviceInfo->isDefault = MA_TRUE;
22852 }
22853
22854 (void)pPulseContext; /* Unused. */
22855}
22856
22857static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
22858{
22859 ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
22860
22861 if (endOfList > 0) {
22862 return;
22863 }
22864
22865 MA_ASSERT(pData != NULL);
22866 pData->foundDevice = MA_TRUE;
22867
22868 if (pInfo->name != NULL) {
22869 ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
22870 }
22871
22872 if (pInfo->description != NULL) {
22873 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
22874 }
22875
22876 /*
22877 We're just reporting a single data format here. I think technically PulseAudio might support
22878 all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to
22879 report the "native" device format.
22880 */
22881 pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format);
22882 pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels;
22883 pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate;
22884 pData->pDeviceInfo->nativeDataFormats[0].flags = 0;
22885 pData->pDeviceInfo->nativeDataFormatCount = 1;
22886
22887 if (pData->defaultDeviceIndex == pInfo->index) {
22888 pData->pDeviceInfo->isDefault = MA_TRUE;
22889 }
22890
22891 (void)pPulseContext; /* Unused. */
22892}
22893
22894static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
22895{
22896 ma_result result = MA_SUCCESS;
22897 ma_context_get_device_info_callback_data__pulse callbackData;
22898 ma_pa_operation* pOP = NULL;
22899 const char* pDeviceName = NULL;
22900
22901 MA_ASSERT(pContext != NULL);
22902
22903 callbackData.pDeviceInfo = pDeviceInfo;
22904 callbackData.foundDevice = MA_FALSE;
22905
22906 if (pDeviceID != NULL) {
22907 pDeviceName = pDeviceID->pulse;
22908 } else {
22909 pDeviceName = NULL;
22910 }
22911
22912 result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex);
22913
22914 if (deviceType == ma_device_type_playback) {
22915 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);
22916 } else {
22917 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);
22918 }
22919
22920 if (pOP != NULL) {
22921 ma_wait_for_operation_and_unref__pulse(pContext, pOP);
22922 } else {
22923 result = MA_ERROR;
22924 goto done;
22925 }
22926
22927 if (!callbackData.foundDevice) {
22928 result = MA_NO_DEVICE;
22929 goto done;
22930 }
22931
22932done:
22933 return result;
22934}
22935
22936static ma_result ma_device_uninit__pulse(ma_device* pDevice)
22937{
22938 ma_context* pContext;
22939
22940 MA_ASSERT(pDevice != NULL);
22941
22942 pContext = pDevice->pContext;
22943 MA_ASSERT(pContext != NULL);
22944
22945 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
22946 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
22947 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
22948 }
22949
22950 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
22951 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
22952 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
22953 }
22954
22955 if (pDevice->type == ma_device_type_duplex) {
22956 ma_duplex_rb_uninit(&pDevice->duplexRB);
22957 }
22958
22959 return MA_SUCCESS;
22960}
22961
22962static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss)
22963{
22964 ma_pa_buffer_attr attr;
22965 attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels);
22966 attr.tlength = attr.maxlength / periods;
22967 attr.prebuf = (ma_uint32)-1;
22968 attr.minreq = (ma_uint32)-1;
22969 attr.fragsize = attr.maxlength / periods;
22970
22971 return attr;
22972}
22973
22974static ma_pa_stream* ma_context__pa_stream_new__pulse(ma_context* pContext, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap)
22975{
22976 static int g_StreamCounter = 0;
22977 char actualStreamName[256];
22978
22979 if (pStreamName != NULL) {
22980 ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1);
22981 } else {
22982 ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:");
22983 ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */
22984 }
22985 g_StreamCounter += 1;
22986
22987 return ((ma_pa_stream_new_proc)pContext->pulse.pa_stream_new)((ma_pa_context*)pContext->pulse.pPulseContext, actualStreamName, ss, cmap);
22988}
22989
22990
22991static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData)
22992{
22993 ma_device* pDevice = (ma_device*)pUserData;
22994 ma_uint32 bpf;
22995 ma_uint32 deviceState;
22996 ma_uint64 frameCount;
22997 ma_uint64 framesProcessed;
22998
22999 MA_ASSERT(pDevice != NULL);
23000
23001 /*
23002 Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio
23003 can fire this callback before the stream has even started. Ridiculous.
23004 */
23005 deviceState = ma_device_get_state(pDevice);
23006 if (deviceState != MA_STATE_STARTING && deviceState != MA_STATE_STARTED) {
23007 return;
23008 }
23009
23010 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
23011 MA_ASSERT(bpf > 0);
23012
23013 frameCount = byteCount / bpf;
23014 framesProcessed = 0;
23015
23016 while (ma_device_get_state(pDevice) == MA_STATE_STARTED && framesProcessed < frameCount) {
23017 const void* pMappedPCMFrames;
23018 size_t bytesMapped;
23019 ma_uint64 framesMapped;
23020
23021 int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped);
23022 if (pulseResult < 0) {
23023 break; /* Failed to map. Abort. */
23024 }
23025
23026 framesMapped = bytesMapped / bpf;
23027 if (framesMapped > 0) {
23028 if (pMappedPCMFrames != NULL) {
23029 ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped);
23030 } else {
23031 /* It's a hole. */
23032 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulse: Hole.\n");
23033 }
23034
23035 pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream);
23036 if (pulseResult < 0) {
23037 break; /* Failed to drop the buffer. */
23038 }
23039
23040 framesProcessed += framesMapped;
23041
23042 } else {
23043 /* Nothing was mapped. Just abort. */
23044 break;
23045 }
23046 }
23047}
23048
23049static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed)
23050{
23051 ma_result result = MA_SUCCESS;
23052 ma_uint64 framesProcessed = 0;
23053 size_t bytesMapped;
23054 ma_uint32 bpf;
23055 ma_uint32 deviceState;
23056
23057 MA_ASSERT(pDevice != NULL);
23058 MA_ASSERT(pStream != NULL);
23059
23060 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
23061 MA_ASSERT(bpf > 0);
23062
23063 deviceState = ma_device_get_state(pDevice);
23064
23065 bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream);
23066 if (bytesMapped != (size_t)-1) {
23067 if (bytesMapped > 0) {
23068 ma_uint64 framesMapped;
23069 void* pMappedPCMFrames;
23070 int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped);
23071 if (pulseResult < 0) {
23072 result = ma_result_from_pulse(pulseResult);
23073 goto done;
23074 }
23075
23076 framesMapped = bytesMapped / bpf;
23077
23078 if (deviceState == MA_STATE_STARTED || deviceState == MA_STATE_STARTING) { /* Check for starting state just in case this is being used to do the initial fill. */
23079 ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped);
23080 } else {
23081 /* Device is not started. Write silence. */
23082 ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels);
23083 }
23084
23085 pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE);
23086 if (pulseResult < 0) {
23087 result = ma_result_from_pulse(pulseResult);
23088 goto done; /* Failed to write data to stream. */
23089 }
23090
23091 framesProcessed += framesMapped;
23092 } else {
23093 result = MA_ERROR; /* No data available. Abort. */
23094 goto done;
23095 }
23096 } else {
23097 result = MA_ERROR; /* Failed to retrieve the writable size. Abort. */
23098 goto done;
23099 }
23100
23101done:
23102 if (pFramesProcessed != NULL) {
23103 *pFramesProcessed = framesProcessed;
23104 }
23105
23106 return result;
23107}
23108
23109static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData)
23110{
23111 ma_device* pDevice = (ma_device*)pUserData;
23112 ma_uint32 bpf;
23113 ma_uint64 frameCount;
23114 ma_uint64 framesProcessed;
23115 ma_uint32 deviceState;
23116 ma_result result;
23117
23118 MA_ASSERT(pDevice != NULL);
23119
23120 /*
23121 Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio
23122 can fire this callback before the stream has even started. Ridiculous.
23123 */
23124 deviceState = ma_device_get_state(pDevice);
23125 if (deviceState != MA_STATE_STARTING && deviceState != MA_STATE_STARTED) {
23126 return;
23127 }
23128
23129 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
23130 MA_ASSERT(bpf > 0);
23131
23132 frameCount = byteCount / bpf;
23133 framesProcessed = 0;
23134
23135 while (framesProcessed < frameCount) {
23136 ma_uint64 framesProcessedThisIteration;
23137
23138 /* Don't keep trying to process frames if the device isn't started. */
23139 deviceState = ma_device_get_state(pDevice);
23140 if (deviceState != MA_STATE_STARTING && deviceState != MA_STATE_STARTED) {
23141 break;
23142 }
23143
23144 result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration);
23145 if (result != MA_SUCCESS) {
23146 break;
23147 }
23148
23149 framesProcessed += framesProcessedThisIteration;
23150 }
23151}
23152
23153static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData)
23154{
23155 ma_device* pDevice = (ma_device*)pUserData;
23156 int suspended;
23157
23158 (void)pStream;
23159
23160 suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream);
23161 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);
23162
23163 if (suspended < 0) {
23164 return;
23165 }
23166
23167 if (suspended == 1) {
23168 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n");
23169
23170 if (pDevice->onStop) {
23171 pDevice->onStop(pDevice);
23172 }
23173 } else {
23174 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n");
23175 }
23176}
23177
23178static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
23179{
23180 /*
23181 Notes for PulseAudio:
23182
23183 - We're always using native format/channels/rate regardless of whether or not PulseAudio
23184 supports the format directly through their own data conversion system. I'm doing this to
23185 reduce as much variability from the PulseAudio side as possible because it's seems to be
23186 extremely unreliable at everything it does.
23187
23188 - When both the period size in frames and milliseconds are 0, we default to miniaudio's
23189 default buffer sizes rather than leaving it up to PulseAudio because I don't trust
23190 PulseAudio to give us any kind of reasonable latency by default.
23191
23192 - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this
23193 flag, capture mode will just not work properly until you open another PulseAudio app.
23194 */
23195
23196 ma_result result = MA_SUCCESS;
23197 int error = 0;
23198 const char* devPlayback = NULL;
23199 const char* devCapture = NULL;
23200 ma_format format = ma_format_unknown;
23201 ma_uint32 channels = 0;
23202 ma_uint32 sampleRate = 0;
23203 ma_pa_sink_info sinkInfo;
23204 ma_pa_source_info sourceInfo;
23205 ma_pa_sample_spec ss;
23206 ma_pa_channel_map cmap;
23207 ma_pa_buffer_attr attr;
23208 const ma_pa_sample_spec* pActualSS = NULL;
23209 const ma_pa_channel_map* pActualCMap = NULL;
23210 const ma_pa_buffer_attr* pActualAttr = NULL;
23211 ma_uint32 iChannel;
23212 ma_pa_stream_flags_t streamFlags;
23213
23214 MA_ASSERT(pDevice != NULL);
23215 MA_ZERO_OBJECT(&pDevice->pulse);
23216
23217 if (pConfig->deviceType == ma_device_type_loopback) {
23218 return MA_DEVICE_TYPE_NOT_SUPPORTED;
23219 }
23220
23221 /* No exclusive mode with the PulseAudio backend. */
23222 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
23223 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
23224 return MA_SHARE_MODE_NOT_SUPPORTED;
23225 }
23226
23227 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
23228 if (pDescriptorPlayback->pDeviceID != NULL) {
23229 devPlayback = pDescriptorPlayback->pDeviceID->pulse;
23230 }
23231
23232 format = pDescriptorPlayback->format;
23233 channels = pDescriptorPlayback->channels;
23234 sampleRate = pDescriptorPlayback->sampleRate;
23235 }
23236
23237 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
23238 if (pDescriptorCapture->pDeviceID != NULL) {
23239 devCapture = pDescriptorCapture->pDeviceID->pulse;
23240 }
23241
23242 format = pDescriptorCapture->format;
23243 channels = pDescriptorCapture->channels;
23244 sampleRate = pDescriptorCapture->sampleRate;
23245 }
23246
23247 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
23248 result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo);
23249 if (result != MA_SUCCESS) {
23250 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device.", result);
23251 goto on_error0;
23252 }
23253
23254 ss = sourceInfo.sample_spec;
23255 cmap = sourceInfo.channel_map;
23256
23257 if (ma_format_from_pulse(ss.format) == ma_format_unknown) {
23258 if (ma_is_little_endian()) {
23259 ss.format = MA_PA_SAMPLE_FLOAT32LE;
23260 } else {
23261 ss.format = MA_PA_SAMPLE_FLOAT32BE;
23262 }
23263 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] WARNING: sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_RATE_FLOAT32\n");
23264 }
23265 if (ss.rate == 0) {
23266 ss.rate = MA_DEFAULT_SAMPLE_RATE;
23267 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] WARNING: sample_spec.rate = 0. Defaulting to %d\n", ss.rate);
23268 }
23269 if (ss.channels == 0) {
23270 ss.channels = MA_DEFAULT_CHANNELS;
23271 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] WARNING: sample_spec.channels = 0. Defaulting to %d\n", ss.channels);
23272 }
23273
23274 /* We now have enough information to calculate our actual period size in frames. */
23275 pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, ss.rate, pConfig->performanceProfile);
23276
23277 attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss);
23278 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[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);
23279
23280 pDevice->pulse.pStreamCapture = ma_context__pa_stream_new__pulse(pDevice->pContext, pConfig->pulse.pStreamNameCapture, &ss, &cmap);
23281 if (pDevice->pulse.pStreamCapture == NULL) {
23282 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
23283 goto on_error0;
23284 }
23285
23286
23287 /* The callback needs to be set before connecting the stream. */
23288 ((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);
23289
23290 /* State callback for checking when the device has been corked. */
23291 ((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);
23292
23293
23294 /* Connect after we've got all of our internal state set up. */
23295 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;
23296 if (devCapture != NULL) {
23297 streamFlags |= MA_PA_STREAM_DONT_MOVE;
23298 }
23299
23300 error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags);
23301 if (error != MA_PA_OK) {
23302 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream.", ma_result_from_pulse(error));
23303 goto on_error1;
23304 }
23305
23306 result = ma_context_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, (ma_pa_stream*)pDevice->pulse.pStreamCapture);
23307 if (result != MA_SUCCESS) {
23308 goto on_error2;
23309 }
23310
23311 /* Internal format. */
23312 pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
23313 if (pActualSS != NULL) {
23314 ss = *pActualSS;
23315 }
23316
23317 pDescriptorCapture->format = ma_format_from_pulse(ss.format);
23318 pDescriptorCapture->channels = ss.channels;
23319 pDescriptorCapture->sampleRate = ss.rate;
23320
23321 /* Internal channel map. */
23322 pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
23323 if (pActualCMap != NULL) {
23324 cmap = *pActualCMap;
23325 }
23326
23327 for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) {
23328 pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
23329 }
23330
23331
23332 /* Buffer. */
23333 pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
23334 if (pActualAttr != NULL) {
23335 attr = *pActualAttr;
23336 }
23337
23338 pDescriptorCapture->periodCount = attr.maxlength / attr.fragsize;
23339 pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount;
23340
23341 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[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);
23342
23343
23344 /* Name. */
23345 devCapture = ((ma_pa_stream_get_device_name_proc)pDevice->pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
23346 if (devCapture != NULL) {
23347 ma_pa_operation* pOP = ((ma_pa_context_get_source_info_by_name_proc)pDevice->pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pDevice->pContext->pulse.pPulseContext, devCapture, ma_device_source_name_callback, pDevice);
23348 ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP);
23349 }
23350 }
23351
23352 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
23353 result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo);
23354 if (result != MA_SUCCESS) {
23355 ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.", result);
23356 goto on_error2;
23357 }
23358
23359 ss = sinkInfo.sample_spec;
23360 cmap = sinkInfo.channel_map;
23361
23362 if (ma_format_from_pulse(ss.format) == ma_format_unknown) {
23363 if (ma_is_little_endian()) {
23364 ss.format = MA_PA_SAMPLE_FLOAT32LE;
23365 } else {
23366 ss.format = MA_PA_SAMPLE_FLOAT32BE;
23367 }
23368 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] WARNING: sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_RATE_FLOAT32\n");
23369 }
23370 if (ss.rate == 0) {
23371 ss.rate = MA_DEFAULT_SAMPLE_RATE;
23372 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] WARNING: sample_spec.rate = 0. Defaulting to %d\n", ss.rate);
23373 }
23374 if (ss.channels == 0) {
23375 ss.channels = MA_DEFAULT_CHANNELS;
23376 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] WARNING: sample_spec.channels = 0. Defaulting to %d\n", ss.channels);
23377 }
23378
23379 /* We now have enough information to calculate the actual buffer size in frames. */
23380 pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, ss.rate, pConfig->performanceProfile);
23381
23382 attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss);
23383
23384 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[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);
23385
23386 pDevice->pulse.pStreamPlayback = ma_context__pa_stream_new__pulse(pDevice->pContext, pConfig->pulse.pStreamNamePlayback, &ss, &cmap);
23387 if (pDevice->pulse.pStreamPlayback == NULL) {
23388 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
23389 goto on_error2;
23390 }
23391
23392
23393 /*
23394 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
23395 device state of MA_STATE_UNINITIALIZED.
23396 */
23397 ((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);
23398
23399 /* State callback for checking when the device has been corked. */
23400 ((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);
23401
23402
23403 /* Connect after we've got all of our internal state set up. */
23404 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;
23405 if (devPlayback != NULL) {
23406 streamFlags |= MA_PA_STREAM_DONT_MOVE;
23407 }
23408
23409 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);
23410 if (error != MA_PA_OK) {
23411 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream.", ma_result_from_pulse(error));
23412 goto on_error3;
23413 }
23414
23415 result = ma_context_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, (ma_pa_stream*)pDevice->pulse.pStreamPlayback);
23416 if (result != MA_SUCCESS) {
23417 goto on_error3;
23418 }
23419
23420
23421 /* Internal format. */
23422 pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
23423 if (pActualSS != NULL) {
23424 ss = *pActualSS;
23425 }
23426
23427 pDescriptorPlayback->format = ma_format_from_pulse(ss.format);
23428 pDescriptorPlayback->channels = ss.channels;
23429 pDescriptorPlayback->sampleRate = ss.rate;
23430
23431 /* Internal channel map. */
23432 pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
23433 if (pActualCMap != NULL) {
23434 cmap = *pActualCMap;
23435 }
23436
23437 for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) {
23438 pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
23439 }
23440
23441
23442 /* Buffer. */
23443 pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
23444 if (pActualAttr != NULL) {
23445 attr = *pActualAttr;
23446 }
23447
23448 pDescriptorPlayback->periodCount = attr.maxlength / attr.tlength;
23449 pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount;
23450 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[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);
23451
23452
23453 /* Name. */
23454 devPlayback = ((ma_pa_stream_get_device_name_proc)pDevice->pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
23455 if (devPlayback != NULL) {
23456 ma_pa_operation* pOP = ((ma_pa_context_get_sink_info_by_name_proc)pDevice->pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pDevice->pContext->pulse.pPulseContext, devPlayback, ma_device_sink_name_callback, pDevice);
23457 ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP);
23458 }
23459 }
23460
23461
23462 /*
23463 We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main
23464 part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for
23465 us later on because that will only do it if it's a fully asynchronous backend - i.e. the
23466 onDeviceDataLoop callback is NULL, which is not the case for PulseAudio.
23467 */
23468 if (pConfig->deviceType == ma_device_type_duplex) {
23469 result = ma_duplex_rb_init(format, channels, sampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);
23470 if (result != MA_SUCCESS) {
23471 result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer.", result);
23472 goto on_error4;
23473 }
23474 }
23475
23476 return MA_SUCCESS;
23477
23478
23479on_error4:
23480 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
23481 ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
23482 }
23483on_error3:
23484 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
23485 ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
23486 }
23487on_error2:
23488 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
23489 ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
23490 }
23491on_error1:
23492 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
23493 ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
23494 }
23495on_error0:
23496 return result;
23497}
23498
23499
23500static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData)
23501{
23502 ma_bool32* pIsSuccessful = (ma_bool32*)pUserData;
23503 MA_ASSERT(pIsSuccessful != NULL);
23504
23505 *pIsSuccessful = (ma_bool32)success;
23506
23507 (void)pStream; /* Unused. */
23508}
23509
23510static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork)
23511{
23512 ma_context* pContext = pDevice->pContext;
23513 ma_bool32 wasSuccessful;
23514 ma_pa_stream* pStream;
23515 ma_pa_operation* pOP;
23516 ma_result result;
23517
23518 /* This should not be called with a duplex device type. */
23519 if (deviceType == ma_device_type_duplex) {
23520 return MA_INVALID_ARGS;
23521 }
23522
23523 wasSuccessful = MA_FALSE;
23524
23525 pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback);
23526 MA_ASSERT(pStream != NULL);
23527
23528 pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful);
23529 if (pOP == NULL) {
23530 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream.", (cork == 0) ? MA_FAILED_TO_START_BACKEND_DEVICE : MA_FAILED_TO_STOP_BACKEND_DEVICE);
23531 }
23532
23533 result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP);
23534 if (result != MA_SUCCESS) {
23535 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork.", result);
23536 }
23537
23538 if (!wasSuccessful) {
23539 if (cork) {
23540 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to stop PulseAudio stream.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
23541 } else {
23542 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to start PulseAudio stream.", MA_FAILED_TO_START_BACKEND_DEVICE);
23543 }
23544 }
23545
23546 return MA_SUCCESS;
23547}
23548
23549static ma_result ma_device_start__pulse(ma_device* pDevice)
23550{
23551 ma_result result;
23552
23553 MA_ASSERT(pDevice != NULL);
23554
23555 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23556 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0);
23557 if (result != MA_SUCCESS) {
23558 return result;
23559 }
23560 }
23561
23562 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23563 /* We need to fill some data before uncorking. Not doing this will result in the write callback never getting fired. */
23564 result = ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL);
23565 if (result != MA_SUCCESS) {
23566 return result; /* Failed to write data. Not sure what to do here... Just aborting. */
23567 }
23568
23569 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0);
23570 if (result != MA_SUCCESS) {
23571 return result;
23572 }
23573 }
23574
23575 return MA_SUCCESS;
23576}
23577
23578static ma_result ma_device_stop__pulse(ma_device* pDevice)
23579{
23580 ma_result result;
23581 ma_bool32 wasSuccessful;
23582
23583 MA_ASSERT(pDevice != NULL);
23584
23585 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23586 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1);
23587 if (result != MA_SUCCESS) {
23588 return result;
23589 }
23590 }
23591
23592 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23593 /* The stream needs to be drained if it's a playback device. */
23594 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);
23595 ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pOP);
23596
23597 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1);
23598 if (result != MA_SUCCESS) {
23599 return result;
23600 }
23601 }
23602
23603 return MA_SUCCESS;
23604}
23605
23606static ma_result ma_device_data_loop__pulse(ma_device* pDevice)
23607{
23608 int resultPA;
23609
23610 MA_ASSERT(pDevice != NULL);
23611
23612 /* NOTE: Don't start the device here. It'll be done at a higher level. */
23613
23614 /*
23615 All data is handled through callbacks. All we need to do is iterate over the main loop and let
23616 the callbacks deal with it.
23617 */
23618 while (ma_device_get_state(pDevice) == MA_STATE_STARTED) {
23619 resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pContext->pulse.pMainLoop, 1, NULL);
23620 if (resultPA < 0) {
23621 break;
23622 }
23623 }
23624
23625 /* NOTE: Don't stop the device here. It'll be done at a higher level. */
23626 return MA_SUCCESS;
23627}
23628
23629static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice)
23630{
23631 MA_ASSERT(pDevice != NULL);
23632
23633 ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pContext->pulse.pMainLoop);
23634
23635 return MA_SUCCESS;
23636}
23637
23638static ma_result ma_context_uninit__pulse(ma_context* pContext)
23639{
23640 MA_ASSERT(pContext != NULL);
23641 MA_ASSERT(pContext->backend == ma_backend_pulseaudio);
23642
23643 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext);
23644 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext);
23645 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop);
23646
23647#ifndef MA_NO_RUNTIME_LINKING
23648 ma_dlclose(pContext, pContext->pulse.pulseSO);
23649#endif
23650
23651 return MA_SUCCESS;
23652}
23653
23654static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
23655{
23656 ma_result result;
23657#ifndef MA_NO_RUNTIME_LINKING
23658 const char* libpulseNames[] = {
23659 "libpulse.so",
23660 "libpulse.so.0"
23661 };
23662 size_t i;
23663
23664 for (i = 0; i < ma_countof(libpulseNames); ++i) {
23665 pContext->pulse.pulseSO = ma_dlopen(pContext, libpulseNames[i]);
23666 if (pContext->pulse.pulseSO != NULL) {
23667 break;
23668 }
23669 }
23670
23671 if (pContext->pulse.pulseSO == NULL) {
23672 return MA_NO_BACKEND;
23673 }
23674
23675 pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_new");
23676 pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_free");
23677 pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_quit");
23678 pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_get_api");
23679 pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_iterate");
23680 pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_wakeup");
23681 pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_new");
23682 pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_free");
23683 pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_start");
23684 pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_stop");
23685 pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_lock");
23686 pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock");
23687 pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_wait");
23688 pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_signal");
23689 pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_accept");
23690 pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval");
23691 pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api");
23692 pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread");
23693 pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name");
23694 pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_new");
23695 pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_unref");
23696 pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_connect");
23697 pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_disconnect");
23698 pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_set_state_callback");
23699 pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_state");
23700 pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_list");
23701 pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_list");
23702 pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name");
23703 pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_by_name");
23704 pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_unref");
23705 pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_get_state");
23706 pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_init_extend");
23707 pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_valid");
23708 pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_compatible");
23709 pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_new");
23710 pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_unref");
23711 pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_playback");
23712 pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_record");
23713 pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_disconnect");
23714 pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_state");
23715 pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_sample_spec");
23716 pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_channel_map");
23717 pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_buffer_attr");
23718 pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_buffer_attr");
23719 pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_device_name");
23720 pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback");
23721 pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback");
23722 pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_suspended_callback");
23723 pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_suspended");
23724 pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush");
23725 pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain");
23726 pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_corked");
23727 pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_cork");
23728 pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_trigger");
23729 pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_begin_write");
23730 pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_write");
23731 pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_peek");
23732 pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drop");
23733 pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_writable_size");
23734 pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_readable_size");
23735#else
23736 /* This strange assignment system is just for type safety. */
23737 ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new;
23738 ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free;
23739 ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit;
23740 ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api;
23741 ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate;
23742 ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup;
23743 ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new;
23744 ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free;
23745 ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start;
23746 ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop;
23747 ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock;
23748 ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock;
23749 ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait;
23750 ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal;
23751 ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept;
23752 ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval;
23753 ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api;
23754 ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread;
23755 ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name;
23756 ma_pa_context_new_proc _pa_context_new = pa_context_new;
23757 ma_pa_context_unref_proc _pa_context_unref = pa_context_unref;
23758 ma_pa_context_connect_proc _pa_context_connect = pa_context_connect;
23759 ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect;
23760 ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback;
23761 ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state;
23762 ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list;
23763 ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list;
23764 ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name;
23765 ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name;
23766 ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref;
23767 ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state;
23768 ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend;
23769 ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid;
23770 ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible;
23771 ma_pa_stream_new_proc _pa_stream_new = pa_stream_new;
23772 ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref;
23773 ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback;
23774 ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record;
23775 ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect;
23776 ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state;
23777 ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec;
23778 ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map;
23779 ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr;
23780 ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr;
23781 ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name;
23782 ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback;
23783 ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback;
23784 ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback;
23785 ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended;
23786 ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush;
23787 ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain;
23788 ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked;
23789 ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork;
23790 ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger;
23791 ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write;
23792 ma_pa_stream_write_proc _pa_stream_write = pa_stream_write;
23793 ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek;
23794 ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop;
23795 ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size;
23796 ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size;
23797
23798 pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new;
23799 pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free;
23800 pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit;
23801 pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api;
23802 pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate;
23803 pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup;
23804 pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new;
23805 pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free;
23806 pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start;
23807 pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop;
23808 pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock;
23809 pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock;
23810 pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait;
23811 pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal;
23812 pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept;
23813 pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval;
23814 pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api;
23815 pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread;
23816 pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name;
23817 pContext->pulse.pa_context_new = (ma_proc)_pa_context_new;
23818 pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref;
23819 pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect;
23820 pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect;
23821 pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback;
23822 pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state;
23823 pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list;
23824 pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list;
23825 pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name;
23826 pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name;
23827 pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref;
23828 pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state;
23829 pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend;
23830 pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid;
23831 pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible;
23832 pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new;
23833 pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref;
23834 pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback;
23835 pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record;
23836 pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect;
23837 pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state;
23838 pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec;
23839 pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map;
23840 pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr;
23841 pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr;
23842 pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name;
23843 pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback;
23844 pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback;
23845 pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback;
23846 pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended;
23847 pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush;
23848 pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain;
23849 pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked;
23850 pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork;
23851 pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger;
23852 pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write;
23853 pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write;
23854 pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek;
23855 pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop;
23856 pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size;
23857 pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size;
23858#endif
23859
23860 /* 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. */
23861 pContext->pulse.pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
23862 if (pContext->pulse.pMainLoop == NULL) {
23863 result = ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop.", MA_FAILED_TO_INIT_BACKEND);
23864 #ifndef MA_NO_RUNTIME_LINKING
23865 ma_dlclose(pContext, pContext->pulse.pulseSO);
23866 #endif
23867 return result;
23868 }
23869
23870 pContext->pulse.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*)pContext->pulse.pMainLoop), pConfig->pulse.pApplicationName);
23871 if (pContext->pulse.pPulseContext == NULL) {
23872 result = ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context.", MA_FAILED_TO_INIT_BACKEND);
23873 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pContext->pulse.pMainLoop));
23874 #ifndef MA_NO_RUNTIME_LINKING
23875 ma_dlclose(pContext, pContext->pulse.pulseSO);
23876 #endif
23877 return result;
23878 }
23879
23880 /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */
23881 result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pContext->pulse.pPulseContext, pConfig->pulse.pServerName, (pConfig->pulse.tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL));
23882 if (result != MA_SUCCESS) {
23883 ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context.", result);
23884 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pContext->pulse.pMainLoop));
23885 #ifndef MA_NO_RUNTIME_LINKING
23886 ma_dlclose(pContext, pContext->pulse.pulseSO);
23887 #endif
23888 return result;
23889 }
23890
23891 /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */
23892 result = ma_context_wait_for_pa_context_to_connect__pulse(pContext);
23893 if (result != MA_SUCCESS) {
23894 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pContext->pulse.pMainLoop));
23895 #ifndef MA_NO_RUNTIME_LINKING
23896 ma_dlclose(pContext, pContext->pulse.pulseSO);
23897 #endif
23898 return result;
23899 }
23900
23901
23902 /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */
23903 pCallbacks->onContextInit = ma_context_init__pulse;
23904 pCallbacks->onContextUninit = ma_context_uninit__pulse;
23905 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse;
23906 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse;
23907 pCallbacks->onDeviceInit = ma_device_init__pulse;
23908 pCallbacks->onDeviceUninit = ma_device_uninit__pulse;
23909 pCallbacks->onDeviceStart = ma_device_start__pulse;
23910 pCallbacks->onDeviceStop = ma_device_stop__pulse;
23911 pCallbacks->onDeviceRead = NULL; /* Not used because we're implementing onDeviceDataLoop. */
23912 pCallbacks->onDeviceWrite = NULL; /* Not used because we're implementing onDeviceDataLoop. */
23913 pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse;
23914 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse;
23915
23916 return MA_SUCCESS;
23917}
23918#endif
23919
23920
23921/******************************************************************************
23922
23923JACK Backend
23924
23925******************************************************************************/
23926#ifdef MA_HAS_JACK
23927
23928/* It is assumed jack.h is available when compile-time linking is being used. */
23929#ifdef MA_NO_RUNTIME_LINKING
23930#include <jack/jack.h>
23931
23932typedef jack_nframes_t ma_jack_nframes_t;
23933typedef jack_options_t ma_jack_options_t;
23934typedef jack_status_t ma_jack_status_t;
23935typedef jack_client_t ma_jack_client_t;
23936typedef jack_port_t ma_jack_port_t;
23937typedef JackProcessCallback ma_JackProcessCallback;
23938typedef JackBufferSizeCallback ma_JackBufferSizeCallback;
23939typedef JackShutdownCallback ma_JackShutdownCallback;
23940#define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE
23941#define ma_JackNoStartServer JackNoStartServer
23942#define ma_JackPortIsInput JackPortIsInput
23943#define ma_JackPortIsOutput JackPortIsOutput
23944#define ma_JackPortIsPhysical JackPortIsPhysical
23945#else
23946typedef ma_uint32 ma_jack_nframes_t;
23947typedef int ma_jack_options_t;
23948typedef int ma_jack_status_t;
23949typedef struct ma_jack_client_t ma_jack_client_t;
23950typedef struct ma_jack_port_t ma_jack_port_t;
23951typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg);
23952typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg);
23953typedef void (* ma_JackShutdownCallback) (void* arg);
23954#define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio"
23955#define ma_JackNoStartServer 1
23956#define ma_JackPortIsInput 1
23957#define ma_JackPortIsOutput 2
23958#define ma_JackPortIsPhysical 4
23959#endif
23960
23961typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...);
23962typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client);
23963typedef int (* ma_jack_client_name_size_proc) (void);
23964typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg);
23965typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg);
23966typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg);
23967typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client);
23968typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client);
23969typedef 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);
23970typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client);
23971typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client);
23972typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port);
23973typedef 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);
23974typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port);
23975typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes);
23976typedef void (* ma_jack_free_proc) (void* ptr);
23977
23978static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient)
23979{
23980 size_t maxClientNameSize;
23981 char clientName[256];
23982 ma_jack_status_t status;
23983 ma_jack_client_t* pClient;
23984
23985 MA_ASSERT(pContext != NULL);
23986 MA_ASSERT(ppClient != NULL);
23987
23988 if (ppClient) {
23989 *ppClient = NULL;
23990 }
23991
23992 maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */
23993 ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1);
23994
23995 pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL);
23996 if (pClient == NULL) {
23997 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
23998 }
23999
24000 if (ppClient) {
24001 *ppClient = pClient;
24002 }
24003
24004 return MA_SUCCESS;
24005}
24006
24007
24008static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
24009{
24010 ma_bool32 cbResult = MA_TRUE;
24011
24012 MA_ASSERT(pContext != NULL);
24013 MA_ASSERT(callback != NULL);
24014
24015 /* Playback. */
24016 if (cbResult) {
24017 ma_device_info deviceInfo;
24018 MA_ZERO_OBJECT(&deviceInfo);
24019 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
24020 deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */
24021 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
24022 }
24023
24024 /* Capture. */
24025 if (cbResult) {
24026 ma_device_info deviceInfo;
24027 MA_ZERO_OBJECT(&deviceInfo);
24028 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
24029 deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */
24030 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
24031 }
24032
24033 (void)cbResult; /* For silencing a static analysis warning. */
24034
24035 return MA_SUCCESS;
24036}
24037
24038static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
24039{
24040 ma_jack_client_t* pClient;
24041 ma_result result;
24042 const char** ppPorts;
24043
24044 MA_ASSERT(pContext != NULL);
24045
24046 if (pDeviceID != NULL && pDeviceID->jack != 0) {
24047 return MA_NO_DEVICE; /* Don't know the device. */
24048 }
24049
24050 /* Name / Description */
24051 if (deviceType == ma_device_type_playback) {
24052 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
24053 } else {
24054 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
24055 }
24056
24057 /* Jack only uses default devices. */
24058 pDeviceInfo->isDefault = MA_TRUE;
24059
24060 /* Jack only supports f32 and has a specific channel count and sample rate. */
24061 pDeviceInfo->nativeDataFormats[0].format = ma_format_f32;
24062
24063 /* The channel count and sample rate can only be determined by opening the device. */
24064 result = ma_context_open_client__jack(pContext, &pClient);
24065 if (result != MA_SUCCESS) {
24066 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", result);
24067 }
24068
24069 pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient);
24070 pDeviceInfo->nativeDataFormats[0].channels = 0;
24071
24072 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));
24073 if (ppPorts == NULL) {
24074 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
24075 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24076 }
24077
24078 while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) {
24079 pDeviceInfo->nativeDataFormats[0].channels += 1;
24080 }
24081
24082 pDeviceInfo->nativeDataFormats[0].flags = 0;
24083 pDeviceInfo->nativeDataFormatCount = 1;
24084
24085 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
24086 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
24087
24088 (void)pContext;
24089 return MA_SUCCESS;
24090}
24091
24092
24093static ma_result ma_device_uninit__jack(ma_device* pDevice)
24094{
24095 ma_context* pContext;
24096
24097 MA_ASSERT(pDevice != NULL);
24098
24099 pContext = pDevice->pContext;
24100 MA_ASSERT(pContext != NULL);
24101
24102 if (pDevice->jack.pClient != NULL) {
24103 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient);
24104 }
24105
24106 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24107 ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
24108 }
24109
24110 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24111 ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
24112 }
24113
24114 return MA_SUCCESS;
24115}
24116
24117static void ma_device__jack_shutdown_callback(void* pUserData)
24118{
24119 /* JACK died. Stop the device. */
24120 ma_device* pDevice = (ma_device*)pUserData;
24121 MA_ASSERT(pDevice != NULL);
24122
24123 ma_device_stop(pDevice);
24124}
24125
24126static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData)
24127{
24128 ma_device* pDevice = (ma_device*)pUserData;
24129 MA_ASSERT(pDevice != NULL);
24130
24131 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24132 size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
24133 float* pNewBuffer = (float*)ma__calloc_from_callbacks(newBufferSize, &pDevice->pContext->allocationCallbacks);
24134 if (pNewBuffer == NULL) {
24135 return MA_OUT_OF_MEMORY;
24136 }
24137
24138 ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
24139
24140 pDevice->jack.pIntermediaryBufferCapture = pNewBuffer;
24141 pDevice->playback.internalPeriodSizeInFrames = frameCount;
24142 }
24143
24144 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24145 size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
24146 float* pNewBuffer = (float*)ma__calloc_from_callbacks(newBufferSize, &pDevice->pContext->allocationCallbacks);
24147 if (pNewBuffer == NULL) {
24148 return MA_OUT_OF_MEMORY;
24149 }
24150
24151 ma__free_from_callbacks(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
24152
24153 pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer;
24154 pDevice->playback.internalPeriodSizeInFrames = frameCount;
24155 }
24156
24157 return 0;
24158}
24159
24160static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData)
24161{
24162 ma_device* pDevice;
24163 ma_context* pContext;
24164 ma_uint32 iChannel;
24165
24166 pDevice = (ma_device*)pUserData;
24167 MA_ASSERT(pDevice != NULL);
24168
24169 pContext = pDevice->pContext;
24170 MA_ASSERT(pContext != NULL);
24171
24172 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24173 /* Channels need to be interleaved. */
24174 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
24175 const float* pSrc = (const float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.pPortsCapture[iChannel], frameCount);
24176 if (pSrc != NULL) {
24177 float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel;
24178 ma_jack_nframes_t iFrame;
24179 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
24180 *pDst = *pSrc;
24181
24182 pDst += pDevice->capture.internalChannels;
24183 pSrc += 1;
24184 }
24185 }
24186 }
24187
24188 ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount);
24189 }
24190
24191 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24192 ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount);
24193
24194 /* Channels need to be deinterleaved. */
24195 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
24196 float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.pPortsPlayback[iChannel], frameCount);
24197 if (pDst != NULL) {
24198 const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel;
24199 ma_jack_nframes_t iFrame;
24200 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
24201 *pDst = *pSrc;
24202
24203 pDst += 1;
24204 pSrc += pDevice->playback.internalChannels;
24205 }
24206 }
24207 }
24208 }
24209
24210 return 0;
24211}
24212
24213static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
24214{
24215 ma_result result;
24216 ma_uint32 periodSizeInFrames;
24217
24218 MA_ASSERT(pConfig != NULL);
24219 MA_ASSERT(pDevice != NULL);
24220
24221 if (pConfig->deviceType == ma_device_type_loopback) {
24222 return MA_DEVICE_TYPE_NOT_SUPPORTED;
24223 }
24224
24225 /* Only supporting default devices with JACK. */
24226 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) ||
24227 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) {
24228 return MA_NO_DEVICE;
24229 }
24230
24231 /* No exclusive mode with the JACK backend. */
24232 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
24233 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
24234 return MA_SHARE_MODE_NOT_SUPPORTED;
24235 }
24236
24237 /* Open the client. */
24238 result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient);
24239 if (result != MA_SUCCESS) {
24240 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", result);
24241 }
24242
24243 /* Callbacks. */
24244 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) {
24245 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24246 }
24247 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) {
24248 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24249 }
24250
24251 ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice);
24252
24253
24254 /* The buffer size in frames can change. */
24255 periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient);
24256
24257 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
24258 const char** ppPorts;
24259
24260 pDescriptorCapture->format = ma_format_f32;
24261 pDescriptorCapture->channels = 0;
24262 pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
24263 ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
24264
24265 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);
24266 if (ppPorts == NULL) {
24267 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24268 }
24269
24270 while (ppPorts[pDescriptorCapture->channels] != NULL) {
24271 char name[64];
24272 ma_strcpy_s(name, sizeof(name), "capture");
24273 ma_itoa_s((int)pDescriptorCapture->channels, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */
24274
24275 pDevice->jack.pPortsCapture[pDescriptorCapture->channels] = ((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);
24276 if (pDevice->jack.pPortsCapture[pDescriptorCapture->channels] == NULL) {
24277 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
24278 ma_device_uninit__jack(pDevice);
24279 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24280 }
24281
24282 pDescriptorCapture->channels += 1;
24283 }
24284
24285 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
24286
24287 pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
24288 pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */
24289
24290 pDevice->jack.pIntermediaryBufferCapture = (float*)ma__calloc_from_callbacks(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks);
24291 if (pDevice->jack.pIntermediaryBufferCapture == NULL) {
24292 ma_device_uninit__jack(pDevice);
24293 return MA_OUT_OF_MEMORY;
24294 }
24295 }
24296
24297 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
24298 const char** ppPorts;
24299
24300 pDescriptorPlayback->format = ma_format_f32;
24301 pDescriptorPlayback->channels = 0;
24302 pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
24303 ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
24304
24305 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);
24306 if (ppPorts == NULL) {
24307 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24308 }
24309
24310 while (ppPorts[pDescriptorPlayback->channels] != NULL) {
24311 char name[64];
24312 ma_strcpy_s(name, sizeof(name), "playback");
24313 ma_itoa_s((int)pDescriptorPlayback->channels, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */
24314
24315 pDevice->jack.pPortsPlayback[pDescriptorPlayback->channels] = ((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);
24316 if (pDevice->jack.pPortsPlayback[pDescriptorPlayback->channels] == NULL) {
24317 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
24318 ma_device_uninit__jack(pDevice);
24319 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24320 }
24321
24322 pDescriptorPlayback->channels += 1;
24323 }
24324
24325 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
24326
24327 pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
24328 pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */
24329
24330 pDevice->jack.pIntermediaryBufferPlayback = (float*)ma__calloc_from_callbacks(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks);
24331 if (pDevice->jack.pIntermediaryBufferPlayback == NULL) {
24332 ma_device_uninit__jack(pDevice);
24333 return MA_OUT_OF_MEMORY;
24334 }
24335 }
24336
24337 return MA_SUCCESS;
24338}
24339
24340
24341static ma_result ma_device_start__jack(ma_device* pDevice)
24342{
24343 ma_context* pContext = pDevice->pContext;
24344 int resultJACK;
24345 size_t i;
24346
24347 resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient);
24348 if (resultJACK != 0) {
24349 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client.", MA_FAILED_TO_START_BACKEND_DEVICE);
24350 }
24351
24352 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24353 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);
24354 if (ppServerPorts == NULL) {
24355 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
24356 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.", MA_ERROR);
24357 }
24358
24359 for (i = 0; ppServerPorts[i] != NULL; ++i) {
24360 const char* pServerPort = ppServerPorts[i];
24361 const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.pPortsCapture[i]);
24362
24363 resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort);
24364 if (resultJACK != 0) {
24365 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
24366 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
24367 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.", MA_ERROR);
24368 }
24369 }
24370
24371 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
24372 }
24373
24374 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24375 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);
24376 if (ppServerPorts == NULL) {
24377 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
24378 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.", MA_ERROR);
24379 }
24380
24381 for (i = 0; ppServerPorts[i] != NULL; ++i) {
24382 const char* pServerPort = ppServerPorts[i];
24383 const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.pPortsPlayback[i]);
24384
24385 resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort);
24386 if (resultJACK != 0) {
24387 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
24388 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
24389 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.", MA_ERROR);
24390 }
24391 }
24392
24393 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
24394 }
24395
24396 return MA_SUCCESS;
24397}
24398
24399static ma_result ma_device_stop__jack(ma_device* pDevice)
24400{
24401 ma_context* pContext = pDevice->pContext;
24402 ma_stop_proc onStop;
24403
24404 if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) {
24405 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.", MA_ERROR);
24406 }
24407
24408 onStop = pDevice->onStop;
24409 if (onStop) {
24410 onStop(pDevice);
24411 }
24412
24413 return MA_SUCCESS;
24414}
24415
24416
24417static ma_result ma_context_uninit__jack(ma_context* pContext)
24418{
24419 MA_ASSERT(pContext != NULL);
24420 MA_ASSERT(pContext->backend == ma_backend_jack);
24421
24422 ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
24423 pContext->jack.pClientName = NULL;
24424
24425#ifndef MA_NO_RUNTIME_LINKING
24426 ma_dlclose(pContext, pContext->jack.jackSO);
24427#endif
24428
24429 return MA_SUCCESS;
24430}
24431
24432static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
24433{
24434#ifndef MA_NO_RUNTIME_LINKING
24435 const char* libjackNames[] = {
24436#ifdef MA_WIN32
24437 "libjack.dll",
24438 "libjack64.dll"
24439#else
24440 "libjack.so",
24441 "libjack.so.0"
24442#endif
24443 };
24444 size_t i;
24445
24446 for (i = 0; i < ma_countof(libjackNames); ++i) {
24447 pContext->jack.jackSO = ma_dlopen(pContext, libjackNames[i]);
24448 if (pContext->jack.jackSO != NULL) {
24449 break;
24450 }
24451 }
24452
24453 if (pContext->jack.jackSO == NULL) {
24454 return MA_NO_BACKEND;
24455 }
24456
24457 pContext->jack.jack_client_open = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_open");
24458 pContext->jack.jack_client_close = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_close");
24459 pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_name_size");
24460 pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_process_callback");
24461 pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_buffer_size_callback");
24462 pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_on_shutdown");
24463 pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_sample_rate");
24464 pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_buffer_size");
24465 pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_ports");
24466 pContext->jack.jack_activate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_activate");
24467 pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_deactivate");
24468 pContext->jack.jack_connect = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_connect");
24469 pContext->jack.jack_port_register = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_register");
24470 pContext->jack.jack_port_name = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_name");
24471 pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_get_buffer");
24472 pContext->jack.jack_free = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_free");
24473#else
24474 /*
24475 This strange assignment system is here just to ensure type safety of miniaudio's function pointer
24476 types. If anything differs slightly the compiler should throw a warning.
24477 */
24478 ma_jack_client_open_proc _jack_client_open = jack_client_open;
24479 ma_jack_client_close_proc _jack_client_close = jack_client_close;
24480 ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size;
24481 ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback;
24482 ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback;
24483 ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown;
24484 ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate;
24485 ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size;
24486 ma_jack_get_ports_proc _jack_get_ports = jack_get_ports;
24487 ma_jack_activate_proc _jack_activate = jack_activate;
24488 ma_jack_deactivate_proc _jack_deactivate = jack_deactivate;
24489 ma_jack_connect_proc _jack_connect = jack_connect;
24490 ma_jack_port_register_proc _jack_port_register = jack_port_register;
24491 ma_jack_port_name_proc _jack_port_name = jack_port_name;
24492 ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer;
24493 ma_jack_free_proc _jack_free = jack_free;
24494
24495 pContext->jack.jack_client_open = (ma_proc)_jack_client_open;
24496 pContext->jack.jack_client_close = (ma_proc)_jack_client_close;
24497 pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size;
24498 pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback;
24499 pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback;
24500 pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown;
24501 pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate;
24502 pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size;
24503 pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports;
24504 pContext->jack.jack_activate = (ma_proc)_jack_activate;
24505 pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate;
24506 pContext->jack.jack_connect = (ma_proc)_jack_connect;
24507 pContext->jack.jack_port_register = (ma_proc)_jack_port_register;
24508 pContext->jack.jack_port_name = (ma_proc)_jack_port_name;
24509 pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer;
24510 pContext->jack.jack_free = (ma_proc)_jack_free;
24511#endif
24512
24513 if (pConfig->jack.pClientName != NULL) {
24514 pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks);
24515 }
24516 pContext->jack.tryStartServer = pConfig->jack.tryStartServer;
24517
24518 /*
24519 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
24520 a temporary client.
24521 */
24522 {
24523 ma_jack_client_t* pDummyClient;
24524 ma_result result = ma_context_open_client__jack(pContext, &pDummyClient);
24525 if (result != MA_SUCCESS) {
24526 ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
24527 #ifndef MA_NO_RUNTIME_LINKING
24528 ma_dlclose(pContext, pContext->jack.jackSO);
24529 #endif
24530 return MA_NO_BACKEND;
24531 }
24532
24533 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient);
24534 }
24535
24536
24537 pCallbacks->onContextInit = ma_context_init__jack;
24538 pCallbacks->onContextUninit = ma_context_uninit__jack;
24539 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack;
24540 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack;
24541 pCallbacks->onDeviceInit = ma_device_init__jack;
24542 pCallbacks->onDeviceUninit = ma_device_uninit__jack;
24543 pCallbacks->onDeviceStart = ma_device_start__jack;
24544 pCallbacks->onDeviceStop = ma_device_stop__jack;
24545 pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */
24546 pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */
24547 pCallbacks->onDeviceDataLoop = NULL; /* Not used because JACK is asynchronous. */
24548
24549 return MA_SUCCESS;
24550}
24551#endif /* JACK */
24552
24553
24554
24555/******************************************************************************
24556
24557Core Audio Backend
24558
24559References
24560==========
24561- Technical Note TN2091: Device input using the HAL Output Audio Unit
24562 https://developer.apple.com/library/archive/technotes/tn2091/_index.html
24563
24564******************************************************************************/
24565#ifdef MA_HAS_COREAUDIO
24566#include <TargetConditionals.h>
24567
24568#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1
24569 #define MA_APPLE_MOBILE
24570 #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
24571 #define MA_APPLE_TV
24572 #endif
24573 #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
24574 #define MA_APPLE_WATCH
24575 #endif
24576#else
24577 #define MA_APPLE_DESKTOP
24578#endif
24579
24580#if defined(MA_APPLE_DESKTOP)
24581#include <CoreAudio/CoreAudio.h>
24582#else
24583#include <AVFoundation/AVFoundation.h>
24584#endif
24585
24586#include <AudioToolbox/AudioToolbox.h>
24587
24588/* CoreFoundation */
24589typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding);
24590typedef void (* ma_CFRelease_proc)(CFTypeRef cf);
24591
24592/* CoreAudio */
24593#if defined(MA_APPLE_DESKTOP)
24594typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData);
24595typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);
24596typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
24597typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
24598typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
24599#endif
24600
24601/* AudioToolbox */
24602typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc);
24603typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance);
24604typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance);
24605typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit);
24606typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit);
24607typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData);
24608typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable);
24609typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize);
24610typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
24611typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit);
24612typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
24613
24614
24615#define MA_COREAUDIO_OUTPUT_BUS 0
24616#define MA_COREAUDIO_INPUT_BUS 1
24617
24618#if defined(MA_APPLE_DESKTOP)
24619static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit);
24620#endif
24621
24622/*
24623Core Audio
24624
24625So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation
24626apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose
24627needing to figure out how this darn thing works, I'm going to outline a few things here.
24628
24629Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be
24630able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen
24631that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent
24632and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the
24633distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API.
24634
24635Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When
24636retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific
24637data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the
24638devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be
24639the central APIs for retrieving information about the system and specific devices.
24640
24641To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a
24642structure with three variables and is used to identify which property you are getting or setting. The first is the "selector"
24643which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is
24644typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and
24645kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to
24646kAudioObjectPropertyElementMaster in miniaudio's case. I don't know of any cases where this would be set to anything different.
24647
24648Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size
24649of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property
24650address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the
24651size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of
24652AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count.
24653*/
24654
24655static ma_result ma_result_from_OSStatus(OSStatus status)
24656{
24657 switch (status)
24658 {
24659 case noErr: return MA_SUCCESS;
24660 #if defined(MA_APPLE_DESKTOP)
24661 case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED;
24662 case kAudioHardwareUnspecifiedError: return MA_ERROR;
24663 case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS;
24664 case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION;
24665 case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION;
24666 case kAudioHardwareBadObjectError: return MA_INVALID_ARGS;
24667 case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS;
24668 case kAudioHardwareBadStreamError: return MA_INVALID_ARGS;
24669 case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION;
24670 case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED;
24671 case kAudioDevicePermissionsError: return MA_ACCESS_DENIED;
24672 #endif
24673 default: return MA_ERROR;
24674 }
24675}
24676
24677#if 0
24678static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit)
24679{
24680 switch (bit)
24681 {
24682 case kAudioChannelBit_Left: return MA_CHANNEL_LEFT;
24683 case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT;
24684 case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER;
24685 case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE;
24686 case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT;
24687 case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT;
24688 case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
24689 case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
24690 case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER;
24691 case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
24692 case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
24693 case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
24694 case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
24695 case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
24696 case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
24697 case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
24698 case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
24699 case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
24700 default: return MA_CHANNEL_NONE;
24701 }
24702}
24703#endif
24704
24705static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut)
24706{
24707 MA_ASSERT(pDescription != NULL);
24708 MA_ASSERT(pFormatOut != NULL);
24709
24710 *pFormatOut = ma_format_unknown; /* Safety. */
24711
24712 /* There's a few things miniaudio doesn't support. */
24713 if (pDescription->mFormatID != kAudioFormatLinearPCM) {
24714 return MA_FORMAT_NOT_SUPPORTED;
24715 }
24716
24717 /* We don't support any non-packed formats that are aligned high. */
24718 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) {
24719 return MA_FORMAT_NOT_SUPPORTED;
24720 }
24721
24722 /* Only supporting native-endian. */
24723 if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) {
24724 return MA_FORMAT_NOT_SUPPORTED;
24725 }
24726
24727 /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */
24728 /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) {
24729 return MA_FORMAT_NOT_SUPPORTED;
24730 }*/
24731
24732 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) {
24733 if (pDescription->mBitsPerChannel == 32) {
24734 *pFormatOut = ma_format_f32;
24735 return MA_SUCCESS;
24736 }
24737 } else {
24738 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) {
24739 if (pDescription->mBitsPerChannel == 16) {
24740 *pFormatOut = ma_format_s16;
24741 return MA_SUCCESS;
24742 } else if (pDescription->mBitsPerChannel == 24) {
24743 if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) {
24744 *pFormatOut = ma_format_s24;
24745 return MA_SUCCESS;
24746 } else {
24747 if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) {
24748 /* TODO: Implement ma_format_s24_32. */
24749 /**pFormatOut = ma_format_s24_32;*/
24750 /*return MA_SUCCESS;*/
24751 return MA_FORMAT_NOT_SUPPORTED;
24752 }
24753 }
24754 } else if (pDescription->mBitsPerChannel == 32) {
24755 *pFormatOut = ma_format_s32;
24756 return MA_SUCCESS;
24757 }
24758 } else {
24759 if (pDescription->mBitsPerChannel == 8) {
24760 *pFormatOut = ma_format_u8;
24761 return MA_SUCCESS;
24762 }
24763 }
24764 }
24765
24766 /* Getting here means the format is not supported. */
24767 return MA_FORMAT_NOT_SUPPORTED;
24768}
24769
24770#if defined(MA_APPLE_DESKTOP)
24771static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label)
24772{
24773 switch (label)
24774 {
24775 case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE;
24776 case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE;
24777 case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE;
24778 case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT;
24779 case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT;
24780 case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER;
24781 case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE;
24782 case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT;
24783 case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT;
24784 case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
24785 case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
24786 case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER;
24787 case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
24788 case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
24789 case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
24790 case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
24791 case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
24792 case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
24793 case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
24794 case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
24795 case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
24796 case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT;
24797 case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT;
24798 case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT;
24799 case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT;
24800 case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE;
24801 case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT;
24802 case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT;
24803 case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE;
24804 case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO;
24805 case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO;
24806 case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO;
24807 case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER;
24808 case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE;
24809 case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE;
24810 case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE;
24811 case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE;
24812 case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE;
24813 case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT;
24814 case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT;
24815 case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT;
24816 case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT;
24817 case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT;
24818 case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT;
24819 case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE;
24820 case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE;
24821 case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE;
24822 case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0;
24823 case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1;
24824 case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2;
24825 case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3;
24826 case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4;
24827 case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5;
24828 case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6;
24829 case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7;
24830 case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8;
24831 case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9;
24832 case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10;
24833 case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11;
24834 case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12;
24835 case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13;
24836 case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14;
24837 case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15;
24838 case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE;
24839
24840 #if 0 /* Introduced in a later version of macOS. */
24841 case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE;
24842 case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0;
24843 case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1;
24844 case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2;
24845 case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3;
24846 case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4;
24847 case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5;
24848 case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6;
24849 case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7;
24850 case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8;
24851 case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9;
24852 case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10;
24853 case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11;
24854 case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12;
24855 case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13;
24856 case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14;
24857 case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15;
24858 case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE;
24859 #endif
24860
24861 default: return MA_CHANNEL_NONE;
24862 }
24863}
24864
24865static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap)
24866{
24867 MA_ASSERT(pChannelLayout != NULL);
24868
24869 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
24870 UInt32 iChannel;
24871 for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) {
24872 pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);
24873 }
24874 } else
24875#if 0
24876 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
24877 /* This is the same kind of system that's used by Windows audio APIs. */
24878 UInt32 iChannel = 0;
24879 UInt32 iBit;
24880 AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap;
24881 for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) {
24882 AudioChannelBitmap bit = bitmap & (1 << iBit);
24883 if (bit != 0) {
24884 pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit);
24885 }
24886 }
24887 } else
24888#endif
24889 {
24890 /*
24891 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
24892 be updated to determine the mapping based on the tag.
24893 */
24894 UInt32 channelCount;
24895
24896 /* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */
24897 if (channelMapCap > 0xFFFFFFFF) {
24898 channelMapCap = 0xFFFFFFFF;
24899 }
24900
24901 channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap);
24902
24903 switch (pChannelLayout->mChannelLayoutTag)
24904 {
24905 case kAudioChannelLayoutTag_Mono:
24906 case kAudioChannelLayoutTag_Stereo:
24907 case kAudioChannelLayoutTag_StereoHeadphones:
24908 case kAudioChannelLayoutTag_MatrixStereo:
24909 case kAudioChannelLayoutTag_MidSide:
24910 case kAudioChannelLayoutTag_XY:
24911 case kAudioChannelLayoutTag_Binaural:
24912 case kAudioChannelLayoutTag_Ambisonic_B_Format:
24913 {
24914 ma_get_standard_channel_map(ma_standard_channel_map_default, channelCount, pChannelMap);
24915 } break;
24916
24917 case kAudioChannelLayoutTag_Octagonal:
24918 {
24919 pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
24920 pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
24921 } /* Intentional fallthrough. */
24922 case kAudioChannelLayoutTag_Hexagonal:
24923 {
24924 pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
24925 } /* Intentional fallthrough. */
24926 case kAudioChannelLayoutTag_Pentagonal:
24927 {
24928 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
24929 } /* Intentional fallghrough. */
24930 case kAudioChannelLayoutTag_Quadraphonic:
24931 {
24932 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
24933 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
24934 pChannelMap[1] = MA_CHANNEL_RIGHT;
24935 pChannelMap[0] = MA_CHANNEL_LEFT;
24936 } break;
24937
24938 /* TODO: Add support for more tags here. */
24939
24940 default:
24941 {
24942 ma_get_standard_channel_map(ma_standard_channel_map_default, channelCount, pChannelMap);
24943 } break;
24944 }
24945 }
24946
24947 return MA_SUCCESS;
24948}
24949
24950static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */
24951{
24952 AudioObjectPropertyAddress propAddressDevices;
24953 UInt32 deviceObjectsDataSize;
24954 OSStatus status;
24955 AudioObjectID* pDeviceObjectIDs;
24956
24957 MA_ASSERT(pContext != NULL);
24958 MA_ASSERT(pDeviceCount != NULL);
24959 MA_ASSERT(ppDeviceObjectIDs != NULL);
24960
24961 /* Safety. */
24962 *pDeviceCount = 0;
24963 *ppDeviceObjectIDs = NULL;
24964
24965 propAddressDevices.mSelector = kAudioHardwarePropertyDevices;
24966 propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal;
24967 propAddressDevices.mElement = kAudioObjectPropertyElementMaster;
24968
24969 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize);
24970 if (status != noErr) {
24971 return ma_result_from_OSStatus(status);
24972 }
24973
24974 pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks);
24975 if (pDeviceObjectIDs == NULL) {
24976 return MA_OUT_OF_MEMORY;
24977 }
24978
24979 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs);
24980 if (status != noErr) {
24981 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
24982 return ma_result_from_OSStatus(status);
24983 }
24984
24985 *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID);
24986 *ppDeviceObjectIDs = pDeviceObjectIDs;
24987
24988 return MA_SUCCESS;
24989}
24990
24991static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID)
24992{
24993 AudioObjectPropertyAddress propAddress;
24994 UInt32 dataSize;
24995 OSStatus status;
24996
24997 MA_ASSERT(pContext != NULL);
24998
24999 propAddress.mSelector = kAudioDevicePropertyDeviceUID;
25000 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
25001 propAddress.mElement = kAudioObjectPropertyElementMaster;
25002
25003 dataSize = sizeof(*pUID);
25004 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID);
25005 if (status != noErr) {
25006 return ma_result_from_OSStatus(status);
25007 }
25008
25009 return MA_SUCCESS;
25010}
25011
25012static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
25013{
25014 CFStringRef uid;
25015 ma_result result;
25016
25017 MA_ASSERT(pContext != NULL);
25018
25019 result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid);
25020 if (result != MA_SUCCESS) {
25021 return result;
25022 }
25023
25024 if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
25025 return MA_ERROR;
25026 }
25027
25028 ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid);
25029 return MA_SUCCESS;
25030}
25031
25032static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
25033{
25034 AudioObjectPropertyAddress propAddress;
25035 CFStringRef deviceName = NULL;
25036 UInt32 dataSize;
25037 OSStatus status;
25038
25039 MA_ASSERT(pContext != NULL);
25040
25041 propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
25042 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
25043 propAddress.mElement = kAudioObjectPropertyElementMaster;
25044
25045 dataSize = sizeof(deviceName);
25046 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName);
25047 if (status != noErr) {
25048 return ma_result_from_OSStatus(status);
25049 }
25050
25051 if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
25052 return MA_ERROR;
25053 }
25054
25055 ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName);
25056 return MA_SUCCESS;
25057}
25058
25059static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope)
25060{
25061 AudioObjectPropertyAddress propAddress;
25062 UInt32 dataSize;
25063 OSStatus status;
25064 AudioBufferList* pBufferList;
25065 ma_bool32 isSupported;
25066
25067 MA_ASSERT(pContext != NULL);
25068
25069 /* 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. */
25070 propAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
25071 propAddress.mScope = scope;
25072 propAddress.mElement = kAudioObjectPropertyElementMaster;
25073
25074 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
25075 if (status != noErr) {
25076 return MA_FALSE;
25077 }
25078
25079 pBufferList = (AudioBufferList*)ma__malloc_from_callbacks(dataSize, &pContext->allocationCallbacks);
25080 if (pBufferList == NULL) {
25081 return MA_FALSE; /* Out of memory. */
25082 }
25083
25084 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList);
25085 if (status != noErr) {
25086 ma__free_from_callbacks(pBufferList, &pContext->allocationCallbacks);
25087 return MA_FALSE;
25088 }
25089
25090 isSupported = MA_FALSE;
25091 if (pBufferList->mNumberBuffers > 0) {
25092 isSupported = MA_TRUE;
25093 }
25094
25095 ma__free_from_callbacks(pBufferList, &pContext->allocationCallbacks);
25096 return isSupported;
25097}
25098
25099static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID)
25100{
25101 return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput);
25102}
25103
25104static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID)
25105{
25106 return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput);
25107}
25108
25109
25110static 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(). */
25111{
25112 AudioObjectPropertyAddress propAddress;
25113 UInt32 dataSize;
25114 OSStatus status;
25115 AudioStreamRangedDescription* pDescriptions;
25116
25117 MA_ASSERT(pContext != NULL);
25118 MA_ASSERT(pDescriptionCount != NULL);
25119 MA_ASSERT(ppDescriptions != NULL);
25120
25121 /*
25122 TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My
25123 MacBook Pro uses s24/32 format, however, which miniaudio does not currently support.
25124 */
25125 propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/
25126 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
25127 propAddress.mElement = kAudioObjectPropertyElementMaster;
25128
25129 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
25130 if (status != noErr) {
25131 return ma_result_from_OSStatus(status);
25132 }
25133
25134 pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks);
25135 if (pDescriptions == NULL) {
25136 return MA_OUT_OF_MEMORY;
25137 }
25138
25139 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions);
25140 if (status != noErr) {
25141 ma_free(pDescriptions, &pContext->allocationCallbacks);
25142 return ma_result_from_OSStatus(status);
25143 }
25144
25145 *pDescriptionCount = dataSize / sizeof(*pDescriptions);
25146 *ppDescriptions = pDescriptions;
25147 return MA_SUCCESS;
25148}
25149
25150
25151static 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(). */
25152{
25153 AudioObjectPropertyAddress propAddress;
25154 UInt32 dataSize;
25155 OSStatus status;
25156 AudioChannelLayout* pChannelLayout;
25157
25158 MA_ASSERT(pContext != NULL);
25159 MA_ASSERT(ppChannelLayout != NULL);
25160
25161 *ppChannelLayout = NULL; /* Safety. */
25162
25163 propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
25164 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
25165 propAddress.mElement = kAudioObjectPropertyElementMaster;
25166
25167 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
25168 if (status != noErr) {
25169 return ma_result_from_OSStatus(status);
25170 }
25171
25172 pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks);
25173 if (pChannelLayout == NULL) {
25174 return MA_OUT_OF_MEMORY;
25175 }
25176
25177 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout);
25178 if (status != noErr) {
25179 ma_free(pChannelLayout, &pContext->allocationCallbacks);
25180 return ma_result_from_OSStatus(status);
25181 }
25182
25183 *ppChannelLayout = pChannelLayout;
25184 return MA_SUCCESS;
25185}
25186
25187static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount)
25188{
25189 AudioChannelLayout* pChannelLayout;
25190 ma_result result;
25191
25192 MA_ASSERT(pContext != NULL);
25193 MA_ASSERT(pChannelCount != NULL);
25194
25195 *pChannelCount = 0; /* Safety. */
25196
25197 result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
25198 if (result != MA_SUCCESS) {
25199 return result;
25200 }
25201
25202 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
25203 *pChannelCount = pChannelLayout->mNumberChannelDescriptions;
25204 } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
25205 *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap);
25206 } else {
25207 *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
25208 }
25209
25210 ma_free(pChannelLayout, &pContext->allocationCallbacks);
25211 return MA_SUCCESS;
25212}
25213
25214#if 0
25215static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
25216{
25217 AudioChannelLayout* pChannelLayout;
25218 ma_result result;
25219
25220 MA_ASSERT(pContext != NULL);
25221
25222 result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
25223 if (result != MA_SUCCESS) {
25224 return result; /* Rather than always failing here, would it be more robust to simply assume a default? */
25225 }
25226
25227 result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
25228 if (result != MA_SUCCESS) {
25229 ma_free(pChannelLayout, &pContext->allocationCallbacks);
25230 return result;
25231 }
25232
25233 ma_free(pChannelLayout, &pContext->allocationCallbacks);
25234 return result;
25235}
25236#endif
25237
25238static 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(). */
25239{
25240 AudioObjectPropertyAddress propAddress;
25241 UInt32 dataSize;
25242 OSStatus status;
25243 AudioValueRange* pSampleRateRanges;
25244
25245 MA_ASSERT(pContext != NULL);
25246 MA_ASSERT(pSampleRateRangesCount != NULL);
25247 MA_ASSERT(ppSampleRateRanges != NULL);
25248
25249 /* Safety. */
25250 *pSampleRateRangesCount = 0;
25251 *ppSampleRateRanges = NULL;
25252
25253 propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
25254 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
25255 propAddress.mElement = kAudioObjectPropertyElementMaster;
25256
25257 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
25258 if (status != noErr) {
25259 return ma_result_from_OSStatus(status);
25260 }
25261
25262 pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks);
25263 if (pSampleRateRanges == NULL) {
25264 return MA_OUT_OF_MEMORY;
25265 }
25266
25267 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges);
25268 if (status != noErr) {
25269 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
25270 return ma_result_from_OSStatus(status);
25271 }
25272
25273 *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges);
25274 *ppSampleRateRanges = pSampleRateRanges;
25275 return MA_SUCCESS;
25276}
25277
25278#if 0
25279static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut)
25280{
25281 UInt32 sampleRateRangeCount;
25282 AudioValueRange* pSampleRateRanges;
25283 ma_result result;
25284
25285 MA_ASSERT(pContext != NULL);
25286 MA_ASSERT(pSampleRateOut != NULL);
25287
25288 *pSampleRateOut = 0; /* Safety. */
25289
25290 result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
25291 if (result != MA_SUCCESS) {
25292 return result;
25293 }
25294
25295 if (sampleRateRangeCount == 0) {
25296 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
25297 return MA_ERROR; /* Should never hit this case should we? */
25298 }
25299
25300 if (sampleRateIn == 0) {
25301 /* Search in order of miniaudio's preferred priority. */
25302 UInt32 iMALSampleRate;
25303 for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) {
25304 ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate];
25305 UInt32 iCASampleRate;
25306 for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) {
25307 AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate];
25308 if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) {
25309 *pSampleRateOut = malSampleRate;
25310 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
25311 return MA_SUCCESS;
25312 }
25313 }
25314 }
25315
25316 /*
25317 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
25318 case we just fall back to the first one reported by Core Audio.
25319 */
25320 MA_ASSERT(sampleRateRangeCount > 0);
25321
25322 *pSampleRateOut = pSampleRateRanges[0].mMinimum;
25323 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
25324 return MA_SUCCESS;
25325 } else {
25326 /* Find the closest match to this sample rate. */
25327 UInt32 currentAbsoluteDifference = INT32_MAX;
25328 UInt32 iCurrentClosestRange = (UInt32)-1;
25329 UInt32 iRange;
25330 for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) {
25331 if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) {
25332 *pSampleRateOut = sampleRateIn;
25333 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
25334 return MA_SUCCESS;
25335 } else {
25336 UInt32 absoluteDifference;
25337 if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) {
25338 absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn;
25339 } else {
25340 absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum;
25341 }
25342
25343 if (currentAbsoluteDifference > absoluteDifference) {
25344 currentAbsoluteDifference = absoluteDifference;
25345 iCurrentClosestRange = iRange;
25346 }
25347 }
25348 }
25349
25350 MA_ASSERT(iCurrentClosestRange != (UInt32)-1);
25351
25352 *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum;
25353 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
25354 return MA_SUCCESS;
25355 }
25356
25357 /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */
25358 /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/
25359 /*return MA_ERROR;*/
25360}
25361#endif
25362
25363static 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)
25364{
25365 AudioObjectPropertyAddress propAddress;
25366 AudioValueRange bufferSizeRange;
25367 UInt32 dataSize;
25368 OSStatus status;
25369
25370 MA_ASSERT(pContext != NULL);
25371 MA_ASSERT(pBufferSizeInFramesOut != NULL);
25372
25373 *pBufferSizeInFramesOut = 0; /* Safety. */
25374
25375 propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
25376 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
25377 propAddress.mElement = kAudioObjectPropertyElementMaster;
25378
25379 dataSize = sizeof(bufferSizeRange);
25380 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange);
25381 if (status != noErr) {
25382 return ma_result_from_OSStatus(status);
25383 }
25384
25385 /* This is just a clamp. */
25386 if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) {
25387 *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum;
25388 } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) {
25389 *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum;
25390 } else {
25391 *pBufferSizeInFramesOut = bufferSizeInFramesIn;
25392 }
25393
25394 return MA_SUCCESS;
25395}
25396
25397static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut)
25398{
25399 ma_result result;
25400 ma_uint32 chosenBufferSizeInFrames;
25401 AudioObjectPropertyAddress propAddress;
25402 UInt32 dataSize;
25403 OSStatus status;
25404
25405 MA_ASSERT(pContext != NULL);
25406
25407 result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames);
25408 if (result != MA_SUCCESS) {
25409 return result;
25410 }
25411
25412 /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */
25413 propAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
25414 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
25415 propAddress.mElement = kAudioObjectPropertyElementMaster;
25416
25417 ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames);
25418
25419 /* Get the actual size of the buffer. */
25420 dataSize = sizeof(*pPeriodSizeInOut);
25421 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames);
25422 if (status != noErr) {
25423 return ma_result_from_OSStatus(status);
25424 }
25425
25426 *pPeriodSizeInOut = chosenBufferSizeInFrames;
25427 return MA_SUCCESS;
25428}
25429
25430static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID)
25431{
25432 AudioObjectPropertyAddress propAddressDefaultDevice;
25433 UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
25434 AudioObjectID defaultDeviceObjectID;
25435 OSStatus status;
25436
25437 MA_ASSERT(pContext != NULL);
25438 MA_ASSERT(pDeviceObjectID != NULL);
25439
25440 /* Safety. */
25441 *pDeviceObjectID = 0;
25442
25443 propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal;
25444 propAddressDefaultDevice.mElement = kAudioObjectPropertyElementMaster;
25445 if (deviceType == ma_device_type_playback) {
25446 propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
25447 } else {
25448 propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice;
25449 }
25450
25451 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
25452 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID);
25453 if (status == noErr) {
25454 *pDeviceObjectID = defaultDeviceObjectID;
25455 return MA_SUCCESS;
25456 }
25457
25458 /* If we get here it means we couldn't find the device. */
25459 return MA_NO_DEVICE;
25460}
25461
25462static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID)
25463{
25464 MA_ASSERT(pContext != NULL);
25465 MA_ASSERT(pDeviceObjectID != NULL);
25466
25467 /* Safety. */
25468 *pDeviceObjectID = 0;
25469
25470 if (pDeviceID == NULL) {
25471 /* Default device. */
25472 return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID);
25473 } else {
25474 /* Explicit device. */
25475 UInt32 deviceCount;
25476 AudioObjectID* pDeviceObjectIDs;
25477 ma_result result;
25478 UInt32 iDevice;
25479
25480 result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
25481 if (result != MA_SUCCESS) {
25482 return result;
25483 }
25484
25485 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
25486 AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
25487
25488 char uid[256];
25489 if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) {
25490 continue;
25491 }
25492
25493 if (deviceType == ma_device_type_playback) {
25494 if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
25495 if (strcmp(uid, pDeviceID->coreaudio) == 0) {
25496 *pDeviceObjectID = deviceObjectID;
25497 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
25498 return MA_SUCCESS;
25499 }
25500 }
25501 } else {
25502 if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
25503 if (strcmp(uid, pDeviceID->coreaudio) == 0) {
25504 *pDeviceObjectID = deviceObjectID;
25505 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
25506 return MA_SUCCESS;
25507 }
25508 }
25509 }
25510 }
25511
25512 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
25513 }
25514
25515 /* If we get here it means we couldn't find the device. */
25516 return MA_NO_DEVICE;
25517}
25518
25519
25520static 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)
25521{
25522 UInt32 deviceFormatDescriptionCount;
25523 AudioStreamRangedDescription* pDeviceFormatDescriptions;
25524 ma_result result;
25525 ma_uint32 desiredSampleRate;
25526 ma_uint32 desiredChannelCount;
25527 ma_format desiredFormat;
25528 AudioStreamBasicDescription bestDeviceFormatSoFar;
25529 ma_bool32 hasSupportedFormat;
25530 UInt32 iFormat;
25531
25532 result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions);
25533 if (result != MA_SUCCESS) {
25534 return result;
25535 }
25536
25537 desiredSampleRate = sampleRate;
25538 if (desiredSampleRate == 0) {
25539 desiredSampleRate = pOrigFormat->mSampleRate;
25540 }
25541
25542 desiredChannelCount = channels;
25543 if (desiredChannelCount == 0) {
25544 desiredChannelCount = pOrigFormat->mChannelsPerFrame;
25545 }
25546
25547 desiredFormat = format;
25548 if (desiredFormat == ma_format_unknown) {
25549 result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat);
25550 if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) {
25551 desiredFormat = g_maFormatPriorities[0];
25552 }
25553 }
25554
25555 /*
25556 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
25557 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.
25558 */
25559 MA_ZERO_OBJECT(&bestDeviceFormatSoFar);
25560
25561 hasSupportedFormat = MA_FALSE;
25562 for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
25563 ma_format format;
25564 ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &format);
25565 if (formatResult == MA_SUCCESS && format != ma_format_unknown) {
25566 hasSupportedFormat = MA_TRUE;
25567 bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat;
25568 break;
25569 }
25570 }
25571
25572 if (!hasSupportedFormat) {
25573 ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
25574 return MA_FORMAT_NOT_SUPPORTED;
25575 }
25576
25577
25578 for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
25579 AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat;
25580 ma_format thisSampleFormat;
25581 ma_result formatResult;
25582 ma_format bestSampleFormatSoFar;
25583
25584 /* If the format is not supported by miniaudio we need to skip this one entirely. */
25585 formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat);
25586 if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) {
25587 continue; /* The format is not supported by miniaudio. Skip. */
25588 }
25589
25590 ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar);
25591
25592 /* Getting here means the format is supported by miniaudio which makes this format a candidate. */
25593 if (thisDeviceFormat.mSampleRate != desiredSampleRate) {
25594 /*
25595 The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format
25596 so far has an equal sample rate we can just ignore this one.
25597 */
25598 if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) {
25599 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. */
25600 } else {
25601 /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */
25602 if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) {
25603 /* This format has a different sample rate _and_ a different channel count. */
25604 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
25605 continue; /* No change to the best format. */
25606 } else {
25607 /*
25608 Both this format and the best so far have different sample rates and different channel counts. Whichever has the
25609 best format is the new best.
25610 */
25611 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
25612 bestDeviceFormatSoFar = thisDeviceFormat;
25613 continue;
25614 } else {
25615 continue; /* No change to the best format. */
25616 }
25617 }
25618 } else {
25619 /* This format has a different sample rate but the desired channel count. */
25620 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
25621 /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */
25622 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
25623 bestDeviceFormatSoFar = thisDeviceFormat;
25624 continue;
25625 } else {
25626 continue; /* No change to the best format for now. */
25627 }
25628 } else {
25629 /* This format has the desired channel count, but the best so far does not. We have a new best. */
25630 bestDeviceFormatSoFar = thisDeviceFormat;
25631 continue;
25632 }
25633 }
25634 }
25635 } else {
25636 /*
25637 The sample rates match which makes this format a very high priority contender. If the best format so far has a different
25638 sample rate it needs to be replaced with this one.
25639 */
25640 if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) {
25641 bestDeviceFormatSoFar = thisDeviceFormat;
25642 continue;
25643 } else {
25644 /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */
25645 if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) {
25646 /*
25647 In this case this format has the same channel count as what the client is requesting. If the best format so far has
25648 a different count, this one becomes the new best.
25649 */
25650 if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) {
25651 bestDeviceFormatSoFar = thisDeviceFormat;
25652 continue;
25653 } else {
25654 /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */
25655 if (thisSampleFormat == desiredFormat) {
25656 bestDeviceFormatSoFar = thisDeviceFormat;
25657 break; /* Found the exact match. */
25658 } else {
25659 /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */
25660 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
25661 bestDeviceFormatSoFar = thisDeviceFormat;
25662 continue;
25663 } else {
25664 continue; /* No change to the best format for now. */
25665 }
25666 }
25667 }
25668 } else {
25669 /*
25670 In this case the channel count is different to what the client has requested. If the best so far has the same channel
25671 count as the requested count then it remains the best.
25672 */
25673 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
25674 continue;
25675 } else {
25676 /*
25677 This is the case where both have the same sample rate (good) but different channel counts. Right now both have about
25678 the same priority, but we need to compare the format now.
25679 */
25680 if (thisSampleFormat == bestSampleFormatSoFar) {
25681 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
25682 bestDeviceFormatSoFar = thisDeviceFormat;
25683 continue;
25684 } else {
25685 continue; /* No change to the best format for now. */
25686 }
25687 }
25688 }
25689 }
25690 }
25691 }
25692 }
25693
25694 *pFormat = bestDeviceFormatSoFar;
25695
25696 ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
25697 return MA_SUCCESS;
25698}
25699
25700static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
25701{
25702 AudioUnitScope deviceScope;
25703 AudioUnitElement deviceBus;
25704 UInt32 channelLayoutSize;
25705 OSStatus status;
25706 AudioChannelLayout* pChannelLayout;
25707 ma_result result;
25708
25709 MA_ASSERT(pContext != NULL);
25710
25711 if (deviceType == ma_device_type_playback) {
25712 deviceScope = kAudioUnitScope_Input;
25713 deviceBus = MA_COREAUDIO_OUTPUT_BUS;
25714 } else {
25715 deviceScope = kAudioUnitScope_Output;
25716 deviceBus = MA_COREAUDIO_INPUT_BUS;
25717 }
25718
25719 status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL);
25720 if (status != noErr) {
25721 return ma_result_from_OSStatus(status);
25722 }
25723
25724 pChannelLayout = (AudioChannelLayout*)ma__malloc_from_callbacks(channelLayoutSize, &pContext->allocationCallbacks);
25725 if (pChannelLayout == NULL) {
25726 return MA_OUT_OF_MEMORY;
25727 }
25728
25729 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize);
25730 if (status != noErr) {
25731 ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks);
25732 return ma_result_from_OSStatus(status);
25733 }
25734
25735 result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
25736 if (result != MA_SUCCESS) {
25737 ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks);
25738 return result;
25739 }
25740
25741 ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks);
25742 return MA_SUCCESS;
25743}
25744#endif /* MA_APPLE_DESKTOP */
25745
25746
25747#if !defined(MA_APPLE_DESKTOP)
25748static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo)
25749{
25750 MA_ZERO_OBJECT(pInfo);
25751 ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1);
25752 ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1);
25753}
25754#endif
25755
25756static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
25757{
25758#if defined(MA_APPLE_DESKTOP)
25759 UInt32 deviceCount;
25760 AudioObjectID* pDeviceObjectIDs;
25761 AudioObjectID defaultDeviceObjectIDPlayback;
25762 AudioObjectID defaultDeviceObjectIDCapture;
25763 ma_result result;
25764 UInt32 iDevice;
25765
25766 ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */
25767 ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */
25768
25769 result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
25770 if (result != MA_SUCCESS) {
25771 return result;
25772 }
25773
25774 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
25775 AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
25776 ma_device_info info;
25777
25778 MA_ZERO_OBJECT(&info);
25779 if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) {
25780 continue;
25781 }
25782 if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) {
25783 continue;
25784 }
25785
25786 if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
25787 if (deviceObjectID == defaultDeviceObjectIDPlayback) {
25788 info.isDefault = MA_TRUE;
25789 }
25790
25791 if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
25792 break;
25793 }
25794 }
25795 if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
25796 if (deviceObjectID == defaultDeviceObjectIDCapture) {
25797 info.isDefault = MA_TRUE;
25798 }
25799
25800 if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
25801 break;
25802 }
25803 }
25804 }
25805
25806 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
25807#else
25808 ma_device_info info;
25809 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
25810 NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
25811
25812 for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
25813 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
25814 if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
25815 return MA_SUCCESS;
25816 }
25817 }
25818
25819 for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
25820 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
25821 if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
25822 return MA_SUCCESS;
25823 }
25824 }
25825#endif
25826
25827 return MA_SUCCESS;
25828}
25829
25830static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
25831{
25832 ma_result result;
25833
25834 MA_ASSERT(pContext != NULL);
25835
25836#if defined(MA_APPLE_DESKTOP)
25837 /* Desktop */
25838 {
25839 AudioObjectID deviceObjectID;
25840 AudioObjectID defaultDeviceObjectID;
25841 UInt32 streamDescriptionCount;
25842 AudioStreamRangedDescription* pStreamDescriptions;
25843 UInt32 iStreamDescription;
25844 UInt32 sampleRateRangeCount;
25845 AudioValueRange* pSampleRateRanges;
25846
25847 ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */
25848
25849 result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
25850 if (result != MA_SUCCESS) {
25851 return result;
25852 }
25853
25854 result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio);
25855 if (result != MA_SUCCESS) {
25856 return result;
25857 }
25858
25859 result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name);
25860 if (result != MA_SUCCESS) {
25861 return result;
25862 }
25863
25864 if (deviceObjectID == defaultDeviceObjectID) {
25865 pDeviceInfo->isDefault = MA_TRUE;
25866 }
25867
25868 /*
25869 There could be a large number of permutations here. Fortunately there is only a single channel count
25870 being reported which reduces this quite a bit. For sample rates we're only reporting those that are
25871 one of miniaudio's recognized "standard" rates. If there are still more formats than can fit into
25872 our fixed sized array we'll just need to truncate them. This is unlikely and will probably only happen
25873 if some driver performs software data conversion and therefore reports every possible format and
25874 sample rate.
25875 */
25876 pDeviceInfo->nativeDataFormatCount = 0;
25877
25878 /* Formats. */
25879 {
25880 ma_format uniqueFormats[ma_format_count];
25881 ma_uint32 uniqueFormatCount = 0;
25882 ma_uint32 channels;
25883
25884 /* Channels. */
25885 result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels);
25886 if (result != MA_SUCCESS) {
25887 return result;
25888 }
25889
25890 /* Formats. */
25891 result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);
25892 if (result != MA_SUCCESS) {
25893 return result;
25894 }
25895
25896 for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) {
25897 ma_format format;
25898 ma_bool32 hasFormatBeenHandled = MA_FALSE;
25899 ma_uint32 iOutputFormat;
25900 ma_uint32 iSampleRate;
25901
25902 result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format);
25903 if (result != MA_SUCCESS) {
25904 continue;
25905 }
25906
25907 MA_ASSERT(format != ma_format_unknown);
25908
25909 /* Make sure the format isn't already in the output list. */
25910 for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) {
25911 if (uniqueFormats[iOutputFormat] == format) {
25912 hasFormatBeenHandled = MA_TRUE;
25913 break;
25914 }
25915 }
25916
25917 /* If we've already handled this format just skip it. */
25918 if (hasFormatBeenHandled) {
25919 continue;
25920 }
25921
25922 uniqueFormats[uniqueFormatCount] = format;
25923 uniqueFormatCount += 1;
25924
25925 /* Sample Rates */
25926 result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
25927 if (result != MA_SUCCESS) {
25928 return result;
25929 }
25930
25931 /*
25932 Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are
25933 between this range.
25934 */
25935 for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) {
25936 ma_uint32 iStandardSampleRate;
25937 for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {
25938 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];
25939 if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) {
25940 /* We have a new data format. Add it to the list. */
25941 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
25942 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
25943 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate;
25944 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
25945 pDeviceInfo->nativeDataFormatCount += 1;
25946
25947 if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
25948 break; /* No more room for any more formats. */
25949 }
25950 }
25951 }
25952 }
25953
25954 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
25955
25956 if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
25957 break; /* No more room for any more formats. */
25958 }
25959 }
25960
25961 ma_free(pStreamDescriptions, &pContext->allocationCallbacks);
25962 }
25963 }
25964#else
25965 /* Mobile */
25966 {
25967 AudioComponentDescription desc;
25968 AudioComponent component;
25969 AudioUnit audioUnit;
25970 OSStatus status;
25971 AudioUnitScope formatScope;
25972 AudioUnitElement formatElement;
25973 AudioStreamBasicDescription bestFormat;
25974 UInt32 propSize;
25975
25976 /* We want to ensure we use a consistent device name to device enumeration. */
25977 if (pDeviceID != NULL) {
25978 ma_bool32 found = MA_FALSE;
25979 if (deviceType == ma_device_type_playback) {
25980 NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
25981 for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
25982 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
25983 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
25984 found = MA_TRUE;
25985 break;
25986 }
25987 }
25988 } else {
25989 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
25990 for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
25991 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
25992 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
25993 found = MA_TRUE;
25994 break;
25995 }
25996 }
25997 }
25998
25999 if (!found) {
26000 return MA_DOES_NOT_EXIST;
26001 }
26002 } else {
26003 if (deviceType == ma_device_type_playback) {
26004 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
26005 } else {
26006 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
26007 }
26008 }
26009
26010
26011 /*
26012 Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is
26013 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
26014 retrieve from the AVAudioSession shared instance.
26015 */
26016 desc.componentType = kAudioUnitType_Output;
26017 desc.componentSubType = kAudioUnitSubType_RemoteIO;
26018 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
26019 desc.componentFlags = 0;
26020 desc.componentFlagsMask = 0;
26021
26022 component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
26023 if (component == NULL) {
26024 return MA_FAILED_TO_INIT_BACKEND;
26025 }
26026
26027 status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit);
26028 if (status != noErr) {
26029 return ma_result_from_OSStatus(status);
26030 }
26031
26032 formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
26033 formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
26034
26035 propSize = sizeof(bestFormat);
26036 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
26037 if (status != noErr) {
26038 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
26039 return ma_result_from_OSStatus(status);
26040 }
26041
26042 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
26043 audioUnit = NULL;
26044
26045 /* Only a single format is being reported for iOS. */
26046 pDeviceInfo->nativeDataFormatCount = 1;
26047
26048 result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format);
26049 if (result != MA_SUCCESS) {
26050 return result;
26051 }
26052
26053 pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame;
26054
26055 /*
26056 It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do
26057 this we just get the shared instance and inspect.
26058 */
26059 @autoreleasepool {
26060 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
26061 MA_ASSERT(pAudioSession != NULL);
26062
26063 pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate;
26064 }
26065 }
26066#endif
26067
26068 (void)pDeviceInfo; /* Unused. */
26069 return MA_SUCCESS;
26070}
26071
26072static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks)
26073{
26074 AudioBufferList* pBufferList;
26075 UInt32 audioBufferSizeInBytes;
26076 size_t allocationSize;
26077
26078 MA_ASSERT(sizeInFrames > 0);
26079 MA_ASSERT(format != ma_format_unknown);
26080 MA_ASSERT(channels > 0);
26081
26082 allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */
26083 if (layout == ma_stream_layout_interleaved) {
26084 /* Interleaved case. This is the simple case because we just have one buffer. */
26085 allocationSize += sizeof(AudioBuffer) * 1;
26086 } else {
26087 /* Non-interleaved case. This is the more complex case because there's more than one buffer. */
26088 allocationSize += sizeof(AudioBuffer) * channels;
26089 }
26090
26091 allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels);
26092
26093 pBufferList = (AudioBufferList*)ma__malloc_from_callbacks(allocationSize, pAllocationCallbacks);
26094 if (pBufferList == NULL) {
26095 return NULL;
26096 }
26097
26098 audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format));
26099
26100 if (layout == ma_stream_layout_interleaved) {
26101 pBufferList->mNumberBuffers = 1;
26102 pBufferList->mBuffers[0].mNumberChannels = channels;
26103 pBufferList->mBuffers[0].mDataByteSize = audioBufferSizeInBytes * channels;
26104 pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList);
26105 } else {
26106 ma_uint32 iBuffer;
26107 pBufferList->mNumberBuffers = channels;
26108 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
26109 pBufferList->mBuffers[iBuffer].mNumberChannels = 1;
26110 pBufferList->mBuffers[iBuffer].mDataByteSize = audioBufferSizeInBytes;
26111 pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer);
26112 }
26113 }
26114
26115 return pBufferList;
26116}
26117
26118static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout)
26119{
26120 MA_ASSERT(pDevice != NULL);
26121 MA_ASSERT(format != ma_format_unknown);
26122 MA_ASSERT(channels > 0);
26123
26124 /* Only resize the buffer if necessary. */
26125 if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) {
26126 AudioBufferList* pNewAudioBufferList;
26127
26128 pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks);
26129 if (pNewAudioBufferList != NULL) {
26130 return MA_OUT_OF_MEMORY;
26131 }
26132
26133 /* At this point we'll have a new AudioBufferList and we can free the old one. */
26134 ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
26135 pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList;
26136 pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames;
26137 }
26138
26139 /* Getting here means the capacity of the audio is fine. */
26140 return MA_SUCCESS;
26141}
26142
26143
26144static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList)
26145{
26146 ma_device* pDevice = (ma_device*)pUserData;
26147 ma_stream_layout layout;
26148
26149 MA_ASSERT(pDevice != NULL);
26150
26151 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers);
26152
26153 /* 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. */
26154 layout = ma_stream_layout_interleaved;
26155 if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) {
26156 layout = ma_stream_layout_deinterleaved;
26157 }
26158
26159 if (layout == ma_stream_layout_interleaved) {
26160 /* For now we can assume everything is interleaved. */
26161 UInt32 iBuffer;
26162 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
26163 if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) {
26164 ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
26165 if (frameCountForThisBuffer > 0) {
26166 ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer);
26167 }
26168
26169 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
26170 } else {
26171 /*
26172 This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
26173 not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just
26174 output silence here.
26175 */
26176 MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
26177 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
26178 }
26179 }
26180 } else {
26181 /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */
26182 MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should heve been validated at initialization time. */
26183
26184 /*
26185 For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
26186 very strange has happened and we're not going to support it.
26187 */
26188 if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) {
26189 ma_uint8 tempBuffer[4096];
26190 UInt32 iBuffer;
26191
26192 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) {
26193 ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat);
26194 ma_uint32 framesRemaining = frameCountPerBuffer;
26195
26196 while (framesRemaining > 0) {
26197 void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
26198 ma_uint32 iChannel;
26199 ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
26200 if (framesToRead > framesRemaining) {
26201 framesToRead = framesRemaining;
26202 }
26203
26204 ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead);
26205
26206 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
26207 ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
26208 }
26209
26210 ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
26211
26212 framesRemaining -= framesToRead;
26213 }
26214 }
26215 }
26216 }
26217
26218 (void)pActionFlags;
26219 (void)pTimeStamp;
26220 (void)busNumber;
26221 (void)frameCount;
26222
26223 return noErr;
26224}
26225
26226static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList)
26227{
26228 ma_device* pDevice = (ma_device*)pUserData;
26229 AudioBufferList* pRenderedBufferList;
26230 ma_result result;
26231 ma_stream_layout layout;
26232 ma_uint32 iBuffer;
26233 OSStatus status;
26234
26235 MA_ASSERT(pDevice != NULL);
26236
26237 pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
26238 MA_ASSERT(pRenderedBufferList);
26239
26240 /* 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. */
26241 layout = ma_stream_layout_interleaved;
26242 if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) {
26243 layout = ma_stream_layout_deinterleaved;
26244 }
26245
26246 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers);
26247
26248 /*
26249 There has been a situation reported where frame count passed into this function is greater than the capacity of
26250 our capture buffer. There doesn't seem to be a reliable way to determine what the maximum frame count will be,
26251 so we need to instead resort to dynamically reallocating our buffer to ensure it's large enough to capture the
26252 number of frames requested by this callback.
26253 */
26254 result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout);
26255 if (result != MA_SUCCESS) {
26256 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.");
26257 return noErr;
26258 }
26259
26260 /*
26261 When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes
26262 that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer
26263 being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a
26264 problem when a future call to this callback specifies a larger number of frames.
26265
26266 To work around this we need to explicitly set the size of each buffer to their respective size in bytes.
26267 */
26268 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
26269 pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels;
26270 }
26271
26272 status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList);
26273 if (status != noErr) {
26274 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " ERROR: AudioUnitRender() failed with %d\n", status);
26275 return status;
26276 }
26277
26278 if (layout == ma_stream_layout_interleaved) {
26279 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
26280 if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) {
26281 ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount);
26282 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
26283 } else {
26284 /*
26285 This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
26286 not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams.
26287 */
26288 ma_uint8 silentBuffer[4096];
26289 ma_uint32 framesRemaining;
26290
26291 MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer));
26292
26293 framesRemaining = frameCount;
26294 while (framesRemaining > 0) {
26295 ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
26296 if (framesToSend > framesRemaining) {
26297 framesToSend = framesRemaining;
26298 }
26299
26300 ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend);
26301
26302 framesRemaining -= framesToSend;
26303 }
26304
26305 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
26306 }
26307 }
26308 } else {
26309 /* 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. */
26310 MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */
26311
26312 /*
26313 For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
26314 very strange has happened and we're not going to support it.
26315 */
26316 if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) {
26317 ma_uint8 tempBuffer[4096];
26318 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) {
26319 ma_uint32 framesRemaining = frameCount;
26320 while (framesRemaining > 0) {
26321 void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
26322 ma_uint32 iChannel;
26323 ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
26324 if (framesToSend > framesRemaining) {
26325 framesToSend = framesRemaining;
26326 }
26327
26328 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
26329 ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
26330 }
26331
26332 ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);
26333 ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend);
26334
26335 framesRemaining -= framesToSend;
26336 }
26337 }
26338 }
26339 }
26340
26341 (void)pActionFlags;
26342 (void)pTimeStamp;
26343 (void)busNumber;
26344 (void)frameCount;
26345 (void)pUnusedBufferList;
26346
26347 return noErr;
26348}
26349
26350static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element)
26351{
26352 ma_device* pDevice = (ma_device*)pUserData;
26353 MA_ASSERT(pDevice != NULL);
26354
26355 /* Don't do anything if it looks like we're just reinitializing due to a device switch. */
26356 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
26357 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
26358 return;
26359 }
26360
26361 /*
26362 There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like
26363 AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit)
26364 can try waiting on the same lock. I'm going to try working around this by not calling any Core
26365 Audio APIs in the callback when the device has been stopped or uninitialized.
26366 */
26367 if (ma_device_get_state(pDevice) == MA_STATE_UNINITIALIZED || ma_device_get_state(pDevice) == MA_STATE_STOPPING || ma_device_get_state(pDevice) == MA_STATE_STOPPED) {
26368 ma_stop_proc onStop = pDevice->onStop;
26369 if (onStop) {
26370 onStop(pDevice);
26371 }
26372
26373 ma_event_signal(&pDevice->coreaudio.stopEvent);
26374 } else {
26375 UInt32 isRunning;
26376 UInt32 isRunningSize = sizeof(isRunning);
26377 OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);
26378 if (status != noErr) {
26379 return; /* Don't really know what to do in this case... just ignore it, I suppose... */
26380 }
26381
26382 if (!isRunning) {
26383 ma_stop_proc onStop;
26384
26385 /*
26386 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:
26387
26388 1) When the device is unplugged, this will be called _before_ the default device change notification.
26389 2) When the device is changed via the default device change notification, this will be called _after_ the switch.
26390
26391 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.
26392 */
26393 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) ||
26394 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) {
26395 /*
26396 It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device
26397 via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the
26398 device to be seamless to the client (we don't want them receiving the onStop event and thinking that the device has stopped when it
26399 hasn't!).
26400 */
26401 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
26402 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
26403 return;
26404 }
26405
26406 /*
26407 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
26408 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
26409 likely be successful in switching to the new device.
26410
26411 TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted.
26412 */
26413 return;
26414 }
26415
26416 /* Getting here means we need to stop the device. */
26417 onStop = pDevice->onStop;
26418 if (onStop) {
26419 onStop(pDevice);
26420 }
26421 }
26422 }
26423
26424 (void)propertyID; /* Unused. */
26425}
26426
26427#if defined(MA_APPLE_DESKTOP)
26428static 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. */
26429static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0;
26430static ma_mutex g_DeviceTrackingMutex_CoreAudio;
26431static ma_device** g_ppTrackedDevices_CoreAudio = NULL;
26432static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0;
26433static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0;
26434
26435static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData)
26436{
26437 ma_device_type deviceType;
26438
26439 /* Not sure if I really need to check this, but it makes me feel better. */
26440 if (addressCount == 0) {
26441 return noErr;
26442 }
26443
26444 if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) {
26445 deviceType = ma_device_type_playback;
26446 } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) {
26447 deviceType = ma_device_type_capture;
26448 } else {
26449 return noErr; /* Should never hit this. */
26450 }
26451
26452 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
26453 {
26454 ma_uint32 iDevice;
26455 for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
26456 ma_result reinitResult;
26457 ma_device* pDevice;
26458
26459 pDevice = g_ppTrackedDevices_CoreAudio[iDevice];
26460 if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) {
26461 if (deviceType == ma_device_type_playback) {
26462 pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE;
26463 reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
26464 pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE;
26465 } else {
26466 pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE;
26467 reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
26468 pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE;
26469 }
26470
26471 if (reinitResult == MA_SUCCESS) {
26472 ma_device__post_init_setup(pDevice, deviceType);
26473
26474 /* Restart the device if required. If this fails we need to stop the device entirely. */
26475 if (ma_device_get_state(pDevice) == MA_STATE_STARTED) {
26476 OSStatus status;
26477 if (deviceType == ma_device_type_playback) {
26478 status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
26479 if (status != noErr) {
26480 if (pDevice->type == ma_device_type_duplex) {
26481 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
26482 }
26483 ma_device__set_state(pDevice, MA_STATE_STOPPED);
26484 }
26485 } else if (deviceType == ma_device_type_capture) {
26486 status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
26487 if (status != noErr) {
26488 if (pDevice->type == ma_device_type_duplex) {
26489 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
26490 }
26491 ma_device__set_state(pDevice, MA_STATE_STOPPED);
26492 }
26493 }
26494 }
26495 }
26496 }
26497 }
26498 }
26499 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
26500
26501 /* Unused parameters. */
26502 (void)objectID;
26503 (void)pUserData;
26504
26505 return noErr;
26506}
26507
26508static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext)
26509{
26510 MA_ASSERT(pContext != NULL);
26511
26512 ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
26513 {
26514 /* Don't do anything if we've already initializd device tracking. */
26515 if (g_DeviceTrackingInitCounter_CoreAudio == 0) {
26516 AudioObjectPropertyAddress propAddress;
26517 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
26518 propAddress.mElement = kAudioObjectPropertyElementMaster;
26519
26520 ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio);
26521
26522 propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
26523 ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
26524
26525 propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
26526 ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
26527
26528 g_DeviceTrackingInitCounter_CoreAudio += 1;
26529 }
26530 }
26531 ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
26532
26533 return MA_SUCCESS;
26534}
26535
26536static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext)
26537{
26538 MA_ASSERT(pContext != NULL);
26539
26540 ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
26541 {
26542 g_DeviceTrackingInitCounter_CoreAudio -= 1;
26543
26544 if (g_DeviceTrackingInitCounter_CoreAudio == 0) {
26545 AudioObjectPropertyAddress propAddress;
26546 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
26547 propAddress.mElement = kAudioObjectPropertyElementMaster;
26548
26549 propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
26550 ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
26551
26552 propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
26553 ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
26554
26555 /* At this point there should be no tracked devices. If not there's an error somewhere. */
26556 if (g_ppTrackedDevices_CoreAudio != NULL) {
26557 ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active.", MA_INVALID_OPERATION);
26558 }
26559
26560 ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio);
26561 }
26562 }
26563 ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
26564
26565 return MA_SUCCESS;
26566}
26567
26568static ma_result ma_device__track__coreaudio(ma_device* pDevice)
26569{
26570 MA_ASSERT(pDevice != NULL);
26571
26572 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
26573 {
26574 /* Allocate memory if required. */
26575 if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) {
26576 ma_uint32 oldCap;
26577 ma_uint32 newCap;
26578 ma_device** ppNewDevices;
26579
26580 oldCap = g_TrackedDeviceCap_CoreAudio;
26581 newCap = g_TrackedDeviceCap_CoreAudio * 2;
26582 if (newCap == 0) {
26583 newCap = 1;
26584 }
26585
26586 ppNewDevices = (ma_device**)ma__realloc_from_callbacks(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, sizeof(*g_ppTrackedDevices_CoreAudio)*oldCap, &pDevice->pContext->allocationCallbacks);
26587 if (ppNewDevices == NULL) {
26588 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
26589 return MA_OUT_OF_MEMORY;
26590 }
26591
26592 g_ppTrackedDevices_CoreAudio = ppNewDevices;
26593 g_TrackedDeviceCap_CoreAudio = newCap;
26594 }
26595
26596 g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice;
26597 g_TrackedDeviceCount_CoreAudio += 1;
26598 }
26599 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
26600
26601 return MA_SUCCESS;
26602}
26603
26604static ma_result ma_device__untrack__coreaudio(ma_device* pDevice)
26605{
26606 MA_ASSERT(pDevice != NULL);
26607
26608 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
26609 {
26610 ma_uint32 iDevice;
26611 for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
26612 if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) {
26613 /* We've found the device. We now need to remove it from the list. */
26614 ma_uint32 jDevice;
26615 for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) {
26616 g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1];
26617 }
26618
26619 g_TrackedDeviceCount_CoreAudio -= 1;
26620
26621 /* If there's nothing else in the list we need to free memory. */
26622 if (g_TrackedDeviceCount_CoreAudio == 0) {
26623 ma__free_from_callbacks(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks);
26624 g_ppTrackedDevices_CoreAudio = NULL;
26625 g_TrackedDeviceCap_CoreAudio = 0;
26626 }
26627
26628 break;
26629 }
26630 }
26631 }
26632 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
26633
26634 return MA_SUCCESS;
26635}
26636#endif
26637
26638#if defined(MA_APPLE_MOBILE)
26639@interface ma_router_change_handler:NSObject {
26640 ma_device* m_pDevice;
26641}
26642@end
26643
26644@implementation ma_router_change_handler
26645-(id)init:(ma_device*)pDevice
26646{
26647 self = [super init];
26648 m_pDevice = pDevice;
26649
26650 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
26651
26652 return self;
26653}
26654
26655-(void)dealloc
26656{
26657 [self remove_handler];
26658}
26659
26660-(void)remove_handler
26661{
26662 [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
26663}
26664
26665-(void)handle_route_change:(NSNotification*)pNotification
26666{
26667 AVAudioSession* pSession = [AVAudioSession sharedInstance];
26668
26669 NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
26670 switch (reason)
26671 {
26672 case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
26673 {
26674 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n");
26675 } break;
26676
26677 case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
26678 {
26679 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n");
26680 } break;
26681
26682 case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
26683 {
26684 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n");
26685 } break;
26686
26687 case AVAudioSessionRouteChangeReasonWakeFromSleep:
26688 {
26689 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n");
26690 } break;
26691
26692 case AVAudioSessionRouteChangeReasonOverride:
26693 {
26694 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n");
26695 } break;
26696
26697 case AVAudioSessionRouteChangeReasonCategoryChange:
26698 {
26699 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n");
26700 } break;
26701
26702 case AVAudioSessionRouteChangeReasonUnknown:
26703 default:
26704 {
26705 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n");
26706 } break;
26707 }
26708
26709 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);
26710
26711 /* Temporarily disabling this section of code because it appears to be causing errors. */
26712#if 0
26713 ma_uint32 previousState = ma_device_get_state(m_pDevice);
26714
26715 if (previousState == MA_STATE_STARTED) {
26716 ma_device_stop(m_pDevice);
26717 }
26718
26719 if (m_pDevice->type == ma_device_type_capture || m_pDevice->type == ma_device_type_duplex) {
26720 m_pDevice->capture.internalChannels = (ma_uint32)pSession.inputNumberOfChannels;
26721 m_pDevice->capture.internalSampleRate = (ma_uint32)pSession.sampleRate;
26722 ma_device__post_init_setup(m_pDevice, ma_device_type_capture);
26723 }
26724 if (m_pDevice->type == ma_device_type_playback || m_pDevice->type == ma_device_type_duplex) {
26725 m_pDevice->playback.internalChannels = (ma_uint32)pSession.outputNumberOfChannels;
26726 m_pDevice->playback.internalSampleRate = (ma_uint32)pSession.sampleRate;
26727 ma_device__post_init_setup(m_pDevice, ma_device_type_playback);
26728 }
26729
26730 if (previousState == MA_STATE_STARTED) {
26731 ma_device_start(m_pDevice);
26732 }
26733#endif
26734}
26735@end
26736#endif
26737
26738static ma_result ma_device_uninit__coreaudio(ma_device* pDevice)
26739{
26740 MA_ASSERT(pDevice != NULL);
26741 MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_UNINITIALIZED);
26742
26743#if defined(MA_APPLE_DESKTOP)
26744 /*
26745 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
26746 just gracefully ignore it.
26747 */
26748 ma_device__untrack__coreaudio(pDevice);
26749#endif
26750#if defined(MA_APPLE_MOBILE)
26751 if (pDevice->coreaudio.pRouteChangeHandler != NULL) {
26752 ma_router_change_handler* pRouteChangeHandler = (__bridge_transfer ma_router_change_handler*)pDevice->coreaudio.pRouteChangeHandler;
26753 [pRouteChangeHandler remove_handler];
26754 }
26755#endif
26756
26757 if (pDevice->coreaudio.audioUnitCapture != NULL) {
26758 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
26759 }
26760 if (pDevice->coreaudio.audioUnitPlayback != NULL) {
26761 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
26762 }
26763
26764 if (pDevice->coreaudio.pAudioBufferList) {
26765 ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
26766 }
26767
26768 return MA_SUCCESS;
26769}
26770
26771typedef struct
26772{
26773 ma_bool32 allowNominalSampleRateChange;
26774
26775 /* Input. */
26776 ma_format formatIn;
26777 ma_uint32 channelsIn;
26778 ma_uint32 sampleRateIn;
26779 ma_channel channelMapIn[MA_MAX_CHANNELS];
26780 ma_uint32 periodSizeInFramesIn;
26781 ma_uint32 periodSizeInMillisecondsIn;
26782 ma_uint32 periodsIn;
26783 ma_share_mode shareMode;
26784 ma_performance_profile performanceProfile;
26785 ma_bool32 registerStopEvent;
26786
26787 /* Output. */
26788#if defined(MA_APPLE_DESKTOP)
26789 AudioObjectID deviceObjectID;
26790#endif
26791 AudioComponent component;
26792 AudioUnit audioUnit;
26793 AudioBufferList* pAudioBufferList; /* Only used for input devices. */
26794 ma_format formatOut;
26795 ma_uint32 channelsOut;
26796 ma_uint32 sampleRateOut;
26797 ma_channel channelMapOut[MA_MAX_CHANNELS];
26798 ma_uint32 periodSizeInFramesOut;
26799 ma_uint32 periodsOut;
26800 char deviceName[256];
26801} ma_device_init_internal_data__coreaudio;
26802
26803static 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. */
26804{
26805 ma_result result;
26806 OSStatus status;
26807 UInt32 enableIOFlag;
26808 AudioStreamBasicDescription bestFormat;
26809 UInt32 actualPeriodSizeInFrames;
26810 AURenderCallbackStruct callbackInfo;
26811#if defined(MA_APPLE_DESKTOP)
26812 AudioObjectID deviceObjectID;
26813#else
26814 UInt32 actualPeriodSizeInFramesSize = sizeof(actualPeriodSizeInFrames);
26815#endif
26816
26817 /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */
26818 if (deviceType == ma_device_type_duplex) {
26819 return MA_INVALID_ARGS;
26820 }
26821
26822 MA_ASSERT(pContext != NULL);
26823 MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture);
26824
26825#if defined(MA_APPLE_DESKTOP)
26826 pData->deviceObjectID = 0;
26827#endif
26828 pData->component = NULL;
26829 pData->audioUnit = NULL;
26830 pData->pAudioBufferList = NULL;
26831
26832#if defined(MA_APPLE_DESKTOP)
26833 result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
26834 if (result != MA_SUCCESS) {
26835 return result;
26836 }
26837
26838 pData->deviceObjectID = deviceObjectID;
26839#endif
26840
26841 /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */
26842 pData->periodsOut = pData->periodsIn;
26843 if (pData->periodsOut == 0) {
26844 pData->periodsOut = MA_DEFAULT_PERIODS;
26845 }
26846 if (pData->periodsOut > 16) {
26847 pData->periodsOut = 16;
26848 }
26849
26850
26851 /* Audio unit. */
26852 status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit);
26853 if (status != noErr) {
26854 return ma_result_from_OSStatus(status);
26855 }
26856
26857
26858 /* 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. */
26859 enableIOFlag = 1;
26860 if (deviceType == ma_device_type_capture) {
26861 enableIOFlag = 0;
26862 }
26863
26864 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
26865 if (status != noErr) {
26866 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26867 return ma_result_from_OSStatus(status);
26868 }
26869
26870 enableIOFlag = (enableIOFlag == 0) ? 1 : 0;
26871 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
26872 if (status != noErr) {
26873 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26874 return ma_result_from_OSStatus(status);
26875 }
26876
26877
26878 /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */
26879#if defined(MA_APPLE_DESKTOP)
26880 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID));
26881 if (status != noErr) {
26882 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26883 return ma_result_from_OSStatus(result);
26884 }
26885#else
26886 /*
26887 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
26888 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.
26889 */
26890 if (pDeviceID != NULL) {
26891 if (deviceType == ma_device_type_capture) {
26892 ma_bool32 found = MA_FALSE;
26893 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
26894 for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
26895 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
26896 [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil];
26897 found = MA_TRUE;
26898 break;
26899 }
26900 }
26901
26902 if (found == MA_FALSE) {
26903 return MA_DOES_NOT_EXIST;
26904 }
26905 }
26906 }
26907#endif
26908
26909 /*
26910 Format. This is the hardest part of initialization because there's a few variables to take into account.
26911 1) The format must be supported by the device.
26912 2) The format must be supported miniaudio.
26913 3) There's a priority that miniaudio prefers.
26914
26915 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
26916 most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same
26917 for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely.
26918
26919 On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to.
26920 */
26921 {
26922 AudioStreamBasicDescription origFormat;
26923 UInt32 origFormatSize = sizeof(origFormat);
26924 AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
26925 AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
26926
26927 if (deviceType == ma_device_type_playback) {
26928 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize);
26929 } else {
26930 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize);
26931 }
26932 if (status != noErr) {
26933 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26934 return ma_result_from_OSStatus(status);
26935 }
26936
26937 #if defined(MA_APPLE_DESKTOP)
26938 result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat);
26939 if (result != MA_SUCCESS) {
26940 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
26941 return result;
26942 }
26943
26944 /*
26945 Technical Note TN2091: Device input using the HAL Output Audio Unit
26946 https://developer.apple.com/library/archive/technotes/tn2091/_index.html
26947
26948 This documentation says the following:
26949
26950 The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY
26951 variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate
26952 conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with
26953 another AudioConverter.
26954
26955 The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We
26956 therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it
26957 safe and apply the same rule to output as well.
26958
26959 I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender()
26960 returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but
26961 this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format.
26962
26963 Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with
26964 this, however, is that it actually changes the sample rate at the operating system level and not just the application. This
26965 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
26966 configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample
26967 rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run
26968 the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is
26969 changed by miniaudio.
26970 */
26971 if (pData->allowNominalSampleRateChange) {
26972 AudioValueRange sampleRateRange;
26973 AudioObjectPropertyAddress propAddress;
26974
26975 sampleRateRange.mMinimum = bestFormat.mSampleRate;
26976 sampleRateRange.mMaximum = bestFormat.mSampleRate;
26977
26978 propAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
26979 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
26980 propAddress.mElement = kAudioObjectPropertyElementMaster;
26981
26982 status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange);
26983 if (status != noErr) {
26984 bestFormat.mSampleRate = origFormat.mSampleRate;
26985 }
26986 } else {
26987 bestFormat.mSampleRate = origFormat.mSampleRate;
26988 }
26989
26990 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
26991 if (status != noErr) {
26992 /* We failed to set the format, so fall back to the current format of the audio unit. */
26993 bestFormat = origFormat;
26994 }
26995 #else
26996 bestFormat = origFormat;
26997
26998 /*
26999 Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try
27000 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
27001 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
27002 can tell, it looks like the sample rate is shared between playback and capture for everything.
27003 */
27004 @autoreleasepool {
27005 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
27006 MA_ASSERT(pAudioSession != NULL);
27007
27008 [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil];
27009 bestFormat.mSampleRate = pAudioSession.sampleRate;
27010
27011 /*
27012 I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with
27013 AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead.
27014 */
27015 if (deviceType == ma_device_type_playback) {
27016 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels;
27017 }
27018 if (deviceType == ma_device_type_capture) {
27019 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels;
27020 }
27021 }
27022
27023 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
27024 if (status != noErr) {
27025 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
27026 return ma_result_from_OSStatus(status);
27027 }
27028 #endif
27029
27030 result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut);
27031 if (result != MA_SUCCESS) {
27032 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
27033 return result;
27034 }
27035
27036 if (pData->formatOut == ma_format_unknown) {
27037 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
27038 return MA_FORMAT_NOT_SUPPORTED;
27039 }
27040
27041 pData->channelsOut = bestFormat.mChannelsPerFrame;
27042 pData->sampleRateOut = bestFormat.mSampleRate;
27043 }
27044
27045 /* Clamp the channel count for safety. */
27046 if (pData->channelsOut > MA_MAX_CHANNELS) {
27047 pData->channelsOut = MA_MAX_CHANNELS;
27048 }
27049
27050 /*
27051 Internal channel map. This is weird in my testing. If I use the AudioObject to get the
27052 channel map, the channel descriptions are set to "Unknown" for some reason. To work around
27053 this it looks like retrieving it from the AudioUnit will work. However, and this is where
27054 it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore
27055 I'm going to fall back to a default assumption in these cases.
27056 */
27057#if defined(MA_APPLE_DESKTOP)
27058 result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut);
27059 if (result != MA_SUCCESS) {
27060 #if 0
27061 /* Try falling back to the channel map from the AudioObject. */
27062 result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut);
27063 if (result != MA_SUCCESS) {
27064 return result;
27065 }
27066 #else
27067 /* Fall back to default assumptions. */
27068 ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
27069 #endif
27070 }
27071#else
27072 /* TODO: Figure out how to get the channel map using AVAudioSession. */
27073 ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
27074#endif
27075
27076
27077 /* Buffer size. Not allowing this to be configurable on iOS. */
27078 if (pData->periodSizeInFramesIn == 0) {
27079 if (pData->periodSizeInMillisecondsIn == 0) {
27080 if (pData->performanceProfile == ma_performance_profile_low_latency) {
27081 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut);
27082 } else {
27083 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut);
27084 }
27085 } else {
27086 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut);
27087 }
27088 } else {
27089 actualPeriodSizeInFrames = pData->periodSizeInFramesIn;
27090 }
27091
27092#if defined(MA_APPLE_DESKTOP)
27093 result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames);
27094 if (result != MA_SUCCESS) {
27095 return result;
27096 }
27097#else
27098 /*
27099 I don't know how to configure buffer sizes on iOS so for now we're not allowing it to be configured. Instead we're
27100 just going to set it to the value of kAudioUnitProperty_MaximumFramesPerSlice.
27101 */
27102 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, &actualPeriodSizeInFramesSize);
27103 if (status != noErr) {
27104 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
27105 return ma_result_from_OSStatus(status);
27106 }
27107#endif
27108
27109
27110 /*
27111 During testing I discovered that the buffer size can be too big. You'll get an error like this:
27112
27113 kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512
27114
27115 Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that
27116 of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice.
27117 */
27118 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames));
27119 if (status != noErr) {
27120 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
27121 return ma_result_from_OSStatus(status);
27122 }
27123
27124 pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames;
27125
27126 /* We need a buffer list if this is an input device. We render into this in the input callback. */
27127 if (deviceType == ma_device_type_capture) {
27128 ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
27129 AudioBufferList* pBufferList;
27130
27131 pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks);
27132 if (pBufferList == NULL) {
27133 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
27134 return MA_OUT_OF_MEMORY;
27135 }
27136
27137 pData->pAudioBufferList = pBufferList;
27138 }
27139
27140 /* Callbacks. */
27141 callbackInfo.inputProcRefCon = pDevice_DoNotReference;
27142 if (deviceType == ma_device_type_playback) {
27143 callbackInfo.inputProc = ma_on_output__coreaudio;
27144 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));
27145 if (status != noErr) {
27146 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
27147 return ma_result_from_OSStatus(status);
27148 }
27149 } else {
27150 callbackInfo.inputProc = ma_on_input__coreaudio;
27151 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));
27152 if (status != noErr) {
27153 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
27154 return ma_result_from_OSStatus(status);
27155 }
27156 }
27157
27158 /* We need to listen for stop events. */
27159 if (pData->registerStopEvent) {
27160 status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference);
27161 if (status != noErr) {
27162 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
27163 return ma_result_from_OSStatus(status);
27164 }
27165 }
27166
27167 /* Initialize the audio unit. */
27168 status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit);
27169 if (status != noErr) {
27170 ma__free_from_callbacks(pData->pAudioBufferList, &pContext->allocationCallbacks);
27171 pData->pAudioBufferList = NULL;
27172 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
27173 return ma_result_from_OSStatus(status);
27174 }
27175
27176 /* Grab the name. */
27177#if defined(MA_APPLE_DESKTOP)
27178 ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName);
27179#else
27180 if (deviceType == ma_device_type_playback) {
27181 ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
27182 } else {
27183 ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME);
27184 }
27185#endif
27186
27187 return result;
27188}
27189
27190#if defined(MA_APPLE_DESKTOP)
27191static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit)
27192{
27193 ma_device_init_internal_data__coreaudio data;
27194 ma_result result;
27195
27196 /* This should only be called for playback or capture, not duplex. */
27197 if (deviceType == ma_device_type_duplex) {
27198 return MA_INVALID_ARGS;
27199 }
27200
27201 data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */
27202
27203 if (deviceType == ma_device_type_capture) {
27204 data.formatIn = pDevice->capture.format;
27205 data.channelsIn = pDevice->capture.channels;
27206 data.sampleRateIn = pDevice->sampleRate;
27207 MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
27208 data.shareMode = pDevice->capture.shareMode;
27209 data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile;
27210 data.registerStopEvent = MA_TRUE;
27211
27212 if (disposePreviousAudioUnit) {
27213 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
27214 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
27215 }
27216 if (pDevice->coreaudio.pAudioBufferList) {
27217 ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
27218 }
27219 } else if (deviceType == ma_device_type_playback) {
27220 data.formatIn = pDevice->playback.format;
27221 data.channelsIn = pDevice->playback.channels;
27222 data.sampleRateIn = pDevice->sampleRate;
27223 MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
27224 data.shareMode = pDevice->playback.shareMode;
27225 data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile;
27226 data.registerStopEvent = (pDevice->type != ma_device_type_duplex);
27227
27228 if (disposePreviousAudioUnit) {
27229 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
27230 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
27231 }
27232 }
27233 data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames;
27234 data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds;
27235 data.periodsIn = pDevice->coreaudio.originalPeriods;
27236
27237 /* Need at least 3 periods for duplex. */
27238 if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) {
27239 data.periodsIn = 3;
27240 }
27241
27242 result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice);
27243 if (result != MA_SUCCESS) {
27244 return result;
27245 }
27246
27247 if (deviceType == ma_device_type_capture) {
27248 #if defined(MA_APPLE_DESKTOP)
27249 pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
27250 #endif
27251 pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
27252 pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
27253 pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut;
27254
27255 pDevice->capture.internalFormat = data.formatOut;
27256 pDevice->capture.internalChannels = data.channelsOut;
27257 pDevice->capture.internalSampleRate = data.sampleRateOut;
27258 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
27259 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
27260 pDevice->capture.internalPeriods = data.periodsOut;
27261 } else if (deviceType == ma_device_type_playback) {
27262 #if defined(MA_APPLE_DESKTOP)
27263 pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
27264 #endif
27265 pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
27266
27267 pDevice->playback.internalFormat = data.formatOut;
27268 pDevice->playback.internalChannels = data.channelsOut;
27269 pDevice->playback.internalSampleRate = data.sampleRateOut;
27270 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
27271 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
27272 pDevice->playback.internalPeriods = data.periodsOut;
27273 }
27274
27275 return MA_SUCCESS;
27276}
27277#endif /* MA_APPLE_DESKTOP */
27278
27279static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
27280{
27281 ma_result result;
27282
27283 MA_ASSERT(pDevice != NULL);
27284 MA_ASSERT(pConfig != NULL);
27285
27286 if (pConfig->deviceType == ma_device_type_loopback) {
27287 return MA_DEVICE_TYPE_NOT_SUPPORTED;
27288 }
27289
27290 /* No exclusive mode with the Core Audio backend for now. */
27291 if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) ||
27292 ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) {
27293 return MA_SHARE_MODE_NOT_SUPPORTED;
27294 }
27295
27296 /* Capture needs to be initialized first. */
27297 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
27298 ma_device_init_internal_data__coreaudio data;
27299 data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange;
27300 data.formatIn = pDescriptorCapture->format;
27301 data.channelsIn = pDescriptorCapture->channels;
27302 data.sampleRateIn = pDescriptorCapture->sampleRate;
27303 MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
27304 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
27305 data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds;
27306 data.periodsIn = pDescriptorCapture->periodCount;
27307 data.shareMode = pDescriptorCapture->shareMode;
27308 data.performanceProfile = pConfig->performanceProfile;
27309 data.registerStopEvent = MA_TRUE;
27310
27311 /* Need at least 3 periods for duplex. */
27312 if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) {
27313 data.periodsIn = 3;
27314 }
27315
27316 result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice);
27317 if (result != MA_SUCCESS) {
27318 return result;
27319 }
27320
27321 pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL);
27322 #if defined(MA_APPLE_DESKTOP)
27323 pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
27324 #endif
27325 pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
27326 pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
27327 pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut;
27328 pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
27329 pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;
27330 pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount;
27331 pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile;
27332
27333 pDescriptorCapture->format = data.formatOut;
27334 pDescriptorCapture->channels = data.channelsOut;
27335 pDescriptorCapture->sampleRate = data.sampleRateOut;
27336 MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
27337 pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut;
27338 pDescriptorCapture->periodCount = data.periodsOut;
27339
27340 #if defined(MA_APPLE_DESKTOP)
27341 /*
27342 If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
27343 switch the device in the background.
27344 */
27345 if (pConfig->capture.pDeviceID == NULL) {
27346 ma_device__track__coreaudio(pDevice);
27347 }
27348 #endif
27349 }
27350
27351 /* Playback. */
27352 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
27353 ma_device_init_internal_data__coreaudio data;
27354 data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange;
27355 data.formatIn = pDescriptorPlayback->format;
27356 data.channelsIn = pDescriptorPlayback->channels;
27357 data.sampleRateIn = pDescriptorPlayback->sampleRate;
27358 MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
27359 data.shareMode = pDescriptorPlayback->shareMode;
27360 data.performanceProfile = pConfig->performanceProfile;
27361
27362 /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */
27363 if (pConfig->deviceType == ma_device_type_duplex) {
27364 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
27365 data.periodsIn = pDescriptorCapture->periodCount;
27366 data.registerStopEvent = MA_FALSE;
27367 } else {
27368 data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames;
27369 data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
27370 data.periodsIn = pDescriptorPlayback->periodCount;
27371 data.registerStopEvent = MA_TRUE;
27372 }
27373
27374 result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice);
27375 if (result != MA_SUCCESS) {
27376 if (pConfig->deviceType == ma_device_type_duplex) {
27377 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
27378 if (pDevice->coreaudio.pAudioBufferList) {
27379 ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
27380 }
27381 }
27382 return result;
27383 }
27384
27385 pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL);
27386 #if defined(MA_APPLE_DESKTOP)
27387 pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
27388 #endif
27389 pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
27390 pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
27391 pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
27392 pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount;
27393 pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile;
27394
27395 pDescriptorPlayback->format = data.formatOut;
27396 pDescriptorPlayback->channels = data.channelsOut;
27397 pDescriptorPlayback->sampleRate = data.sampleRateOut;
27398 MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
27399 pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut;
27400 pDescriptorPlayback->periodCount = data.periodsOut;
27401
27402 #if defined(MA_APPLE_DESKTOP)
27403 /*
27404 If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
27405 switch the device in the background.
27406 */
27407 if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) {
27408 ma_device__track__coreaudio(pDevice);
27409 }
27410 #endif
27411 }
27412
27413
27414
27415 /*
27416 When stopping the device, a callback is called on another thread. We need to wait for this callback
27417 before returning from ma_device_stop(). This event is used for this.
27418 */
27419 ma_event_init(&pDevice->coreaudio.stopEvent);
27420
27421 /*
27422 We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done
27423 differently on non-Desktop Apple platforms.
27424 */
27425#if defined(MA_APPLE_MOBILE)
27426 pDevice->coreaudio.pRouteChangeHandler = (__bridge_retained void*)[[ma_router_change_handler alloc] init:pDevice];
27427#endif
27428
27429 return MA_SUCCESS;
27430}
27431
27432
27433static ma_result ma_device_start__coreaudio(ma_device* pDevice)
27434{
27435 MA_ASSERT(pDevice != NULL);
27436
27437 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27438 OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
27439 if (status != noErr) {
27440 return ma_result_from_OSStatus(status);
27441 }
27442 }
27443
27444 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27445 OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
27446 if (status != noErr) {
27447 if (pDevice->type == ma_device_type_duplex) {
27448 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
27449 }
27450 return ma_result_from_OSStatus(status);
27451 }
27452 }
27453
27454 return MA_SUCCESS;
27455}
27456
27457static ma_result ma_device_stop__coreaudio(ma_device* pDevice)
27458{
27459 MA_ASSERT(pDevice != NULL);
27460
27461 /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */
27462
27463 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27464 OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
27465 if (status != noErr) {
27466 return ma_result_from_OSStatus(status);
27467 }
27468 }
27469
27470 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27471 OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
27472 if (status != noErr) {
27473 return ma_result_from_OSStatus(status);
27474 }
27475 }
27476
27477 /* We need to wait for the callback to finish before returning. */
27478 ma_event_wait(&pDevice->coreaudio.stopEvent);
27479 return MA_SUCCESS;
27480}
27481
27482
27483static ma_result ma_context_uninit__coreaudio(ma_context* pContext)
27484{
27485 MA_ASSERT(pContext != NULL);
27486 MA_ASSERT(pContext->backend == ma_backend_coreaudio);
27487
27488#if defined(MA_APPLE_MOBILE)
27489 if (!pContext->coreaudio.noAudioSessionDeactivate) {
27490 if (![[AVAudioSession sharedInstance] setActive:false error:nil]) {
27491 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session.", MA_FAILED_TO_INIT_BACKEND);
27492 }
27493 }
27494#endif
27495
27496#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
27497 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
27498 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
27499 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
27500#endif
27501
27502#if !defined(MA_APPLE_MOBILE)
27503 ma_context__uninit_device_tracking__coreaudio(pContext);
27504#endif
27505
27506 (void)pContext;
27507 return MA_SUCCESS;
27508}
27509
27510#if defined(MA_APPLE_MOBILE)
27511static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category)
27512{
27513 /* The "default" and "none" categories are treated different and should not be used as an input into this function. */
27514 MA_ASSERT(category != ma_ios_session_category_default);
27515 MA_ASSERT(category != ma_ios_session_category_none);
27516
27517 switch (category) {
27518 case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient;
27519 case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient;
27520 case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback;
27521 case ma_ios_session_category_record: return AVAudioSessionCategoryRecord;
27522 case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord;
27523 case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute;
27524 case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient;
27525 case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient;
27526 default: return AVAudioSessionCategoryAmbient;
27527 }
27528}
27529#endif
27530
27531static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
27532{
27533#if !defined(MA_APPLE_MOBILE)
27534 ma_result result;
27535#endif
27536
27537 MA_ASSERT(pConfig != NULL);
27538 MA_ASSERT(pContext != NULL);
27539
27540#if defined(MA_APPLE_MOBILE)
27541 @autoreleasepool {
27542 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
27543 AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions;
27544
27545 MA_ASSERT(pAudioSession != NULL);
27546
27547 if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) {
27548 /*
27549 I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails
27550 we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category.
27551 */
27552 #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH)
27553 options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
27554 #endif
27555
27556 if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) {
27557 /* Using PlayAndRecord */
27558 } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) {
27559 /* Using Playback */
27560 } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) {
27561 /* Using Record */
27562 } else {
27563 /* Leave as default? */
27564 }
27565 } else {
27566 if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) {
27567 if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) {
27568 return MA_INVALID_OPERATION; /* Failed to set session category. */
27569 }
27570 }
27571 }
27572
27573 if (!pConfig->coreaudio.noAudioSessionActivate) {
27574 if (![pAudioSession setActive:true error:nil]) {
27575 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to activate audio session.", MA_FAILED_TO_INIT_BACKEND);
27576 }
27577 }
27578 }
27579#endif
27580
27581#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
27582 pContext->coreaudio.hCoreFoundation = ma_dlopen(pContext, "CoreFoundation.framework/CoreFoundation");
27583 if (pContext->coreaudio.hCoreFoundation == NULL) {
27584 return MA_API_NOT_FOUND;
27585 }
27586
27587 pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFStringGetCString");
27588 pContext->coreaudio.CFRelease = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFRelease");
27589
27590
27591 pContext->coreaudio.hCoreAudio = ma_dlopen(pContext, "CoreAudio.framework/CoreAudio");
27592 if (pContext->coreaudio.hCoreAudio == NULL) {
27593 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
27594 return MA_API_NOT_FOUND;
27595 }
27596
27597 pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData");
27598 pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize");
27599 pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData");
27600 pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener");
27601 pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener");
27602
27603 /*
27604 It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still
27605 defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback.
27606 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
27607 AudioToolbox.
27608 */
27609 pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioUnit.framework/AudioUnit");
27610 if (pContext->coreaudio.hAudioUnit == NULL) {
27611 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
27612 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
27613 return MA_API_NOT_FOUND;
27614 }
27615
27616 if (ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) {
27617 /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */
27618 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
27619 pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioToolbox.framework/AudioToolbox");
27620 if (pContext->coreaudio.hAudioUnit == NULL) {
27621 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
27622 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
27623 return MA_API_NOT_FOUND;
27624 }
27625 }
27626
27627 pContext->coreaudio.AudioComponentFindNext = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext");
27628 pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose");
27629 pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew");
27630 pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart");
27631 pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop");
27632 pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener");
27633 pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo");
27634 pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty");
27635 pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty");
27636 pContext->coreaudio.AudioUnitInitialize = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitInitialize");
27637 pContext->coreaudio.AudioUnitRender = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitRender");
27638#else
27639 pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString;
27640 pContext->coreaudio.CFRelease = (ma_proc)CFRelease;
27641
27642 #if defined(MA_APPLE_DESKTOP)
27643 pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData;
27644 pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize;
27645 pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData;
27646 pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener;
27647 pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener;
27648 #endif
27649
27650 pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext;
27651 pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose;
27652 pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew;
27653 pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart;
27654 pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop;
27655 pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener;
27656 pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo;
27657 pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty;
27658 pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty;
27659 pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize;
27660 pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender;
27661#endif
27662
27663 /* Audio component. */
27664 {
27665 AudioComponentDescription desc;
27666 desc.componentType = kAudioUnitType_Output;
27667 #if defined(MA_APPLE_DESKTOP)
27668 desc.componentSubType = kAudioUnitSubType_HALOutput;
27669 #else
27670 desc.componentSubType = kAudioUnitSubType_RemoteIO;
27671 #endif
27672 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
27673 desc.componentFlags = 0;
27674 desc.componentFlagsMask = 0;
27675
27676 pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
27677 if (pContext->coreaudio.component == NULL) {
27678 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
27679 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
27680 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
27681 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
27682 #endif
27683 return MA_FAILED_TO_INIT_BACKEND;
27684 }
27685 }
27686
27687#if !defined(MA_APPLE_MOBILE)
27688 result = ma_context__init_device_tracking__coreaudio(pContext);
27689 if (result != MA_SUCCESS) {
27690 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
27691 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
27692 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
27693 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
27694 #endif
27695 return result;
27696 }
27697#endif
27698
27699 pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate;
27700
27701 pCallbacks->onContextInit = ma_context_init__coreaudio;
27702 pCallbacks->onContextUninit = ma_context_uninit__coreaudio;
27703 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio;
27704 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio;
27705 pCallbacks->onDeviceInit = ma_device_init__coreaudio;
27706 pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio;
27707 pCallbacks->onDeviceStart = ma_device_start__coreaudio;
27708 pCallbacks->onDeviceStop = ma_device_stop__coreaudio;
27709 pCallbacks->onDeviceRead = NULL;
27710 pCallbacks->onDeviceWrite = NULL;
27711 pCallbacks->onDeviceDataLoop = NULL;
27712
27713 return MA_SUCCESS;
27714}
27715#endif /* Core Audio */
27716
27717
27718
27719/******************************************************************************
27720
27721sndio Backend
27722
27723******************************************************************************/
27724#ifdef MA_HAS_SNDIO
27725#include <fcntl.h>
27726
27727/*
27728Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due
27729to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device
27730just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's
27731demand for it or if I can get it tested and debugged more thoroughly.
27732*/
27733#if 0
27734#if defined(__NetBSD__) || defined(__OpenBSD__)
27735#include <sys/audioio.h>
27736#endif
27737#if defined(__FreeBSD__) || defined(__DragonFly__)
27738#include <sys/soundcard.h>
27739#endif
27740#endif
27741
27742#define MA_SIO_DEVANY "default"
27743#define MA_SIO_PLAY 1
27744#define MA_SIO_REC 2
27745#define MA_SIO_NENC 8
27746#define MA_SIO_NCHAN 8
27747#define MA_SIO_NRATE 16
27748#define MA_SIO_NCONF 4
27749
27750struct ma_sio_hdl; /* <-- Opaque */
27751
27752struct ma_sio_par
27753{
27754 unsigned int bits;
27755 unsigned int bps;
27756 unsigned int sig;
27757 unsigned int le;
27758 unsigned int msb;
27759 unsigned int rchan;
27760 unsigned int pchan;
27761 unsigned int rate;
27762 unsigned int bufsz;
27763 unsigned int xrun;
27764 unsigned int round;
27765 unsigned int appbufsz;
27766 int __pad[3];
27767 unsigned int __magic;
27768};
27769
27770struct ma_sio_enc
27771{
27772 unsigned int bits;
27773 unsigned int bps;
27774 unsigned int sig;
27775 unsigned int le;
27776 unsigned int msb;
27777};
27778
27779struct ma_sio_conf
27780{
27781 unsigned int enc;
27782 unsigned int rchan;
27783 unsigned int pchan;
27784 unsigned int rate;
27785};
27786
27787struct ma_sio_cap
27788{
27789 struct ma_sio_enc enc[MA_SIO_NENC];
27790 unsigned int rchan[MA_SIO_NCHAN];
27791 unsigned int pchan[MA_SIO_NCHAN];
27792 unsigned int rate[MA_SIO_NRATE];
27793 int __pad[7];
27794 unsigned int nconf;
27795 struct ma_sio_conf confs[MA_SIO_NCONF];
27796};
27797
27798typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int);
27799typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*);
27800typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
27801typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
27802typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*);
27803typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t);
27804typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t);
27805typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*);
27806typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*);
27807typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*);
27808
27809static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */
27810{
27811 ma_uint32 i;
27812 for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) {
27813 if (g_maStandardSampleRatePriorities[i] == sampleRate) {
27814 return i;
27815 }
27816 }
27817
27818 return (ma_uint32)-1;
27819}
27820
27821static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb)
27822{
27823 /* We only support native-endian right now. */
27824 if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) {
27825 return ma_format_unknown;
27826 }
27827
27828 if (bits == 8 && bps == 1 && sig == 0) {
27829 return ma_format_u8;
27830 }
27831 if (bits == 16 && bps == 2 && sig == 1) {
27832 return ma_format_s16;
27833 }
27834 if (bits == 24 && bps == 3 && sig == 1) {
27835 return ma_format_s24;
27836 }
27837 if (bits == 24 && bps == 4 && sig == 1 && msb == 0) {
27838 /*return ma_format_s24_32;*/
27839 }
27840 if (bits == 32 && bps == 4 && sig == 1) {
27841 return ma_format_s32;
27842 }
27843
27844 return ma_format_unknown;
27845}
27846
27847static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps)
27848{
27849 ma_format bestFormat;
27850 unsigned int iConfig;
27851
27852 MA_ASSERT(caps != NULL);
27853
27854 bestFormat = ma_format_unknown;
27855 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
27856 unsigned int iEncoding;
27857 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
27858 unsigned int bits;
27859 unsigned int bps;
27860 unsigned int sig;
27861 unsigned int le;
27862 unsigned int msb;
27863 ma_format format;
27864
27865 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
27866 continue;
27867 }
27868
27869 bits = caps->enc[iEncoding].bits;
27870 bps = caps->enc[iEncoding].bps;
27871 sig = caps->enc[iEncoding].sig;
27872 le = caps->enc[iEncoding].le;
27873 msb = caps->enc[iEncoding].msb;
27874 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
27875 if (format == ma_format_unknown) {
27876 continue; /* Format not supported. */
27877 }
27878
27879 if (bestFormat == ma_format_unknown) {
27880 bestFormat = format;
27881 } else {
27882 if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */
27883 bestFormat = format;
27884 }
27885 }
27886 }
27887 }
27888
27889 return bestFormat;
27890}
27891
27892static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat)
27893{
27894 ma_uint32 maxChannels;
27895 unsigned int iConfig;
27896
27897 MA_ASSERT(caps != NULL);
27898 MA_ASSERT(requiredFormat != ma_format_unknown);
27899
27900 /* Just pick whatever configuration has the most channels. */
27901 maxChannels = 0;
27902 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
27903 /* The encoding should be of requiredFormat. */
27904 unsigned int iEncoding;
27905 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
27906 unsigned int iChannel;
27907 unsigned int bits;
27908 unsigned int bps;
27909 unsigned int sig;
27910 unsigned int le;
27911 unsigned int msb;
27912 ma_format format;
27913
27914 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
27915 continue;
27916 }
27917
27918 bits = caps->enc[iEncoding].bits;
27919 bps = caps->enc[iEncoding].bps;
27920 sig = caps->enc[iEncoding].sig;
27921 le = caps->enc[iEncoding].le;
27922 msb = caps->enc[iEncoding].msb;
27923 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
27924 if (format != requiredFormat) {
27925 continue;
27926 }
27927
27928 /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
27929 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
27930 unsigned int chan = 0;
27931 unsigned int channels;
27932
27933 if (deviceType == ma_device_type_playback) {
27934 chan = caps->confs[iConfig].pchan;
27935 } else {
27936 chan = caps->confs[iConfig].rchan;
27937 }
27938
27939 if ((chan & (1UL << iChannel)) == 0) {
27940 continue;
27941 }
27942
27943 if (deviceType == ma_device_type_playback) {
27944 channels = caps->pchan[iChannel];
27945 } else {
27946 channels = caps->rchan[iChannel];
27947 }
27948
27949 if (maxChannels < channels) {
27950 maxChannels = channels;
27951 }
27952 }
27953 }
27954 }
27955
27956 return maxChannels;
27957}
27958
27959static 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)
27960{
27961 ma_uint32 firstSampleRate;
27962 ma_uint32 bestSampleRate;
27963 unsigned int iConfig;
27964
27965 MA_ASSERT(caps != NULL);
27966 MA_ASSERT(requiredFormat != ma_format_unknown);
27967 MA_ASSERT(requiredChannels > 0);
27968 MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS);
27969
27970 firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */
27971 bestSampleRate = 0;
27972
27973 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
27974 /* The encoding should be of requiredFormat. */
27975 unsigned int iEncoding;
27976 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
27977 unsigned int iChannel;
27978 unsigned int bits;
27979 unsigned int bps;
27980 unsigned int sig;
27981 unsigned int le;
27982 unsigned int msb;
27983 ma_format format;
27984
27985 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
27986 continue;
27987 }
27988
27989 bits = caps->enc[iEncoding].bits;
27990 bps = caps->enc[iEncoding].bps;
27991 sig = caps->enc[iEncoding].sig;
27992 le = caps->enc[iEncoding].le;
27993 msb = caps->enc[iEncoding].msb;
27994 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
27995 if (format != requiredFormat) {
27996 continue;
27997 }
27998
27999 /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
28000 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
28001 unsigned int chan = 0;
28002 unsigned int channels;
28003 unsigned int iRate;
28004
28005 if (deviceType == ma_device_type_playback) {
28006 chan = caps->confs[iConfig].pchan;
28007 } else {
28008 chan = caps->confs[iConfig].rchan;
28009 }
28010
28011 if ((chan & (1UL << iChannel)) == 0) {
28012 continue;
28013 }
28014
28015 if (deviceType == ma_device_type_playback) {
28016 channels = caps->pchan[iChannel];
28017 } else {
28018 channels = caps->rchan[iChannel];
28019 }
28020
28021 if (channels != requiredChannels) {
28022 continue;
28023 }
28024
28025 /* Getting here means we have found a compatible encoding/channel pair. */
28026 for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
28027 ma_uint32 rate = (ma_uint32)caps->rate[iRate];
28028 ma_uint32 ratePriority;
28029
28030 if (firstSampleRate == 0) {
28031 firstSampleRate = rate;
28032 }
28033
28034 /* Disregard this rate if it's not a standard one. */
28035 ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate);
28036 if (ratePriority == (ma_uint32)-1) {
28037 continue;
28038 }
28039
28040 if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) { /* Lower = better. */
28041 bestSampleRate = rate;
28042 }
28043 }
28044 }
28045 }
28046 }
28047
28048 /* If a standard sample rate was not found just fall back to the first one that was iterated. */
28049 if (bestSampleRate == 0) {
28050 bestSampleRate = firstSampleRate;
28051 }
28052
28053 return bestSampleRate;
28054}
28055
28056
28057static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
28058{
28059 ma_bool32 isTerminating = MA_FALSE;
28060 struct ma_sio_hdl* handle;
28061
28062 MA_ASSERT(pContext != NULL);
28063 MA_ASSERT(callback != NULL);
28064
28065 /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */
28066
28067 /* Playback. */
28068 if (!isTerminating) {
28069 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0);
28070 if (handle != NULL) {
28071 /* Supports playback. */
28072 ma_device_info deviceInfo;
28073 MA_ZERO_OBJECT(&deviceInfo);
28074 ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY);
28075 ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
28076
28077 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
28078
28079 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
28080 }
28081 }
28082
28083 /* Capture. */
28084 if (!isTerminating) {
28085 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0);
28086 if (handle != NULL) {
28087 /* Supports capture. */
28088 ma_device_info deviceInfo;
28089 MA_ZERO_OBJECT(&deviceInfo);
28090 ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default");
28091 ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME);
28092
28093 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
28094
28095 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
28096 }
28097 }
28098
28099 return MA_SUCCESS;
28100}
28101
28102static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
28103{
28104 char devid[256];
28105 struct ma_sio_hdl* handle;
28106 struct ma_sio_cap caps;
28107 unsigned int iConfig;
28108
28109 MA_ASSERT(pContext != NULL);
28110
28111 /* We need to open the device before we can get information about it. */
28112 if (pDeviceID == NULL) {
28113 ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY);
28114 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME);
28115 } else {
28116 ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio);
28117 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid);
28118 }
28119
28120 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0);
28121 if (handle == NULL) {
28122 return MA_NO_DEVICE;
28123 }
28124
28125 if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) {
28126 return MA_ERROR;
28127 }
28128
28129 pDeviceInfo->nativeDataFormatCount = 0;
28130
28131 for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) {
28132 /*
28133 The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give
28134 preference to some formats over others.
28135 */
28136 unsigned int iEncoding;
28137 unsigned int iChannel;
28138 unsigned int iRate;
28139
28140 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
28141 unsigned int bits;
28142 unsigned int bps;
28143 unsigned int sig;
28144 unsigned int le;
28145 unsigned int msb;
28146 ma_format format;
28147
28148 if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) {
28149 continue;
28150 }
28151
28152 bits = caps.enc[iEncoding].bits;
28153 bps = caps.enc[iEncoding].bps;
28154 sig = caps.enc[iEncoding].sig;
28155 le = caps.enc[iEncoding].le;
28156 msb = caps.enc[iEncoding].msb;
28157 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
28158 if (format == ma_format_unknown) {
28159 continue; /* Format not supported. */
28160 }
28161
28162
28163 /* Channels. */
28164 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
28165 unsigned int chan = 0;
28166 unsigned int channels;
28167
28168 if (deviceType == ma_device_type_playback) {
28169 chan = caps.confs[iConfig].pchan;
28170 } else {
28171 chan = caps.confs[iConfig].rchan;
28172 }
28173
28174 if ((chan & (1UL << iChannel)) == 0) {
28175 continue;
28176 }
28177
28178 if (deviceType == ma_device_type_playback) {
28179 channels = caps.pchan[iChannel];
28180 } else {
28181 channels = caps.rchan[iChannel];
28182 }
28183
28184
28185 /* Sample Rates. */
28186 for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
28187 if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) {
28188 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0);
28189 }
28190 }
28191 }
28192 }
28193 }
28194
28195 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
28196 return MA_SUCCESS;
28197}
28198
28199static ma_result ma_device_uninit__sndio(ma_device* pDevice)
28200{
28201 MA_ASSERT(pDevice != NULL);
28202
28203 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28204 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
28205 }
28206
28207 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28208 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
28209 }
28210
28211 return MA_SUCCESS;
28212}
28213
28214static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
28215{
28216 const char* pDeviceName;
28217 ma_ptr handle;
28218 int openFlags = 0;
28219 struct ma_sio_cap caps;
28220 struct ma_sio_par par;
28221 const ma_device_id* pDeviceID;
28222 ma_format format;
28223 ma_uint32 channels;
28224 ma_uint32 sampleRate;
28225 ma_format internalFormat;
28226 ma_uint32 internalChannels;
28227 ma_uint32 internalSampleRate;
28228 ma_uint32 internalPeriodSizeInFrames;
28229 ma_uint32 internalPeriods;
28230
28231 MA_ASSERT(pConfig != NULL);
28232 MA_ASSERT(deviceType != ma_device_type_duplex);
28233 MA_ASSERT(pDevice != NULL);
28234
28235 if (deviceType == ma_device_type_capture) {
28236 openFlags = MA_SIO_REC;
28237 } else {
28238 openFlags = MA_SIO_PLAY;
28239 }
28240
28241 pDeviceID = pDescriptor->pDeviceID;
28242 format = pDescriptor->format;
28243 channels = pDescriptor->channels;
28244 sampleRate = pDescriptor->sampleRate;
28245
28246 pDeviceName = MA_SIO_DEVANY;
28247 if (pDeviceID != NULL) {
28248 pDeviceName = pDeviceID->sndio;
28249 }
28250
28251 handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0);
28252 if (handle == NULL) {
28253 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
28254 }
28255
28256 /* We need to retrieve the device caps to determine the most appropriate format to use. */
28257 if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) {
28258 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
28259 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps.", MA_ERROR);
28260 }
28261
28262 /*
28263 Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real
28264 way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this
28265 to the requested channels, regardless of whether or not the default channel count is requested.
28266
28267 For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the
28268 value returned by ma_find_best_channels_from_sio_cap__sndio().
28269 */
28270 if (deviceType == ma_device_type_capture) {
28271 if (format == ma_format_unknown) {
28272 format = ma_find_best_format_from_sio_cap__sndio(&caps);
28273 }
28274
28275 if (channels == 0) {
28276 if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
28277 channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
28278 } else {
28279 channels = MA_DEFAULT_CHANNELS;
28280 }
28281 }
28282 } else {
28283 if (format == ma_format_unknown) {
28284 format = ma_find_best_format_from_sio_cap__sndio(&caps);
28285 }
28286
28287 if (channels == 0) {
28288 if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
28289 channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
28290 } else {
28291 channels = MA_DEFAULT_CHANNELS;
28292 }
28293 }
28294 }
28295
28296 if (sampleRate == 0) {
28297 sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels);
28298 }
28299
28300
28301 ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par);
28302 par.msb = 0;
28303 par.le = ma_is_little_endian();
28304
28305 switch (format) {
28306 case ma_format_u8:
28307 {
28308 par.bits = 8;
28309 par.bps = 1;
28310 par.sig = 0;
28311 } break;
28312
28313 case ma_format_s24:
28314 {
28315 par.bits = 24;
28316 par.bps = 3;
28317 par.sig = 1;
28318 } break;
28319
28320 case ma_format_s32:
28321 {
28322 par.bits = 32;
28323 par.bps = 4;
28324 par.sig = 1;
28325 } break;
28326
28327 case ma_format_s16:
28328 case ma_format_f32:
28329 case ma_format_unknown:
28330 default:
28331 {
28332 par.bits = 16;
28333 par.bps = 2;
28334 par.sig = 1;
28335 } break;
28336 }
28337
28338 if (deviceType == ma_device_type_capture) {
28339 par.rchan = channels;
28340 } else {
28341 par.pchan = channels;
28342 }
28343
28344 par.rate = sampleRate;
28345
28346 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile);
28347
28348 par.round = internalPeriodSizeInFrames;
28349 par.appbufsz = par.round * pDescriptor->periodCount;
28350
28351 if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) {
28352 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
28353 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.", MA_FORMAT_NOT_SUPPORTED);
28354 }
28355
28356 if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) {
28357 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
28358 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size.", MA_FORMAT_NOT_SUPPORTED);
28359 }
28360
28361 internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb);
28362 internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan;
28363 internalSampleRate = par.rate;
28364 internalPeriods = par.appbufsz / par.round;
28365 internalPeriodSizeInFrames = par.round;
28366
28367 if (deviceType == ma_device_type_capture) {
28368 pDevice->sndio.handleCapture = handle;
28369 } else {
28370 pDevice->sndio.handlePlayback = handle;
28371 }
28372
28373 pDescriptor->format = internalFormat;
28374 pDescriptor->channels = internalChannels;
28375 pDescriptor->sampleRate = internalSampleRate;
28376 ma_get_standard_channel_map(ma_standard_channel_map_sndio, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
28377 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
28378 pDescriptor->periodCount = internalPeriods;
28379
28380 #ifdef MA_DEBUG_OUTPUT
28381 {
28382 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "DEVICE INFO\n");
28383 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Format: %s\n", ma_get_format_name(internalFormat));
28384 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Channels: %d\n", internalChannels);
28385 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Sample Rate: %d\n", internalSampleRate);
28386 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Period Size: %d\n", internalPeriodSizeInFrames);
28387 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Periods: %d\n", internalPeriods);
28388 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " appbufsz: %d\n", par.appbufsz);
28389 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " round: %d\n", par.round);
28390 }
28391 #endif
28392
28393 return MA_SUCCESS;
28394}
28395
28396static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
28397{
28398 MA_ASSERT(pDevice != NULL);
28399
28400 MA_ZERO_OBJECT(&pDevice->sndio);
28401
28402 if (pConfig->deviceType == ma_device_type_loopback) {
28403 return MA_DEVICE_TYPE_NOT_SUPPORTED;
28404 }
28405
28406 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
28407 ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
28408 if (result != MA_SUCCESS) {
28409 return result;
28410 }
28411 }
28412
28413 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
28414 ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
28415 if (result != MA_SUCCESS) {
28416 return result;
28417 }
28418 }
28419
28420 return MA_SUCCESS;
28421}
28422
28423static ma_result ma_device_start__sndio(ma_device* pDevice)
28424{
28425 MA_ASSERT(pDevice != NULL);
28426
28427 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28428 ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
28429 }
28430
28431 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
28432 ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */
28433 }
28434
28435 return MA_SUCCESS;
28436}
28437
28438static ma_result ma_device_stop__sndio(ma_device* pDevice)
28439{
28440 MA_ASSERT(pDevice != NULL);
28441
28442 /*
28443 From the documentation:
28444
28445 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
28446 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
28447 buffer is drained. In no case are samples in the play buffer discarded.
28448
28449 Therefore, sio_stop() performs all of the necessary draining for us.
28450 */
28451
28452 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28453 ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
28454 }
28455
28456 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
28457 ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
28458 }
28459
28460 return MA_SUCCESS;
28461}
28462
28463static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
28464{
28465 int result;
28466
28467 if (pFramesWritten != NULL) {
28468 *pFramesWritten = 0;
28469 }
28470
28471 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));
28472 if (result == 0) {
28473 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device.", MA_IO_ERROR);
28474 }
28475
28476 if (pFramesWritten != NULL) {
28477 *pFramesWritten = frameCount;
28478 }
28479
28480 return MA_SUCCESS;
28481}
28482
28483static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
28484{
28485 int result;
28486
28487 if (pFramesRead != NULL) {
28488 *pFramesRead = 0;
28489 }
28490
28491 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));
28492 if (result == 0) {
28493 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device.", MA_IO_ERROR);
28494 }
28495
28496 if (pFramesRead != NULL) {
28497 *pFramesRead = frameCount;
28498 }
28499
28500 return MA_SUCCESS;
28501}
28502
28503static ma_result ma_context_uninit__sndio(ma_context* pContext)
28504{
28505 MA_ASSERT(pContext != NULL);
28506 MA_ASSERT(pContext->backend == ma_backend_sndio);
28507
28508 (void)pContext;
28509 return MA_SUCCESS;
28510}
28511
28512static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
28513{
28514#ifndef MA_NO_RUNTIME_LINKING
28515 const char* libsndioNames[] = {
28516 "libsndio.so"
28517 };
28518 size_t i;
28519
28520 for (i = 0; i < ma_countof(libsndioNames); ++i) {
28521 pContext->sndio.sndioSO = ma_dlopen(pContext, libsndioNames[i]);
28522 if (pContext->sndio.sndioSO != NULL) {
28523 break;
28524 }
28525 }
28526
28527 if (pContext->sndio.sndioSO == NULL) {
28528 return MA_NO_BACKEND;
28529 }
28530
28531 pContext->sndio.sio_open = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_open");
28532 pContext->sndio.sio_close = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_close");
28533 pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_setpar");
28534 pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getpar");
28535 pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getcap");
28536 pContext->sndio.sio_write = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_write");
28537 pContext->sndio.sio_read = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_read");
28538 pContext->sndio.sio_start = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_start");
28539 pContext->sndio.sio_stop = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_stop");
28540 pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_initpar");
28541#else
28542 pContext->sndio.sio_open = sio_open;
28543 pContext->sndio.sio_close = sio_close;
28544 pContext->sndio.sio_setpar = sio_setpar;
28545 pContext->sndio.sio_getpar = sio_getpar;
28546 pContext->sndio.sio_getcap = sio_getcap;
28547 pContext->sndio.sio_write = sio_write;
28548 pContext->sndio.sio_read = sio_read;
28549 pContext->sndio.sio_start = sio_start;
28550 pContext->sndio.sio_stop = sio_stop;
28551 pContext->sndio.sio_initpar = sio_initpar;
28552#endif
28553
28554 pCallbacks->onContextInit = ma_context_init__sndio;
28555 pCallbacks->onContextUninit = ma_context_uninit__sndio;
28556 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio;
28557 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio;
28558 pCallbacks->onDeviceInit = ma_device_init__sndio;
28559 pCallbacks->onDeviceUninit = ma_device_uninit__sndio;
28560 pCallbacks->onDeviceStart = ma_device_start__sndio;
28561 pCallbacks->onDeviceStop = ma_device_stop__sndio;
28562 pCallbacks->onDeviceRead = ma_device_read__sndio;
28563 pCallbacks->onDeviceWrite = ma_device_write__sndio;
28564 pCallbacks->onDeviceDataLoop = NULL;
28565
28566 (void)pConfig;
28567 return MA_SUCCESS;
28568}
28569#endif /* sndio */
28570
28571
28572
28573/******************************************************************************
28574
28575audio(4) Backend
28576
28577******************************************************************************/
28578#ifdef MA_HAS_AUDIO4
28579#include <fcntl.h>
28580#include <poll.h>
28581#include <errno.h>
28582#include <sys/stat.h>
28583#include <sys/types.h>
28584#include <sys/ioctl.h>
28585#include <sys/audioio.h>
28586
28587#if defined(__OpenBSD__)
28588 #include <sys/param.h>
28589 #if defined(OpenBSD) && OpenBSD >= 201709
28590 #define MA_AUDIO4_USE_NEW_API
28591 #endif
28592#endif
28593
28594static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex)
28595{
28596 size_t baseLen;
28597
28598 MA_ASSERT(id != NULL);
28599 MA_ASSERT(idSize > 0);
28600 MA_ASSERT(deviceIndex >= 0);
28601
28602 baseLen = strlen(base);
28603 MA_ASSERT(idSize > baseLen);
28604
28605 ma_strcpy_s(id, idSize, base);
28606 ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10);
28607}
28608
28609static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut)
28610{
28611 size_t idLen;
28612 size_t baseLen;
28613 const char* deviceIndexStr;
28614
28615 MA_ASSERT(id != NULL);
28616 MA_ASSERT(base != NULL);
28617 MA_ASSERT(pIndexOut != NULL);
28618
28619 idLen = strlen(id);
28620 baseLen = strlen(base);
28621 if (idLen <= baseLen) {
28622 return MA_ERROR; /* Doesn't look like the id starts with the base. */
28623 }
28624
28625 if (strncmp(id, base, baseLen) != 0) {
28626 return MA_ERROR; /* ID does not begin with base. */
28627 }
28628
28629 deviceIndexStr = id + baseLen;
28630 if (deviceIndexStr[0] == '\0') {
28631 return MA_ERROR; /* No index specified in the ID. */
28632 }
28633
28634 if (pIndexOut) {
28635 *pIndexOut = atoi(deviceIndexStr);
28636 }
28637
28638 return MA_SUCCESS;
28639}
28640
28641
28642#if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
28643static ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision)
28644{
28645 if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) {
28646 return ma_format_u8;
28647 } else {
28648 if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) {
28649 if (precision == 16) {
28650 return ma_format_s16;
28651 } else if (precision == 24) {
28652 return ma_format_s24;
28653 } else if (precision == 32) {
28654 return ma_format_s32;
28655 }
28656 } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) {
28657 if (precision == 16) {
28658 return ma_format_s16;
28659 } else if (precision == 24) {
28660 return ma_format_s24;
28661 } else if (precision == 32) {
28662 return ma_format_s32;
28663 }
28664 }
28665 }
28666
28667 return ma_format_unknown; /* Encoding not supported. */
28668}
28669
28670static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision)
28671{
28672 MA_ASSERT(pEncoding != NULL);
28673 MA_ASSERT(pPrecision != NULL);
28674
28675 switch (format)
28676 {
28677 case ma_format_u8:
28678 {
28679 *pEncoding = AUDIO_ENCODING_ULINEAR;
28680 *pPrecision = 8;
28681 } break;
28682
28683 case ma_format_s24:
28684 {
28685 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
28686 *pPrecision = 24;
28687 } break;
28688
28689 case ma_format_s32:
28690 {
28691 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
28692 *pPrecision = 32;
28693 } break;
28694
28695 case ma_format_s16:
28696 case ma_format_f32:
28697 case ma_format_unknown:
28698 default:
28699 {
28700 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
28701 *pPrecision = 16;
28702 } break;
28703 }
28704}
28705
28706static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo)
28707{
28708 return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision);
28709}
28710
28711static ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat)
28712{
28713 audio_encoding_t encoding;
28714 ma_uint32 iFormat;
28715 int counter = 0;
28716
28717 /* First check to see if the preferred format is supported. */
28718 if (preferredFormat != ma_format_unknown) {
28719 counter = 0;
28720 for (;;) {
28721 MA_ZERO_OBJECT(&encoding);
28722 encoding.index = counter;
28723 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
28724 break;
28725 }
28726
28727 if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) {
28728 return preferredFormat; /* Found the preferred format. */
28729 }
28730
28731 /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */
28732 counter += 1;
28733 }
28734 }
28735
28736 /* Getting here means our preferred format is not supported, so fall back to our standard priorities. */
28737 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
28738 ma_format format = g_maFormatPriorities[iFormat];
28739
28740 counter = 0;
28741 for (;;) {
28742 MA_ZERO_OBJECT(&encoding);
28743 encoding.index = counter;
28744 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
28745 break;
28746 }
28747
28748 if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) {
28749 return format; /* Found a workable format. */
28750 }
28751
28752 /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */
28753 counter += 1;
28754 }
28755 }
28756
28757 /* Getting here means not appropriate format was found. */
28758 return ma_format_unknown;
28759}
28760#else
28761static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par)
28762{
28763 if (par->bits == 8 && par->bps == 1 && par->sig == 0) {
28764 return ma_format_u8;
28765 }
28766 if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) {
28767 return ma_format_s16;
28768 }
28769 if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) {
28770 return ma_format_s24;
28771 }
28772 if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) {
28773 return ma_format_f32;
28774 }
28775
28776 /* Format not supported. */
28777 return ma_format_unknown;
28778}
28779#endif
28780
28781static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo)
28782{
28783 audio_device_t fdDevice;
28784
28785 MA_ASSERT(pContext != NULL);
28786 MA_ASSERT(fd >= 0);
28787 MA_ASSERT(pDeviceInfo != NULL);
28788
28789 (void)pContext;
28790 (void)deviceType;
28791
28792 if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) {
28793 return MA_ERROR; /* Failed to retrieve device info. */
28794 }
28795
28796 /* Name. */
28797 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name);
28798
28799 #if !defined(MA_AUDIO4_USE_NEW_API)
28800 {
28801 audio_info_t fdInfo;
28802 int counter = 0;
28803 ma_uint32 channels;
28804 ma_uint32 sampleRate;
28805
28806 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
28807 return MA_ERROR;
28808 }
28809
28810 if (deviceType == ma_device_type_playback) {
28811 channels = fdInfo.play.channels;
28812 sampleRate = fdInfo.play.sample_rate;
28813 } else {
28814 channels = fdInfo.record.channels;
28815 sampleRate = fdInfo.record.sample_rate;
28816 }
28817
28818 /* Supported formats. We get this by looking at the encodings. */
28819 pDeviceInfo->nativeDataFormatCount = 0;
28820 for (;;) {
28821 audio_encoding_t encoding;
28822 ma_format format;
28823
28824 MA_ZERO_OBJECT(&encoding);
28825 encoding.index = counter;
28826 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
28827 break;
28828 }
28829
28830 format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision);
28831 if (format != ma_format_unknown) {
28832 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0);
28833 }
28834
28835 counter += 1;
28836 }
28837 }
28838 #else
28839 {
28840 struct audio_swpar fdPar;
28841 ma_format format;
28842 ma_uint32 channels;
28843 ma_uint32 sampleRate;
28844
28845 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
28846 return MA_ERROR;
28847 }
28848
28849 format = ma_format_from_swpar__audio4(&fdPar);
28850 if (format == ma_format_unknown) {
28851 return MA_FORMAT_NOT_SUPPORTED;
28852 }
28853
28854 if (deviceType == ma_device_type_playback) {
28855 channels = fdPar.pchan;
28856 } else {
28857 channels = fdPar.rchan;
28858 }
28859
28860 sampleRate = fdPar.rate;
28861
28862 pDeviceInfo->nativeDataFormatCount = 0;
28863 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0);
28864 }
28865 #endif
28866
28867 return MA_SUCCESS;
28868}
28869
28870static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
28871{
28872 const int maxDevices = 64;
28873 char devpath[256];
28874 int iDevice;
28875
28876 MA_ASSERT(pContext != NULL);
28877 MA_ASSERT(callback != NULL);
28878
28879 /*
28880 Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN"
28881 version here since we can open it even when another process has control of the "/dev/audioN" device.
28882 */
28883 for (iDevice = 0; iDevice < maxDevices; ++iDevice) {
28884 struct stat st;
28885 int fd;
28886 ma_bool32 isTerminating = MA_FALSE;
28887
28888 ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl");
28889 ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10);
28890
28891 if (stat(devpath, &st) < 0) {
28892 break;
28893 }
28894
28895 /* The device exists, but we need to check if it's usable as playback and/or capture. */
28896
28897 /* Playback. */
28898 if (!isTerminating) {
28899 fd = open(devpath, O_RDONLY, 0);
28900 if (fd >= 0) {
28901 /* Supports playback. */
28902 ma_device_info deviceInfo;
28903 MA_ZERO_OBJECT(&deviceInfo);
28904 ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
28905 if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) {
28906 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
28907 }
28908
28909 close(fd);
28910 }
28911 }
28912
28913 /* Capture. */
28914 if (!isTerminating) {
28915 fd = open(devpath, O_WRONLY, 0);
28916 if (fd >= 0) {
28917 /* Supports capture. */
28918 ma_device_info deviceInfo;
28919 MA_ZERO_OBJECT(&deviceInfo);
28920 ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
28921 if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) {
28922 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
28923 }
28924
28925 close(fd);
28926 }
28927 }
28928
28929 if (isTerminating) {
28930 break;
28931 }
28932 }
28933
28934 return MA_SUCCESS;
28935}
28936
28937static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
28938{
28939 int fd = -1;
28940 int deviceIndex = -1;
28941 char ctlid[256];
28942 ma_result result;
28943
28944 MA_ASSERT(pContext != NULL);
28945
28946 /*
28947 We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number
28948 from the device ID which will be in "/dev/audioN" format.
28949 */
28950 if (pDeviceID == NULL) {
28951 /* Default device. */
28952 ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl");
28953 } else {
28954 /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */
28955 result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex);
28956 if (result != MA_SUCCESS) {
28957 return result;
28958 }
28959
28960 ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex);
28961 }
28962
28963 fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0);
28964 if (fd == -1) {
28965 return MA_NO_DEVICE;
28966 }
28967
28968 if (deviceIndex == -1) {
28969 ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio");
28970 } else {
28971 ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex);
28972 }
28973
28974 result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo);
28975
28976 close(fd);
28977 return result;
28978}
28979
28980static ma_result ma_device_uninit__audio4(ma_device* pDevice)
28981{
28982 MA_ASSERT(pDevice != NULL);
28983
28984 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28985 close(pDevice->audio4.fdCapture);
28986 }
28987
28988 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
28989 close(pDevice->audio4.fdPlayback);
28990 }
28991
28992 return MA_SUCCESS;
28993}
28994
28995static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
28996{
28997 const char* pDefaultDeviceNames[] = {
28998 "/dev/audio",
28999 "/dev/audio0"
29000 };
29001 int fd;
29002 int fdFlags = 0;
29003 ma_format internalFormat;
29004 ma_uint32 internalChannels;
29005 ma_uint32 internalSampleRate;
29006 ma_uint32 internalPeriodSizeInFrames;
29007 ma_uint32 internalPeriods;
29008
29009 MA_ASSERT(pConfig != NULL);
29010 MA_ASSERT(deviceType != ma_device_type_duplex);
29011 MA_ASSERT(pDevice != NULL);
29012
29013 /* The first thing to do is open the file. */
29014 if (deviceType == ma_device_type_capture) {
29015 fdFlags = O_RDONLY;
29016 } else {
29017 fdFlags = O_WRONLY;
29018 }
29019 /*fdFlags |= O_NONBLOCK;*/
29020
29021 if (pDescriptor->pDeviceID == NULL) {
29022 /* Default device. */
29023 size_t iDevice;
29024 for (iDevice = 0; iDevice < ma_countof(pDefaultDeviceNames); ++iDevice) {
29025 fd = open(pDefaultDeviceNames[iDevice], fdFlags, 0);
29026 if (fd != -1) {
29027 break;
29028 }
29029 }
29030 } else {
29031 /* Specific device. */
29032 fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0);
29033 }
29034
29035 if (fd == -1) {
29036 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device.", ma_result_from_errno(errno));
29037 }
29038
29039 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
29040 {
29041 audio_info_t fdInfo;
29042
29043 /*
29044 The documentation is a little bit unclear to me as to how it handles formats. It says the
29045 following:
29046
29047 Regardless of formats supported by underlying driver, the audio driver accepts the
29048 following formats.
29049
29050 By then the next sentence says this:
29051
29052 `encoding` and `precision` are one of the values obtained by AUDIO_GETENC.
29053
29054 It sounds like a direct contradiction to me. I'm going to play this safe any only use the
29055 best sample format returned by AUDIO_GETENC. If the requested format is supported we'll
29056 use that, but otherwise we'll just use our standard format priorities to pick an
29057 appropriate one.
29058 */
29059 AUDIO_INITINFO(&fdInfo);
29060
29061 /* We get the driver to do as much of the data conversion as possible. */
29062 if (deviceType == ma_device_type_capture) {
29063 fdInfo.mode = AUMODE_RECORD;
29064 ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision);
29065
29066 if (pDescriptor->channels != 0) {
29067 fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */
29068 }
29069
29070 if (pDescriptor->sampleRate != 0) {
29071 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`.) */
29072 }
29073 } else {
29074 fdInfo.mode = AUMODE_PLAY;
29075 ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision);
29076
29077 if (pDescriptor->channels != 0) {
29078 fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */
29079 }
29080
29081 if (pDescriptor->sampleRate != 0) {
29082 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`.) */
29083 }
29084 }
29085
29086 if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
29087 close(fd);
29088 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
29089 }
29090
29091 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
29092 close(fd);
29093 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
29094 }
29095
29096 if (deviceType == ma_device_type_capture) {
29097 internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record);
29098 internalChannels = fdInfo.record.channels;
29099 internalSampleRate = fdInfo.record.sample_rate;
29100 } else {
29101 internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play);
29102 internalChannels = fdInfo.play.channels;
29103 internalSampleRate = fdInfo.play.sample_rate;
29104 }
29105
29106 if (internalFormat == ma_format_unknown) {
29107 close(fd);
29108 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable.", MA_FORMAT_NOT_SUPPORTED);
29109 }
29110
29111 /* Buffer. */
29112 {
29113 ma_uint32 internalPeriodSizeInBytes;
29114
29115 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile);
29116
29117 internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
29118 if (internalPeriodSizeInBytes < 16) {
29119 internalPeriodSizeInBytes = 16;
29120 }
29121
29122 internalPeriods = pDescriptor->periodCount;
29123 if (internalPeriods < 2) {
29124 internalPeriods = 2;
29125 }
29126
29127 /* What miniaudio calls a period, audio4 calls a block. */
29128 AUDIO_INITINFO(&fdInfo);
29129 fdInfo.hiwat = internalPeriods;
29130 fdInfo.lowat = internalPeriods-1;
29131 fdInfo.blocksize = internalPeriodSizeInBytes;
29132 if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
29133 close(fd);
29134 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
29135 }
29136
29137 internalPeriods = fdInfo.hiwat;
29138 internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels);
29139 }
29140 }
29141 #else
29142 {
29143 struct audio_swpar fdPar;
29144
29145 /* 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. */
29146 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
29147 close(fd);
29148 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters.", MA_FORMAT_NOT_SUPPORTED);
29149 }
29150
29151 internalFormat = ma_format_from_swpar__audio4(&fdPar);
29152 internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
29153 internalSampleRate = fdPar.rate;
29154
29155 if (internalFormat == ma_format_unknown) {
29156 close(fd);
29157 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable.", MA_FORMAT_NOT_SUPPORTED);
29158 }
29159
29160 /* Buffer. */
29161 {
29162 ma_uint32 internalPeriodSizeInBytes;
29163
29164 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile);
29165
29166 /* What miniaudio calls a period, audio4 calls a block. */
29167 internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
29168 if (internalPeriodSizeInBytes < 16) {
29169 internalPeriodSizeInBytes = 16;
29170 }
29171
29172 fdPar.nblks = pDescriptor->periodCount;
29173 fdPar.round = internalPeriodSizeInBytes;
29174
29175 if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) {
29176 close(fd);
29177 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters.", MA_FORMAT_NOT_SUPPORTED);
29178 }
29179
29180 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
29181 close(fd);
29182 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters.", MA_FORMAT_NOT_SUPPORTED);
29183 }
29184 }
29185
29186 internalFormat = ma_format_from_swpar__audio4(&fdPar);
29187 internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
29188 internalSampleRate = fdPar.rate;
29189 internalPeriods = fdPar.nblks;
29190 internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels);
29191 }
29192 #endif
29193
29194 if (internalFormat == ma_format_unknown) {
29195 close(fd);
29196 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable.", MA_FORMAT_NOT_SUPPORTED);
29197 }
29198
29199 if (deviceType == ma_device_type_capture) {
29200 pDevice->audio4.fdCapture = fd;
29201 } else {
29202 pDevice->audio4.fdPlayback = fd;
29203 }
29204
29205 pDescriptor->format = internalFormat;
29206 pDescriptor->channels = internalChannels;
29207 pDescriptor->sampleRate = internalSampleRate;
29208 ma_get_standard_channel_map(ma_standard_channel_map_sound4, internalChannels, pDescriptor->channelMap);
29209 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
29210 pDescriptor->periodCount = internalPeriods;
29211
29212 return MA_SUCCESS;
29213}
29214
29215static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
29216{
29217 MA_ASSERT(pDevice != NULL);
29218
29219 MA_ZERO_OBJECT(&pDevice->audio4);
29220
29221 if (pConfig->deviceType == ma_device_type_loopback) {
29222 return MA_DEVICE_TYPE_NOT_SUPPORTED;
29223 }
29224
29225 pDevice->audio4.fdCapture = -1;
29226 pDevice->audio4.fdPlayback = -1;
29227
29228 /*
29229 The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD
29230 introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as
29231 I'm aware.
29232 */
29233#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000
29234 /* NetBSD 8.0+ */
29235 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
29236 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
29237 return MA_SHARE_MODE_NOT_SUPPORTED;
29238 }
29239#else
29240 /* All other flavors. */
29241#endif
29242
29243 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
29244 ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
29245 if (result != MA_SUCCESS) {
29246 return result;
29247 }
29248 }
29249
29250 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
29251 ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
29252 if (result != MA_SUCCESS) {
29253 if (pConfig->deviceType == ma_device_type_duplex) {
29254 close(pDevice->audio4.fdCapture);
29255 }
29256 return result;
29257 }
29258 }
29259
29260 return MA_SUCCESS;
29261}
29262
29263static ma_result ma_device_start__audio4(ma_device* pDevice)
29264{
29265 MA_ASSERT(pDevice != NULL);
29266
29267 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
29268 if (pDevice->audio4.fdCapture == -1) {
29269 return MA_INVALID_ARGS;
29270 }
29271 }
29272
29273 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
29274 if (pDevice->audio4.fdPlayback == -1) {
29275 return MA_INVALID_ARGS;
29276 }
29277 }
29278
29279 return MA_SUCCESS;
29280}
29281
29282static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd)
29283{
29284 if (fd == -1) {
29285 return MA_INVALID_ARGS;
29286 }
29287
29288#if !defined(MA_AUDIO4_USE_NEW_API)
29289 if (ioctl(fd, AUDIO_FLUSH, 0) < 0) {
29290 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed.", ma_result_from_errno(errno));
29291 }
29292#else
29293 if (ioctl(fd, AUDIO_STOP, 0) < 0) {
29294 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed.", ma_result_from_errno(errno));
29295 }
29296#endif
29297
29298 return MA_SUCCESS;
29299}
29300
29301static ma_result ma_device_stop__audio4(ma_device* pDevice)
29302{
29303 MA_ASSERT(pDevice != NULL);
29304
29305 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
29306 ma_result result;
29307
29308 result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture);
29309 if (result != MA_SUCCESS) {
29310 return result;
29311 }
29312 }
29313
29314 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
29315 ma_result result;
29316
29317 /* 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. */
29318 #if !defined(MA_AUDIO4_USE_NEW_API)
29319 ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0);
29320 #endif
29321
29322 /* Here is where the device is stopped immediately. */
29323 result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback);
29324 if (result != MA_SUCCESS) {
29325 return result;
29326 }
29327 }
29328
29329 return MA_SUCCESS;
29330}
29331
29332static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
29333{
29334 int result;
29335
29336 if (pFramesWritten != NULL) {
29337 *pFramesWritten = 0;
29338 }
29339
29340 result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
29341 if (result < 0) {
29342 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device.", ma_result_from_errno(errno));
29343 }
29344
29345 if (pFramesWritten != NULL) {
29346 *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
29347 }
29348
29349 return MA_SUCCESS;
29350}
29351
29352static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
29353{
29354 int result;
29355
29356 if (pFramesRead != NULL) {
29357 *pFramesRead = 0;
29358 }
29359
29360 result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
29361 if (result < 0) {
29362 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device.", ma_result_from_errno(errno));
29363 }
29364
29365 if (pFramesRead != NULL) {
29366 *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
29367 }
29368
29369 return MA_SUCCESS;
29370}
29371
29372static ma_result ma_context_uninit__audio4(ma_context* pContext)
29373{
29374 MA_ASSERT(pContext != NULL);
29375 MA_ASSERT(pContext->backend == ma_backend_audio4);
29376
29377 (void)pContext;
29378 return MA_SUCCESS;
29379}
29380
29381static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
29382{
29383 MA_ASSERT(pContext != NULL);
29384
29385 (void)pConfig;
29386
29387 pCallbacks->onContextInit = ma_context_init__audio4;
29388 pCallbacks->onContextUninit = ma_context_uninit__audio4;
29389 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4;
29390 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4;
29391 pCallbacks->onDeviceInit = ma_device_init__audio4;
29392 pCallbacks->onDeviceUninit = ma_device_uninit__audio4;
29393 pCallbacks->onDeviceStart = ma_device_start__audio4;
29394 pCallbacks->onDeviceStop = ma_device_stop__audio4;
29395 pCallbacks->onDeviceRead = ma_device_read__audio4;
29396 pCallbacks->onDeviceWrite = ma_device_write__audio4;
29397 pCallbacks->onDeviceDataLoop = NULL;
29398
29399 return MA_SUCCESS;
29400}
29401#endif /* audio4 */
29402
29403
29404/******************************************************************************
29405
29406OSS Backend
29407
29408******************************************************************************/
29409#ifdef MA_HAS_OSS
29410#include <sys/ioctl.h>
29411#include <unistd.h>
29412#include <fcntl.h>
29413#include <sys/soundcard.h>
29414
29415#ifndef SNDCTL_DSP_HALT
29416#define SNDCTL_DSP_HALT SNDCTL_DSP_RESET
29417#endif
29418
29419#define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp"
29420
29421static int ma_open_temp_device__oss()
29422{
29423 /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */
29424 int fd = open("/dev/mixer", O_RDONLY, 0);
29425 if (fd >= 0) {
29426 return fd;
29427 }
29428
29429 return -1;
29430}
29431
29432static 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)
29433{
29434 const char* deviceName;
29435 int flags;
29436
29437 MA_ASSERT(pContext != NULL);
29438 MA_ASSERT(pfd != NULL);
29439 (void)pContext;
29440
29441 *pfd = -1;
29442
29443 /* This function should only be called for playback or capture, not duplex. */
29444 if (deviceType == ma_device_type_duplex) {
29445 return MA_INVALID_ARGS;
29446 }
29447
29448 deviceName = MA_OSS_DEFAULT_DEVICE_NAME;
29449 if (pDeviceID != NULL) {
29450 deviceName = pDeviceID->oss;
29451 }
29452
29453 flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY;
29454 if (shareMode == ma_share_mode_exclusive) {
29455 flags |= O_EXCL;
29456 }
29457
29458 *pfd = open(deviceName, flags, 0);
29459 if (*pfd == -1) {
29460 return ma_result_from_errno(errno);
29461 }
29462
29463 return MA_SUCCESS;
29464}
29465
29466static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
29467{
29468 int fd;
29469 oss_sysinfo si;
29470 int result;
29471
29472 MA_ASSERT(pContext != NULL);
29473 MA_ASSERT(callback != NULL);
29474
29475 fd = ma_open_temp_device__oss();
29476 if (fd == -1) {
29477 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.", MA_NO_BACKEND);
29478 }
29479
29480 result = ioctl(fd, SNDCTL_SYSINFO, &si);
29481 if (result != -1) {
29482 int iAudioDevice;
29483 for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
29484 oss_audioinfo ai;
29485 ai.dev = iAudioDevice;
29486 result = ioctl(fd, SNDCTL_AUDIOINFO, &ai);
29487 if (result != -1) {
29488 if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */
29489 ma_device_info deviceInfo;
29490 ma_bool32 isTerminating = MA_FALSE;
29491
29492 MA_ZERO_OBJECT(&deviceInfo);
29493
29494 /* ID */
29495 ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1);
29496
29497 /*
29498 The human readable device name should be in the "ai.handle" variable, but it can
29499 sometimes be empty in which case we just fall back to "ai.name" which is less user
29500 friendly, but usually has a value.
29501 */
29502 if (ai.handle[0] != '\0') {
29503 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1);
29504 } else {
29505 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1);
29506 }
29507
29508 /* The device can be both playback and capture. */
29509 if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) {
29510 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
29511 }
29512 if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) {
29513 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
29514 }
29515
29516 if (isTerminating) {
29517 break;
29518 }
29519 }
29520 }
29521 }
29522 } else {
29523 close(fd);
29524 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MA_NO_BACKEND);
29525 }
29526
29527 close(fd);
29528 return MA_SUCCESS;
29529}
29530
29531static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo)
29532{
29533 unsigned int minChannels;
29534 unsigned int maxChannels;
29535 unsigned int iRate;
29536
29537 MA_ASSERT(pContext != NULL);
29538 MA_ASSERT(pAudioInfo != NULL);
29539 MA_ASSERT(pDeviceInfo != NULL);
29540
29541 /* If we support all channels we just report 0. */
29542 minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
29543 maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
29544
29545 /*
29546 OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness,
29547 which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which
29548 case we'll need to use min_rate and max_rate and report only standard rates.
29549 */
29550 if (pAudioInfo->nrates > 0) {
29551 for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) {
29552 unsigned int rate = pAudioInfo->rates[iRate];
29553
29554 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
29555 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. */
29556 } else {
29557 unsigned int iChannel;
29558 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
29559 ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0);
29560 }
29561 }
29562 }
29563 } else {
29564 for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) {
29565 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate];
29566
29567 if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) {
29568 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
29569 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. */
29570 } else {
29571 unsigned int iChannel;
29572 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
29573 ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0);
29574 }
29575 }
29576 }
29577 }
29578 }
29579}
29580
29581static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
29582{
29583 ma_bool32 foundDevice;
29584 int fdTemp;
29585 oss_sysinfo si;
29586 int result;
29587
29588 MA_ASSERT(pContext != NULL);
29589
29590 /* Handle the default device a little differently. */
29591 if (pDeviceID == NULL) {
29592 if (deviceType == ma_device_type_playback) {
29593 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
29594 } else {
29595 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
29596 }
29597
29598 return MA_SUCCESS;
29599 }
29600
29601
29602 /* If we get here it means we are _not_ using the default device. */
29603 foundDevice = MA_FALSE;
29604
29605 fdTemp = ma_open_temp_device__oss();
29606 if (fdTemp == -1) {
29607 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.", MA_NO_BACKEND);
29608 }
29609
29610 result = ioctl(fdTemp, SNDCTL_SYSINFO, &si);
29611 if (result != -1) {
29612 int iAudioDevice;
29613 for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
29614 oss_audioinfo ai;
29615 ai.dev = iAudioDevice;
29616 result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai);
29617 if (result != -1) {
29618 if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) {
29619 /* It has the same name, so now just confirm the type. */
29620 if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) ||
29621 (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) {
29622 unsigned int formatMask;
29623
29624 /* ID */
29625 ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1);
29626
29627 /*
29628 The human readable device name should be in the "ai.handle" variable, but it can
29629 sometimes be empty in which case we just fall back to "ai.name" which is less user
29630 friendly, but usually has a value.
29631 */
29632 if (ai.handle[0] != '\0') {
29633 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1);
29634 } else {
29635 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1);
29636 }
29637
29638
29639 pDeviceInfo->nativeDataFormatCount = 0;
29640
29641 if (deviceType == ma_device_type_playback) {
29642 formatMask = ai.oformats;
29643 } else {
29644 formatMask = ai.iformats;
29645 }
29646
29647 if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) {
29648 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo);
29649 }
29650 if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) {
29651 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo);
29652 }
29653 if ((formatMask & AFMT_U8) != 0) {
29654 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo);
29655 }
29656
29657 foundDevice = MA_TRUE;
29658 break;
29659 }
29660 }
29661 }
29662 }
29663 } else {
29664 close(fdTemp);
29665 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MA_NO_BACKEND);
29666 }
29667
29668
29669 close(fdTemp);
29670
29671 if (!foundDevice) {
29672 return MA_NO_DEVICE;
29673 }
29674
29675 return MA_SUCCESS;
29676}
29677
29678static ma_result ma_device_uninit__oss(ma_device* pDevice)
29679{
29680 MA_ASSERT(pDevice != NULL);
29681
29682 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
29683 close(pDevice->oss.fdCapture);
29684 }
29685
29686 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
29687 close(pDevice->oss.fdPlayback);
29688 }
29689
29690 return MA_SUCCESS;
29691}
29692
29693static int ma_format_to_oss(ma_format format)
29694{
29695 int ossFormat = AFMT_U8;
29696 switch (format) {
29697 case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
29698 case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
29699 case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
29700 case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
29701 case ma_format_u8:
29702 default: ossFormat = AFMT_U8; break;
29703 }
29704
29705 return ossFormat;
29706}
29707
29708static ma_format ma_format_from_oss(int ossFormat)
29709{
29710 if (ossFormat == AFMT_U8) {
29711 return ma_format_u8;
29712 } else {
29713 if (ma_is_little_endian()) {
29714 switch (ossFormat) {
29715 case AFMT_S16_LE: return ma_format_s16;
29716 case AFMT_S32_LE: return ma_format_s32;
29717 default: return ma_format_unknown;
29718 }
29719 } else {
29720 switch (ossFormat) {
29721 case AFMT_S16_BE: return ma_format_s16;
29722 case AFMT_S32_BE: return ma_format_s32;
29723 default: return ma_format_unknown;
29724 }
29725 }
29726 }
29727
29728 return ma_format_unknown;
29729}
29730
29731static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
29732{
29733 ma_result result;
29734 int ossResult;
29735 int fd;
29736 const ma_device_id* pDeviceID = NULL;
29737 ma_share_mode shareMode;
29738 int ossFormat;
29739 int ossChannels;
29740 int ossSampleRate;
29741 int ossFragment;
29742
29743 MA_ASSERT(pDevice != NULL);
29744 MA_ASSERT(pConfig != NULL);
29745 MA_ASSERT(deviceType != ma_device_type_duplex);
29746
29747 pDeviceID = pDescriptor->pDeviceID;
29748 shareMode = pDescriptor->shareMode;
29749 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. */
29750 ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS;
29751 ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE;
29752
29753 result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd);
29754 if (result != MA_SUCCESS) {
29755 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", result);
29756 }
29757
29758 /*
29759 The OSS documantation is very clear about the order we should be initializing the device's properties:
29760 1) Format
29761 2) Channels
29762 3) Sample rate.
29763 */
29764
29765 /* Format. */
29766 ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat);
29767 if (ossResult == -1) {
29768 close(fd);
29769 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format.", MA_FORMAT_NOT_SUPPORTED);
29770 }
29771
29772 /* Channels. */
29773 ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels);
29774 if (ossResult == -1) {
29775 close(fd);
29776 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count.", MA_FORMAT_NOT_SUPPORTED);
29777 }
29778
29779 /* Sample Rate. */
29780 ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate);
29781 if (ossResult == -1) {
29782 close(fd);
29783 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate.", MA_FORMAT_NOT_SUPPORTED);
29784 }
29785
29786 /*
29787 Buffer.
29788
29789 The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if
29790 it should be done before or after format/channels/rate.
29791
29792 OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual
29793 value.
29794 */
29795 {
29796 ma_uint32 periodSizeInFrames;
29797 ma_uint32 periodSizeInBytes;
29798 ma_uint32 ossFragmentSizePower;
29799
29800 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile);
29801
29802 periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels));
29803 if (periodSizeInBytes < 16) {
29804 periodSizeInBytes = 16;
29805 }
29806
29807 ossFragmentSizePower = 4;
29808 periodSizeInBytes >>= 4;
29809 while (periodSizeInBytes >>= 1) {
29810 ossFragmentSizePower += 1;
29811 }
29812
29813 ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower);
29814 ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment);
29815 if (ossResult == -1) {
29816 close(fd);
29817 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count.", MA_FORMAT_NOT_SUPPORTED);
29818 }
29819 }
29820
29821 /* Internal settings. */
29822 if (deviceType == ma_device_type_capture) {
29823 pDevice->oss.fdCapture = fd;
29824 } else {
29825 pDevice->oss.fdPlayback = fd;
29826 }
29827
29828 pDescriptor->format = ma_format_from_oss(ossFormat);
29829 pDescriptor->channels = ossChannels;
29830 pDescriptor->sampleRate = ossSampleRate;
29831 ma_get_standard_channel_map(ma_standard_channel_map_sound4, pDescriptor->channels, pDescriptor->channelMap);
29832 pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16);
29833 pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels);
29834
29835 if (pDescriptor->format == ma_format_unknown) {
29836 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
29837 }
29838
29839 return MA_SUCCESS;
29840}
29841
29842static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
29843{
29844 MA_ASSERT(pDevice != NULL);
29845 MA_ASSERT(pConfig != NULL);
29846
29847 MA_ZERO_OBJECT(&pDevice->oss);
29848
29849 if (pConfig->deviceType == ma_device_type_loopback) {
29850 return MA_DEVICE_TYPE_NOT_SUPPORTED;
29851 }
29852
29853 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
29854 ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
29855 if (result != MA_SUCCESS) {
29856 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", result);
29857 }
29858 }
29859
29860 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
29861 ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
29862 if (result != MA_SUCCESS) {
29863 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", result);
29864 }
29865 }
29866
29867 return MA_SUCCESS;
29868}
29869
29870/*
29871Note on Starting and Stopping
29872=============================
29873In the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when
29874trying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will
29875fail. Instead what we need to do is just not write or read to and from the device when the
29876device is not running.
29877
29878As a result, both the start and stop functions for OSS are just empty stubs. The starting and
29879stopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check
29880the device state, and if the device is stopped they will simply not do any kind of processing.
29881
29882The downside to this technique is that I've noticed a fairly lengthy delay in stopping the
29883device, up to a second. This is on a virtual machine, and as such might just be due to the
29884virtual drivers, but I'm not fully sure. I am not sure how to work around this problem so for
29885the moment that's just how it's going to have to be.
29886
29887When starting the device, OSS will automatically start it when write() or read() is called.
29888*/
29889static ma_result ma_device_start__oss(ma_device* pDevice)
29890{
29891 MA_ASSERT(pDevice != NULL);
29892
29893 /* The device is automatically started with reading and writing. */
29894 (void)pDevice;
29895
29896 return MA_SUCCESS;
29897}
29898
29899static ma_result ma_device_stop__oss(ma_device* pDevice)
29900{
29901 MA_ASSERT(pDevice != NULL);
29902
29903 /* See note above on why this is empty. */
29904 (void)pDevice;
29905
29906 return MA_SUCCESS;
29907}
29908
29909static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
29910{
29911 int resultOSS;
29912 ma_uint32 deviceState;
29913
29914 if (pFramesWritten != NULL) {
29915 *pFramesWritten = 0;
29916 }
29917
29918 /* Don't do any processing if the device is stopped. */
29919 deviceState = ma_device_get_state(pDevice);
29920 if (deviceState != MA_STATE_STARTED && deviceState != MA_STATE_STARTING) {
29921 return MA_SUCCESS;
29922 }
29923
29924 resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
29925 if (resultOSS < 0) {
29926 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device.", ma_result_from_errno(errno));
29927 }
29928
29929 if (pFramesWritten != NULL) {
29930 *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
29931 }
29932
29933 return MA_SUCCESS;
29934}
29935
29936static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
29937{
29938 int resultOSS;
29939 ma_uint32 deviceState;
29940
29941 if (pFramesRead != NULL) {
29942 *pFramesRead = 0;
29943 }
29944
29945 /* Don't do any processing if the device is stopped. */
29946 deviceState = ma_device_get_state(pDevice);
29947 if (deviceState != MA_STATE_STARTED && deviceState != MA_STATE_STARTING) {
29948 return MA_SUCCESS;
29949 }
29950
29951 resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
29952 if (resultOSS < 0) {
29953 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client.", ma_result_from_errno(errno));
29954 }
29955
29956 if (pFramesRead != NULL) {
29957 *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
29958 }
29959
29960 return MA_SUCCESS;
29961}
29962
29963static ma_result ma_context_uninit__oss(ma_context* pContext)
29964{
29965 MA_ASSERT(pContext != NULL);
29966 MA_ASSERT(pContext->backend == ma_backend_oss);
29967
29968 (void)pContext;
29969 return MA_SUCCESS;
29970}
29971
29972static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
29973{
29974 int fd;
29975 int ossVersion;
29976 int result;
29977
29978 MA_ASSERT(pContext != NULL);
29979
29980 (void)pConfig;
29981
29982 /* Try opening a temporary device first so we can get version information. This is closed at the end. */
29983 fd = ma_open_temp_device__oss();
29984 if (fd == -1) {
29985 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties.", MA_NO_BACKEND); /* Looks liks OSS isn't installed, or there are no available devices. */
29986 }
29987
29988 /* Grab the OSS version. */
29989 ossVersion = 0;
29990 result = ioctl(fd, OSS_GETVERSION, &ossVersion);
29991 if (result == -1) {
29992 close(fd);
29993 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version.", MA_NO_BACKEND);
29994 }
29995
29996 /* The file handle to temp device is no longer needed. Close ASAP. */
29997 close(fd);
29998
29999 pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16);
30000 pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8);
30001
30002 pCallbacks->onContextInit = ma_context_init__oss;
30003 pCallbacks->onContextUninit = ma_context_uninit__oss;
30004 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss;
30005 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss;
30006 pCallbacks->onDeviceInit = ma_device_init__oss;
30007 pCallbacks->onDeviceUninit = ma_device_uninit__oss;
30008 pCallbacks->onDeviceStart = ma_device_start__oss;
30009 pCallbacks->onDeviceStop = ma_device_stop__oss;
30010 pCallbacks->onDeviceRead = ma_device_read__oss;
30011 pCallbacks->onDeviceWrite = ma_device_write__oss;
30012 pCallbacks->onDeviceDataLoop = NULL;
30013
30014 return MA_SUCCESS;
30015}
30016#endif /* OSS */
30017
30018
30019/******************************************************************************
30020
30021AAudio Backend
30022
30023******************************************************************************/
30024#ifdef MA_HAS_AAUDIO
30025
30026/*#include <AAudio/AAudio.h>*/
30027
30028typedef int32_t ma_aaudio_result_t;
30029typedef int32_t ma_aaudio_direction_t;
30030typedef int32_t ma_aaudio_sharing_mode_t;
30031typedef int32_t ma_aaudio_format_t;
30032typedef int32_t ma_aaudio_stream_state_t;
30033typedef int32_t ma_aaudio_performance_mode_t;
30034typedef int32_t ma_aaudio_usage_t;
30035typedef int32_t ma_aaudio_content_type_t;
30036typedef int32_t ma_aaudio_input_preset_t;
30037typedef int32_t ma_aaudio_data_callback_result_t;
30038typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder;
30039typedef struct ma_AAudioStream_t* ma_AAudioStream;
30040
30041#define MA_AAUDIO_UNSPECIFIED 0
30042
30043/* Result codes. miniaudio only cares about the success code. */
30044#define MA_AAUDIO_OK 0
30045
30046/* Directions. */
30047#define MA_AAUDIO_DIRECTION_OUTPUT 0
30048#define MA_AAUDIO_DIRECTION_INPUT 1
30049
30050/* Sharing modes. */
30051#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0
30052#define MA_AAUDIO_SHARING_MODE_SHARED 1
30053
30054/* Formats. */
30055#define MA_AAUDIO_FORMAT_PCM_I16 1
30056#define MA_AAUDIO_FORMAT_PCM_FLOAT 2
30057
30058/* Stream states. */
30059#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0
30060#define MA_AAUDIO_STREAM_STATE_UNKNOWN 1
30061#define MA_AAUDIO_STREAM_STATE_OPEN 2
30062#define MA_AAUDIO_STREAM_STATE_STARTING 3
30063#define MA_AAUDIO_STREAM_STATE_STARTED 4
30064#define MA_AAUDIO_STREAM_STATE_PAUSING 5
30065#define MA_AAUDIO_STREAM_STATE_PAUSED 6
30066#define MA_AAUDIO_STREAM_STATE_FLUSHING 7
30067#define MA_AAUDIO_STREAM_STATE_FLUSHED 8
30068#define MA_AAUDIO_STREAM_STATE_STOPPING 9
30069#define MA_AAUDIO_STREAM_STATE_STOPPED 10
30070#define MA_AAUDIO_STREAM_STATE_CLOSING 11
30071#define MA_AAUDIO_STREAM_STATE_CLOSED 12
30072#define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13
30073
30074/* Performance modes. */
30075#define MA_AAUDIO_PERFORMANCE_MODE_NONE 10
30076#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11
30077#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12
30078
30079/* Usage types. */
30080#define MA_AAUDIO_USAGE_MEDIA 1
30081#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2
30082#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3
30083#define MA_AAUDIO_USAGE_ALARM 4
30084#define MA_AAUDIO_USAGE_NOTIFICATION 5
30085#define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6
30086#define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10
30087#define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11
30088#define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12
30089#define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13
30090#define MA_AAUDIO_USAGE_GAME 14
30091#define MA_AAUDIO_USAGE_ASSISTANT 16
30092#define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000
30093#define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001
30094#define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002
30095#define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003
30096
30097/* Content types. */
30098#define MA_AAUDIO_CONTENT_TYPE_SPEECH 1
30099#define MA_AAUDIO_CONTENT_TYPE_MUSIC 2
30100#define MA_AAUDIO_CONTENT_TYPE_MOVIE 3
30101#define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4
30102
30103/* Input presets. */
30104#define MA_AAUDIO_INPUT_PRESET_GENERIC 1
30105#define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5
30106#define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6
30107#define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7
30108#define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9
30109#define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10
30110
30111/* Callback results. */
30112#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0
30113#define MA_AAUDIO_CALLBACK_RESULT_STOP 1
30114
30115
30116typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames);
30117typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error);
30118
30119typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder);
30120typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder);
30121typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId);
30122typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction);
30123typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode);
30124typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format);
30125typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount);
30126typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate);
30127typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
30128typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
30129typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData);
30130typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData);
30131typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode);
30132typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType);
30133typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType);
30134typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset);
30135typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream);
30136typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream);
30137typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream);
30138typedef 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);
30139typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream);
30140typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream);
30141typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream);
30142typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream);
30143typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream);
30144typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream);
30145typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream);
30146typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream);
30147
30148static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA)
30149{
30150 switch (resultAA)
30151 {
30152 case MA_AAUDIO_OK: return MA_SUCCESS;
30153 default: break;
30154 }
30155
30156 return MA_ERROR;
30157}
30158
30159static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage)
30160{
30161 switch (usage) {
30162 case ma_aaudio_usage_announcement: return MA_AAUDIO_USAGE_MEDIA;
30163 case ma_aaudio_usage_emergency: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION;
30164 case ma_aaudio_usage_safety: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
30165 case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_USAGE_ALARM;
30166 case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_NOTIFICATION;
30167 case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE;
30168 case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT;
30169 case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
30170 case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
30171 case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION;
30172 case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_GAME;
30173 case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_ASSISTANT;
30174 case ma_aaudio_usage_notification_event: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY;
30175 case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_SYSTEM_USAGE_SAFETY;
30176 case ma_aaudio_usage_voice_communication: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS;
30177 case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT;
30178 default: break;
30179 }
30180
30181 return MA_AAUDIO_USAGE_MEDIA;
30182}
30183
30184static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType)
30185{
30186 switch (contentType) {
30187 case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE;
30188 case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC;
30189 case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION;
30190 case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH;
30191 default: break;
30192 }
30193
30194 return MA_AAUDIO_CONTENT_TYPE_SPEECH;
30195}
30196
30197static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset)
30198{
30199 switch (inputPreset) {
30200 case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC;
30201 case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER;
30202 case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED;
30203 case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION;
30204 case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION;
30205 case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE;
30206 default: break;
30207 }
30208
30209 return MA_AAUDIO_INPUT_PRESET_GENERIC;
30210}
30211
30212static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error)
30213{
30214 ma_device* pDevice = (ma_device*)pUserData;
30215 MA_ASSERT(pDevice != NULL);
30216
30217 (void)error;
30218
30219 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream));
30220
30221 /*
30222 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
30223 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.
30224 */
30225 if (((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream) == MA_AAUDIO_STREAM_STATE_DISCONNECTED) {
30226 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[AAudio] Device Disconnected.\n");
30227 }
30228}
30229
30230static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
30231{
30232 ma_device* pDevice = (ma_device*)pUserData;
30233 MA_ASSERT(pDevice != NULL);
30234
30235 ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, frameCount);
30236
30237 (void)pStream;
30238 return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
30239}
30240
30241static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
30242{
30243 ma_device* pDevice = (ma_device*)pUserData;
30244 MA_ASSERT(pDevice != NULL);
30245
30246 ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, frameCount);
30247
30248 (void)pStream;
30249 return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
30250}
30251
30252static 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)
30253{
30254 ma_AAudioStreamBuilder* pBuilder;
30255 ma_aaudio_result_t resultAA;
30256 ma_uint32 bufferCapacityInFrames;
30257
30258 /* Safety. */
30259 *ppBuilder = NULL;
30260
30261 resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder);
30262 if (resultAA != MA_AAUDIO_OK) {
30263 return ma_result_from_aaudio(resultAA);
30264 }
30265
30266 if (pDeviceID != NULL) {
30267 ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio);
30268 }
30269
30270 ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT);
30271 ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE);
30272
30273
30274 /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */
30275 if (pDescriptor != NULL) {
30276 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. */
30277
30278 if (pDescriptor->sampleRate != 0) {
30279 ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate);
30280 }
30281
30282 if (deviceType == ma_device_type_capture) {
30283 if (pDescriptor->channels != 0) {
30284 ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
30285 }
30286 if (pDescriptor->format != ma_format_unknown) {
30287 ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
30288 }
30289 } else {
30290 if (pDescriptor->channels != 0) {
30291 ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
30292 }
30293 if (pDescriptor->format != ma_format_unknown) {
30294 ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
30295 }
30296 }
30297
30298 /*
30299 AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you
30300 retrieve the actual sample rate until after you've opened the stream. But you need to configure
30301 the buffer capacity before you open the stream... :/
30302
30303 To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on.
30304 */
30305 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount;
30306
30307 ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames);
30308 ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount);
30309
30310 if (deviceType == ma_device_type_capture) {
30311 if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) {
30312 ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset));
30313 }
30314
30315 ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice);
30316 } else {
30317 if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) {
30318 ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage));
30319 }
30320
30321 if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) {
30322 ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType));
30323 }
30324
30325 ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice);
30326 }
30327
30328 /* 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. */
30329 ((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);
30330
30331 /* We need to set an error callback to detect device changes. */
30332 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. */
30333 ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice);
30334 }
30335 }
30336
30337 *ppBuilder = pBuilder;
30338
30339 return MA_SUCCESS;
30340}
30341
30342static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream)
30343{
30344 ma_result result;
30345
30346 result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream));
30347 ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
30348
30349 return result;
30350}
30351
30352static 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)
30353{
30354 ma_result result;
30355 ma_AAudioStreamBuilder* pBuilder;
30356
30357 *ppStream = NULL;
30358
30359 result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder);
30360 if (result != MA_SUCCESS) {
30361 return result;
30362 }
30363
30364 return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream);
30365}
30366
30367static 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)
30368{
30369 ma_result result;
30370 ma_AAudioStreamBuilder* pBuilder;
30371
30372 MA_ASSERT(pConfig != NULL);
30373 MA_ASSERT(pConfig->deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */
30374
30375 *ppStream = NULL;
30376
30377 result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder);
30378 if (result != MA_SUCCESS) {
30379 return result;
30380 }
30381
30382 return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream);
30383}
30384
30385static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream)
30386{
30387 return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream));
30388}
30389
30390static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType)
30391{
30392 /* The only way to know this is to try creating a stream. */
30393 ma_AAudioStream* pStream;
30394 ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream);
30395 if (result != MA_SUCCESS) {
30396 return MA_FALSE;
30397 }
30398
30399 ma_close_stream__aaudio(pContext, pStream);
30400 return MA_TRUE;
30401}
30402
30403static 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)
30404{
30405 ma_aaudio_stream_state_t actualNewState;
30406 ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */
30407 if (resultAA != MA_AAUDIO_OK) {
30408 return ma_result_from_aaudio(resultAA);
30409 }
30410
30411 if (newState != actualNewState) {
30412 return MA_ERROR; /* Failed to transition into the expected state. */
30413 }
30414
30415 return MA_SUCCESS;
30416}
30417
30418
30419static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
30420{
30421 ma_bool32 cbResult = MA_TRUE;
30422
30423 MA_ASSERT(pContext != NULL);
30424 MA_ASSERT(callback != NULL);
30425
30426 /* 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. */
30427
30428 /* Playback. */
30429 if (cbResult) {
30430 ma_device_info deviceInfo;
30431 MA_ZERO_OBJECT(&deviceInfo);
30432 deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
30433 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
30434
30435 if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) {
30436 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
30437 }
30438 }
30439
30440 /* Capture. */
30441 if (cbResult) {
30442 ma_device_info deviceInfo;
30443 MA_ZERO_OBJECT(&deviceInfo);
30444 deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
30445 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
30446
30447 if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) {
30448 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
30449 }
30450 }
30451
30452 return MA_SUCCESS;
30453}
30454
30455static 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)
30456{
30457 MA_ASSERT(pContext != NULL);
30458 MA_ASSERT(pStream != NULL);
30459 MA_ASSERT(pDeviceInfo != NULL);
30460
30461 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
30462 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream);
30463 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream);
30464 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
30465 pDeviceInfo->nativeDataFormatCount += 1;
30466}
30467
30468static void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo)
30469{
30470 /* AAudio supports s16 and f32. */
30471 ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo);
30472 ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo);
30473}
30474
30475static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
30476{
30477 ma_AAudioStream* pStream;
30478 ma_result result;
30479
30480 MA_ASSERT(pContext != NULL);
30481
30482 /* ID */
30483 if (pDeviceID != NULL) {
30484 pDeviceInfo->id.aaudio = pDeviceID->aaudio;
30485 } else {
30486 pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED;
30487 }
30488
30489 /* Name */
30490 if (deviceType == ma_device_type_playback) {
30491 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
30492 } else {
30493 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
30494 }
30495
30496
30497 pDeviceInfo->nativeDataFormatCount = 0;
30498
30499 /* We'll need to open the device to get accurate sample rate and channel count information. */
30500 result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream);
30501 if (result != MA_SUCCESS) {
30502 return result;
30503 }
30504
30505 ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo);
30506
30507 ma_close_stream__aaudio(pContext, pStream);
30508 pStream = NULL;
30509
30510 return MA_SUCCESS;
30511}
30512
30513
30514static ma_result ma_device_uninit__aaudio(ma_device* pDevice)
30515{
30516 MA_ASSERT(pDevice != NULL);
30517
30518 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
30519 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
30520 pDevice->aaudio.pStreamCapture = NULL;
30521 }
30522
30523 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
30524 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
30525 pDevice->aaudio.pStreamPlayback = NULL;
30526 }
30527
30528 return MA_SUCCESS;
30529}
30530
30531static 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)
30532{
30533 ma_result result;
30534 int32_t bufferCapacityInFrames;
30535 int32_t framesPerDataCallback;
30536 ma_AAudioStream* pStream;
30537
30538 MA_ASSERT(pDevice != NULL);
30539 MA_ASSERT(pConfig != NULL);
30540 MA_ASSERT(pDescriptor != NULL);
30541
30542 *ppStream = NULL; /* Safety. */
30543
30544 /* First step is to open the stream. From there we'll be able to extract the internal configuration. */
30545 result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream);
30546 if (result != MA_SUCCESS) {
30547 return result; /* Failed to open the AAudio stream. */
30548 }
30549
30550 /* Now extract the internal configuration. */
30551 pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32;
30552 pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream);
30553 pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream);
30554
30555 /* For the channel map we need to be sure we don't overflow any buffers. */
30556 if (pDescriptor->channels <= MA_MAX_CHANNELS) {
30557 ma_get_standard_channel_map(ma_standard_channel_map_default, pDescriptor->channels, pDescriptor->channelMap); /* <-- Cannot find info on channel order, so assuming a default. */
30558 } else {
30559 ma_channel_map_init_blank(MA_MAX_CHANNELS, pDescriptor->channelMap); /* Too many channels. Use a blank channel map. */
30560 }
30561
30562 bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream);
30563 framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream);
30564
30565 if (framesPerDataCallback > 0) {
30566 pDescriptor->periodSizeInFrames = framesPerDataCallback;
30567 pDescriptor->periodCount = bufferCapacityInFrames / framesPerDataCallback;
30568 } else {
30569 pDescriptor->periodSizeInFrames = bufferCapacityInFrames;
30570 pDescriptor->periodCount = 1;
30571 }
30572
30573 *ppStream = pStream;
30574
30575 return MA_SUCCESS;
30576}
30577
30578static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
30579{
30580 ma_result result;
30581
30582 MA_ASSERT(pDevice != NULL);
30583
30584 if (pConfig->deviceType == ma_device_type_loopback) {
30585 return MA_DEVICE_TYPE_NOT_SUPPORTED;
30586 }
30587
30588 /* No exclusive mode with AAudio. */
30589 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
30590 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
30591 return MA_SHARE_MODE_NOT_SUPPORTED;
30592 }
30593
30594 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
30595 result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture);
30596 if (result != MA_SUCCESS) {
30597 return result;
30598 }
30599 }
30600
30601 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
30602 result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback);
30603 if (result != MA_SUCCESS) {
30604 return result;
30605 }
30606 }
30607
30608 return MA_SUCCESS;
30609}
30610
30611static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
30612{
30613 ma_aaudio_result_t resultAA;
30614 ma_aaudio_stream_state_t currentState;
30615
30616 MA_ASSERT(pDevice != NULL);
30617
30618 resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream);
30619 if (resultAA != MA_AAUDIO_OK) {
30620 return ma_result_from_aaudio(resultAA);
30621 }
30622
30623 /* Do we actually need to wait for the device to transition into it's started state? */
30624
30625 /* 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. */
30626 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
30627 if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) {
30628 ma_result result;
30629
30630 if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) {
30631 return MA_ERROR; /* Expecting the stream to be a starting or started state. */
30632 }
30633
30634 result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED);
30635 if (result != MA_SUCCESS) {
30636 return result;
30637 }
30638 }
30639
30640 return MA_SUCCESS;
30641}
30642
30643static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
30644{
30645 ma_aaudio_result_t resultAA;
30646 ma_aaudio_stream_state_t currentState;
30647
30648 MA_ASSERT(pDevice != NULL);
30649
30650 /*
30651 From the AAudio documentation:
30652
30653 The stream will stop after all of the data currently buffered has been played.
30654
30655 This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic.
30656 */
30657
30658 resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream);
30659 if (resultAA != MA_AAUDIO_OK) {
30660 return ma_result_from_aaudio(resultAA);
30661 }
30662
30663 /* 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. */
30664 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
30665 if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) {
30666 ma_result result;
30667
30668 if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) {
30669 return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */
30670 }
30671
30672 result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED);
30673 if (result != MA_SUCCESS) {
30674 return result;
30675 }
30676 }
30677
30678 return MA_SUCCESS;
30679}
30680
30681static ma_result ma_device_start__aaudio(ma_device* pDevice)
30682{
30683 MA_ASSERT(pDevice != NULL);
30684
30685 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
30686 ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
30687 if (result != MA_SUCCESS) {
30688 return result;
30689 }
30690 }
30691
30692 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
30693 ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
30694 if (result != MA_SUCCESS) {
30695 if (pDevice->type == ma_device_type_duplex) {
30696 ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
30697 }
30698 return result;
30699 }
30700 }
30701
30702 return MA_SUCCESS;
30703}
30704
30705static ma_result ma_device_stop__aaudio(ma_device* pDevice)
30706{
30707 ma_stop_proc onStop;
30708
30709 MA_ASSERT(pDevice != NULL);
30710
30711 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
30712 ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
30713 if (result != MA_SUCCESS) {
30714 return result;
30715 }
30716 }
30717
30718 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
30719 ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
30720 if (result != MA_SUCCESS) {
30721 return result;
30722 }
30723 }
30724
30725 onStop = pDevice->onStop;
30726 if (onStop) {
30727 onStop(pDevice);
30728 }
30729
30730 return MA_SUCCESS;
30731}
30732
30733
30734static ma_result ma_context_uninit__aaudio(ma_context* pContext)
30735{
30736 MA_ASSERT(pContext != NULL);
30737 MA_ASSERT(pContext->backend == ma_backend_aaudio);
30738
30739 ma_dlclose(pContext, pContext->aaudio.hAAudio);
30740 pContext->aaudio.hAAudio = NULL;
30741
30742 return MA_SUCCESS;
30743}
30744
30745static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
30746{
30747 size_t i;
30748 const char* libNames[] = {
30749 "libaaudio.so"
30750 };
30751
30752 for (i = 0; i < ma_countof(libNames); ++i) {
30753 pContext->aaudio.hAAudio = ma_dlopen(pContext, libNames[i]);
30754 if (pContext->aaudio.hAAudio != NULL) {
30755 break;
30756 }
30757 }
30758
30759 if (pContext->aaudio.hAAudio == NULL) {
30760 return MA_FAILED_TO_INIT_BACKEND;
30761 }
30762
30763 pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudio_createStreamBuilder");
30764 pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete");
30765 pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId");
30766 pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection");
30767 pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode");
30768 pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat");
30769 pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount");
30770 pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate");
30771 pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames");
30772 pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback");
30773 pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback");
30774 pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback");
30775 pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode");
30776 pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage");
30777 pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType");
30778 pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset");
30779 pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream");
30780 pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close");
30781 pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState");
30782 pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange");
30783 pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFormat");
30784 pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getChannelCount");
30785 pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getSampleRate");
30786 pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames");
30787 pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback");
30788 pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst");
30789 pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStart");
30790 pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStop");
30791
30792
30793 pCallbacks->onContextInit = ma_context_init__aaudio;
30794 pCallbacks->onContextUninit = ma_context_uninit__aaudio;
30795 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio;
30796 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio;
30797 pCallbacks->onDeviceInit = ma_device_init__aaudio;
30798 pCallbacks->onDeviceUninit = ma_device_uninit__aaudio;
30799 pCallbacks->onDeviceStart = ma_device_start__aaudio;
30800 pCallbacks->onDeviceStop = ma_device_stop__aaudio;
30801 pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */
30802 pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */
30803 pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */
30804
30805 (void)pConfig;
30806 return MA_SUCCESS;
30807}
30808#endif /* AAudio */
30809
30810
30811/******************************************************************************
30812
30813OpenSL|ES Backend
30814
30815******************************************************************************/
30816#ifdef MA_HAS_OPENSL
30817#include <SLES/OpenSLES.h>
30818#ifdef MA_ANDROID
30819#include <SLES/OpenSLES_Android.h>
30820#endif
30821
30822typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired);
30823
30824/* OpenSL|ES has one-per-application objects :( */
30825static SLObjectItf g_maEngineObjectSL = NULL;
30826static SLEngineItf g_maEngineSL = NULL;
30827static ma_uint32 g_maOpenSLInitCounter = 0;
30828static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */
30829
30830#define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p)))
30831#define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p)))
30832#define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p)))
30833#define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p)))
30834
30835#ifdef MA_ANDROID
30836#define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p)))
30837#else
30838#define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p)))
30839#endif
30840
30841static ma_result ma_result_from_OpenSL(SLuint32 result)
30842{
30843 switch (result)
30844 {
30845 case SL_RESULT_SUCCESS: return MA_SUCCESS;
30846 case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR;
30847 case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS;
30848 case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY;
30849 case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA;
30850 case SL_RESULT_RESOURCE_LOST: return MA_ERROR;
30851 case SL_RESULT_IO_ERROR: return MA_IO_ERROR;
30852 case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE;
30853 case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA;
30854 case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED;
30855 case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR;
30856 case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED;
30857 case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED;
30858 case SL_RESULT_INTERNAL_ERROR: return MA_ERROR;
30859 case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR;
30860 case SL_RESULT_OPERATION_ABORTED: return MA_ERROR;
30861 case SL_RESULT_CONTROL_LOST: return MA_ERROR;
30862 default: return MA_ERROR;
30863 }
30864}
30865
30866/* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
30867static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id)
30868{
30869 switch (id)
30870 {
30871 case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
30872 case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
30873 case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
30874 case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
30875 case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
30876 case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
30877 case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
30878 case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
30879 case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
30880 case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
30881 case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
30882 case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
30883 case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
30884 case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
30885 case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
30886 case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
30887 case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
30888 case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
30889 default: return 0;
30890 }
30891}
30892
30893/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */
30894static SLuint32 ma_channel_id_to_opensl(ma_uint8 id)
30895{
30896 switch (id)
30897 {
30898 case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER;
30899 case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT;
30900 case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT;
30901 case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER;
30902 case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY;
30903 case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT;
30904 case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT;
30905 case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER;
30906 case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER;
30907 case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER;
30908 case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT;
30909 case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT;
30910 case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER;
30911 case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT;
30912 case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER;
30913 case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT;
30914 case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT;
30915 case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER;
30916 case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT;
30917 default: return 0;
30918 }
30919}
30920
30921/* Converts a channel mapping to an OpenSL-style channel mask. */
30922static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels)
30923{
30924 SLuint32 channelMask = 0;
30925 ma_uint32 iChannel;
30926 for (iChannel = 0; iChannel < channels; ++iChannel) {
30927 channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]);
30928 }
30929
30930 return channelMask;
30931}
30932
30933/* Converts an OpenSL-style channel mask to a miniaudio channel map. */
30934static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap)
30935{
30936 if (channels == 1 && channelMask == 0) {
30937 pChannelMap[0] = MA_CHANNEL_MONO;
30938 } else if (channels == 2 && channelMask == 0) {
30939 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
30940 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
30941 } else {
30942 if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) {
30943 pChannelMap[0] = MA_CHANNEL_MONO;
30944 } else {
30945 /* Just iterate over each bit. */
30946 ma_uint32 iChannel = 0;
30947 ma_uint32 iBit;
30948 for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {
30949 SLuint32 bitValue = (channelMask & (1UL << iBit));
30950 if (bitValue != 0) {
30951 /* The bit is set. */
30952 pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue);
30953 iChannel += 1;
30954 }
30955 }
30956 }
30957 }
30958}
30959
30960static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec)
30961{
30962 if (samplesPerSec <= SL_SAMPLINGRATE_8) {
30963 return SL_SAMPLINGRATE_8;
30964 }
30965 if (samplesPerSec <= SL_SAMPLINGRATE_11_025) {
30966 return SL_SAMPLINGRATE_11_025;
30967 }
30968 if (samplesPerSec <= SL_SAMPLINGRATE_12) {
30969 return SL_SAMPLINGRATE_12;
30970 }
30971 if (samplesPerSec <= SL_SAMPLINGRATE_16) {
30972 return SL_SAMPLINGRATE_16;
30973 }
30974 if (samplesPerSec <= SL_SAMPLINGRATE_22_05) {
30975 return SL_SAMPLINGRATE_22_05;
30976 }
30977 if (samplesPerSec <= SL_SAMPLINGRATE_24) {
30978 return SL_SAMPLINGRATE_24;
30979 }
30980 if (samplesPerSec <= SL_SAMPLINGRATE_32) {
30981 return SL_SAMPLINGRATE_32;
30982 }
30983 if (samplesPerSec <= SL_SAMPLINGRATE_44_1) {
30984 return SL_SAMPLINGRATE_44_1;
30985 }
30986 if (samplesPerSec <= SL_SAMPLINGRATE_48) {
30987 return SL_SAMPLINGRATE_48;
30988 }
30989
30990 /* Android doesn't support more than 48000. */
30991#ifndef MA_ANDROID
30992 if (samplesPerSec <= SL_SAMPLINGRATE_64) {
30993 return SL_SAMPLINGRATE_64;
30994 }
30995 if (samplesPerSec <= SL_SAMPLINGRATE_88_2) {
30996 return SL_SAMPLINGRATE_88_2;
30997 }
30998 if (samplesPerSec <= SL_SAMPLINGRATE_96) {
30999 return SL_SAMPLINGRATE_96;
31000 }
31001 if (samplesPerSec <= SL_SAMPLINGRATE_192) {
31002 return SL_SAMPLINGRATE_192;
31003 }
31004#endif
31005
31006 return SL_SAMPLINGRATE_16;
31007}
31008
31009
31010static SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType)
31011{
31012 switch (streamType) {
31013 case ma_opensl_stream_type_voice: return SL_ANDROID_STREAM_VOICE;
31014 case ma_opensl_stream_type_system: return SL_ANDROID_STREAM_SYSTEM;
31015 case ma_opensl_stream_type_ring: return SL_ANDROID_STREAM_RING;
31016 case ma_opensl_stream_type_media: return SL_ANDROID_STREAM_MEDIA;
31017 case ma_opensl_stream_type_alarm: return SL_ANDROID_STREAM_ALARM;
31018 case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION;
31019 default: break;
31020 }
31021
31022 return SL_ANDROID_STREAM_VOICE;
31023}
31024
31025static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset)
31026{
31027 switch (recordingPreset) {
31028 case ma_opensl_recording_preset_generic: return SL_ANDROID_RECORDING_PRESET_GENERIC;
31029 case ma_opensl_recording_preset_camcorder: return SL_ANDROID_RECORDING_PRESET_CAMCORDER;
31030 case ma_opensl_recording_preset_voice_recognition: return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
31031 case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION;
31032 case ma_opensl_recording_preset_voice_unprocessed: return SL_ANDROID_RECORDING_PRESET_UNPROCESSED;
31033 default: break;
31034 }
31035
31036 return SL_ANDROID_RECORDING_PRESET_NONE;
31037}
31038
31039
31040static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
31041{
31042 ma_bool32 cbResult;
31043
31044 MA_ASSERT(pContext != NULL);
31045 MA_ASSERT(callback != NULL);
31046
31047 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. */
31048 if (g_maOpenSLInitCounter == 0) {
31049 return MA_INVALID_OPERATION;
31050 }
31051
31052 /*
31053 TODO: Test Me.
31054
31055 This is currently untested, so for now we are just returning default devices.
31056 */
31057#if 0 && !defined(MA_ANDROID)
31058 ma_bool32 isTerminated = MA_FALSE;
31059
31060 SLuint32 pDeviceIDs[128];
31061 SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]);
31062
31063 SLAudioIODeviceCapabilitiesItf deviceCaps;
31064 SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
31065 if (resultSL != SL_RESULT_SUCCESS) {
31066 /* The interface may not be supported so just report a default device. */
31067 goto return_default_device;
31068 }
31069
31070 /* Playback */
31071 if (!isTerminated) {
31072 resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs);
31073 if (resultSL != SL_RESULT_SUCCESS) {
31074 return ma_result_from_OpenSL(resultSL);
31075 }
31076
31077 for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
31078 ma_device_info deviceInfo;
31079 MA_ZERO_OBJECT(&deviceInfo);
31080 deviceInfo.id.opensl = pDeviceIDs[iDevice];
31081
31082 SLAudioOutputDescriptor desc;
31083 resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
31084 if (resultSL == SL_RESULT_SUCCESS) {
31085 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1);
31086
31087 ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
31088 if (cbResult == MA_FALSE) {
31089 isTerminated = MA_TRUE;
31090 break;
31091 }
31092 }
31093 }
31094 }
31095
31096 /* Capture */
31097 if (!isTerminated) {
31098 resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs);
31099 if (resultSL != SL_RESULT_SUCCESS) {
31100 return ma_result_from_OpenSL(resultSL);
31101 }
31102
31103 for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
31104 ma_device_info deviceInfo;
31105 MA_ZERO_OBJECT(&deviceInfo);
31106 deviceInfo.id.opensl = pDeviceIDs[iDevice];
31107
31108 SLAudioInputDescriptor desc;
31109 resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
31110 if (resultSL == SL_RESULT_SUCCESS) {
31111 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1);
31112
31113 ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
31114 if (cbResult == MA_FALSE) {
31115 isTerminated = MA_TRUE;
31116 break;
31117 }
31118 }
31119 }
31120 }
31121
31122 return MA_SUCCESS;
31123#else
31124 goto return_default_device;
31125#endif
31126
31127return_default_device:;
31128 cbResult = MA_TRUE;
31129
31130 /* Playback. */
31131 if (cbResult) {
31132 ma_device_info deviceInfo;
31133 MA_ZERO_OBJECT(&deviceInfo);
31134 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
31135 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
31136 }
31137
31138 /* Capture. */
31139 if (cbResult) {
31140 ma_device_info deviceInfo;
31141 MA_ZERO_OBJECT(&deviceInfo);
31142 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
31143 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
31144 }
31145
31146 return MA_SUCCESS;
31147}
31148
31149static void ma_context_add_data_format_ex__opensl(ma_context* pContext, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_device_info* pDeviceInfo)
31150{
31151 MA_ASSERT(pContext != NULL);
31152 MA_ASSERT(pDeviceInfo != NULL);
31153
31154 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
31155 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
31156 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
31157 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
31158 pDeviceInfo->nativeDataFormatCount += 1;
31159}
31160
31161static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo)
31162{
31163 ma_uint32 minChannels = 1;
31164 ma_uint32 maxChannels = 2;
31165 ma_uint32 minSampleRate = (ma_uint32)ma_standard_sample_rate_8000;
31166 ma_uint32 maxSampleRate = (ma_uint32)ma_standard_sample_rate_48000;
31167 ma_uint32 iChannel;
31168 ma_uint32 iSampleRate;
31169
31170 MA_ASSERT(pContext != NULL);
31171 MA_ASSERT(pDeviceInfo != NULL);
31172
31173 /*
31174 Each sample format can support mono and stereo, and we'll support a small subset of standard
31175 rates (up to 48000). A better solution would be to somehow find a native sample rate.
31176 */
31177 for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) {
31178 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {
31179 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];
31180 if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {
31181 ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo);
31182 }
31183 }
31184 }
31185}
31186
31187static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
31188{
31189 MA_ASSERT(pContext != NULL);
31190
31191 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. */
31192 if (g_maOpenSLInitCounter == 0) {
31193 return MA_INVALID_OPERATION;
31194 }
31195
31196 /*
31197 TODO: Test Me.
31198
31199 This is currently untested, so for now we are just returning default devices.
31200 */
31201#if 0 && !defined(MA_ANDROID)
31202 SLAudioIODeviceCapabilitiesItf deviceCaps;
31203 SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
31204 if (resultSL != SL_RESULT_SUCCESS) {
31205 /* The interface may not be supported so just report a default device. */
31206 goto return_default_device;
31207 }
31208
31209 if (deviceType == ma_device_type_playback) {
31210 SLAudioOutputDescriptor desc;
31211 resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
31212 if (resultSL != SL_RESULT_SUCCESS) {
31213 return ma_result_from_OpenSL(resultSL);
31214 }
31215
31216 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1);
31217 } else {
31218 SLAudioInputDescriptor desc;
31219 resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
31220 if (resultSL != SL_RESULT_SUCCESS) {
31221 return ma_result_from_OpenSL(resultSL);
31222 }
31223
31224 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1);
31225 }
31226
31227 goto return_detailed_info;
31228#else
31229 goto return_default_device;
31230#endif
31231
31232return_default_device:
31233 if (pDeviceID != NULL) {
31234 if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) ||
31235 (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) {
31236 return MA_NO_DEVICE; /* Don't know the device. */
31237 }
31238 }
31239
31240 /* Name / Description */
31241 if (deviceType == ma_device_type_playback) {
31242 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
31243 } else {
31244 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
31245 }
31246
31247 pDeviceInfo->isDefault = MA_TRUE;
31248
31249 goto return_detailed_info;
31250
31251
31252return_detailed_info:
31253
31254 /*
31255 For now we're just outputting a set of values that are supported by the API but not necessarily supported
31256 by the device natively. Later on we should work on this so that it more closely reflects the device's
31257 actual native format.
31258 */
31259 pDeviceInfo->nativeDataFormatCount = 0;
31260#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
31261 ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo);
31262#endif
31263 ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo);
31264 ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo);
31265
31266 return MA_SUCCESS;
31267}
31268
31269
31270#ifdef MA_ANDROID
31271/*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/
31272static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
31273{
31274 ma_device* pDevice = (ma_device*)pUserData;
31275 size_t periodSizeInBytes;
31276 ma_uint8* pBuffer;
31277 SLresult resultSL;
31278
31279 MA_ASSERT(pDevice != NULL);
31280
31281 (void)pBufferQueue;
31282
31283 /*
31284 For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like
31285 OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this,
31286 but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :(
31287 */
31288
31289 /* Don't do anything if the device is not started. */
31290 if (ma_device_get_state(pDevice) != MA_STATE_STARTED) {
31291 return;
31292 }
31293
31294 /* Don't do anything if the device is being drained. */
31295 if (pDevice->opensl.isDrainingCapture) {
31296 return;
31297 }
31298
31299 periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
31300 pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes);
31301
31302 ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames);
31303
31304 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes);
31305 if (resultSL != SL_RESULT_SUCCESS) {
31306 return;
31307 }
31308
31309 pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods;
31310}
31311
31312static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
31313{
31314 ma_device* pDevice = (ma_device*)pUserData;
31315 size_t periodSizeInBytes;
31316 ma_uint8* pBuffer;
31317 SLresult resultSL;
31318
31319 MA_ASSERT(pDevice != NULL);
31320
31321 (void)pBufferQueue;
31322
31323 /* Don't do anything if the device is not started. */
31324 if (ma_device_get_state(pDevice) != MA_STATE_STARTED) {
31325 return;
31326 }
31327
31328 /* Don't do anything if the device is being drained. */
31329 if (pDevice->opensl.isDrainingPlayback) {
31330 return;
31331 }
31332
31333 periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
31334 pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes);
31335
31336 ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames);
31337
31338 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes);
31339 if (resultSL != SL_RESULT_SUCCESS) {
31340 return;
31341 }
31342
31343 pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods;
31344}
31345#endif
31346
31347static ma_result ma_device_uninit__opensl(ma_device* pDevice)
31348{
31349 MA_ASSERT(pDevice != NULL);
31350
31351 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. */
31352 if (g_maOpenSLInitCounter == 0) {
31353 return MA_INVALID_OPERATION;
31354 }
31355
31356 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31357 if (pDevice->opensl.pAudioRecorderObj) {
31358 MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj);
31359 }
31360
31361 ma__free_from_callbacks(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks);
31362 }
31363
31364 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31365 if (pDevice->opensl.pAudioPlayerObj) {
31366 MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj);
31367 }
31368 if (pDevice->opensl.pOutputMixObj) {
31369 MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj);
31370 }
31371
31372 ma__free_from_callbacks(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks);
31373 }
31374
31375 return MA_SUCCESS;
31376}
31377
31378#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
31379typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM;
31380#else
31381typedef SLDataFormat_PCM ma_SLDataFormat_PCM;
31382#endif
31383
31384static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat)
31385{
31386 /* We need to convert our format/channels/rate so that they aren't set to default. */
31387 if (format == ma_format_unknown) {
31388 format = MA_DEFAULT_FORMAT;
31389 }
31390 if (channels == 0) {
31391 channels = MA_DEFAULT_CHANNELS;
31392 }
31393 if (sampleRate == 0) {
31394 sampleRate = MA_DEFAULT_SAMPLE_RATE;
31395 }
31396
31397#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
31398 if (format == ma_format_f32) {
31399 pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
31400 pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
31401 } else {
31402 pDataFormat->formatType = SL_DATAFORMAT_PCM;
31403 }
31404#else
31405 pDataFormat->formatType = SL_DATAFORMAT_PCM;
31406#endif
31407
31408 pDataFormat->numChannels = channels;
31409 ((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 */
31410 pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format)*8;
31411 pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels);
31412 pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
31413
31414 /*
31415 Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html
31416 - Only mono and stereo is supported.
31417 - Only u8 and s16 formats are supported.
31418 - Maximum sample rate of 48000.
31419 */
31420#ifdef MA_ANDROID
31421 if (pDataFormat->numChannels > 2) {
31422 pDataFormat->numChannels = 2;
31423 }
31424#if __ANDROID_API__ >= 21
31425 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
31426 /* It's floating point. */
31427 MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
31428 if (pDataFormat->bitsPerSample > 32) {
31429 pDataFormat->bitsPerSample = 32;
31430 }
31431 } else {
31432 if (pDataFormat->bitsPerSample > 16) {
31433 pDataFormat->bitsPerSample = 16;
31434 }
31435 }
31436#else
31437 if (pDataFormat->bitsPerSample > 16) {
31438 pDataFormat->bitsPerSample = 16;
31439 }
31440#endif
31441 if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) {
31442 ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48;
31443 }
31444#endif
31445
31446 pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */
31447
31448 return MA_SUCCESS;
31449}
31450
31451static 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)
31452{
31453 ma_bool32 isFloatingPoint = MA_FALSE;
31454#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
31455 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
31456 MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
31457 isFloatingPoint = MA_TRUE;
31458 }
31459#endif
31460 if (isFloatingPoint) {
31461 if (pDataFormat->bitsPerSample == 32) {
31462 *pFormat = ma_format_f32;
31463 }
31464 } else {
31465 if (pDataFormat->bitsPerSample == 8) {
31466 *pFormat = ma_format_u8;
31467 } else if (pDataFormat->bitsPerSample == 16) {
31468 *pFormat = ma_format_s16;
31469 } else if (pDataFormat->bitsPerSample == 24) {
31470 *pFormat = ma_format_s24;
31471 } else if (pDataFormat->bitsPerSample == 32) {
31472 *pFormat = ma_format_s32;
31473 }
31474 }
31475
31476 *pChannels = pDataFormat->numChannels;
31477 *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000;
31478 ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap);
31479
31480 return MA_SUCCESS;
31481}
31482
31483static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
31484{
31485#ifdef MA_ANDROID
31486 SLDataLocator_AndroidSimpleBufferQueue queue;
31487 SLresult resultSL;
31488 size_t bufferSizeInBytes;
31489 SLInterfaceID itfIDs[2];
31490 const SLboolean itfIDsRequired[] = {
31491 SL_BOOLEAN_TRUE, /* SL_IID_ANDROIDSIMPLEBUFFERQUEUE */
31492 SL_BOOLEAN_FALSE /* SL_IID_ANDROIDCONFIGURATION */
31493 };
31494#endif
31495
31496 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. */
31497 if (g_maOpenSLInitCounter == 0) {
31498 return MA_INVALID_OPERATION;
31499 }
31500
31501 if (pConfig->deviceType == ma_device_type_loopback) {
31502 return MA_DEVICE_TYPE_NOT_SUPPORTED;
31503 }
31504
31505 /*
31506 For now, only supporting Android implementations of OpenSL|ES since that's the only one I've
31507 been able to test with and I currently depend on Android-specific extensions (simple buffer
31508 queues).
31509 */
31510#ifdef MA_ANDROID
31511 itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
31512 itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION;
31513
31514 /* No exclusive mode with OpenSL|ES. */
31515 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
31516 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
31517 return MA_SHARE_MODE_NOT_SUPPORTED;
31518 }
31519
31520 /* Now we can start initializing the device properly. */
31521 MA_ASSERT(pDevice != NULL);
31522 MA_ZERO_OBJECT(&pDevice->opensl);
31523
31524 queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
31525
31526 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
31527 ma_SLDataFormat_PCM pcm;
31528 SLDataLocator_IODevice locatorDevice;
31529 SLDataSource source;
31530 SLDataSink sink;
31531 SLAndroidConfigurationItf pRecorderConfig;
31532
31533 ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm);
31534
31535 locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE;
31536 locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT;
31537 locatorDevice.deviceID = (pDescriptorCapture->pDeviceID == NULL) ? SL_DEFAULTDEVICEID_AUDIOINPUT : pDescriptorCapture->pDeviceID->opensl;
31538 locatorDevice.device = NULL;
31539
31540 source.pLocator = &locatorDevice;
31541 source.pFormat = NULL;
31542
31543 queue.numBuffers = pDescriptorCapture->periodCount;
31544
31545 sink.pLocator = &queue;
31546 sink.pFormat = (SLDataFormat_PCM*)&pcm;
31547
31548 resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
31549 if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) {
31550 /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
31551 pcm.formatType = SL_DATAFORMAT_PCM;
31552 pcm.numChannels = 1;
31553 ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */
31554 pcm.bitsPerSample = 16;
31555 pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
31556 pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
31557 resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
31558 }
31559
31560 if (resultSL != SL_RESULT_SUCCESS) {
31561 ma_device_uninit__opensl(pDevice);
31562 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder.", ma_result_from_OpenSL(resultSL));
31563 }
31564
31565
31566 /* Set the recording preset before realizing the player. */
31567 if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) {
31568 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig);
31569 if (resultSL == SL_RESULT_SUCCESS) {
31570 SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset);
31571 resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32));
31572 if (resultSL != SL_RESULT_SUCCESS) {
31573 /* Failed to set the configuration. Just keep going. */
31574 }
31575 }
31576 }
31577
31578 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE);
31579 if (resultSL != SL_RESULT_SUCCESS) {
31580 ma_device_uninit__opensl(pDevice);
31581 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.", ma_result_from_OpenSL(resultSL));
31582 }
31583
31584 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder);
31585 if (resultSL != SL_RESULT_SUCCESS) {
31586 ma_device_uninit__opensl(pDevice);
31587 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.", ma_result_from_OpenSL(resultSL));
31588 }
31589
31590 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture);
31591 if (resultSL != SL_RESULT_SUCCESS) {
31592 ma_device_uninit__opensl(pDevice);
31593 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", ma_result_from_OpenSL(resultSL));
31594 }
31595
31596 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice);
31597 if (resultSL != SL_RESULT_SUCCESS) {
31598 ma_device_uninit__opensl(pDevice);
31599 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", ma_result_from_OpenSL(resultSL));
31600 }
31601
31602 /* The internal format is determined by the "pcm" object. */
31603 ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap));
31604
31605 /* Buffer. */
31606 pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
31607 pDevice->opensl.currentBufferIndexCapture = 0;
31608
31609 bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount;
31610 pDevice->opensl.pBufferCapture = (ma_uint8*)ma__calloc_from_callbacks(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);
31611 if (pDevice->opensl.pBufferCapture == NULL) {
31612 ma_device_uninit__opensl(pDevice);
31613 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.", MA_OUT_OF_MEMORY);
31614 }
31615 MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes);
31616 }
31617
31618 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
31619 ma_SLDataFormat_PCM pcm;
31620 SLDataSource source;
31621 SLDataLocator_OutputMix outmixLocator;
31622 SLDataSink sink;
31623 SLAndroidConfigurationItf pPlayerConfig;
31624
31625 ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm);
31626
31627 resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL);
31628 if (resultSL != SL_RESULT_SUCCESS) {
31629 ma_device_uninit__opensl(pDevice);
31630 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix.", ma_result_from_OpenSL(resultSL));
31631 }
31632
31633 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE);
31634 if (resultSL != SL_RESULT_SUCCESS) {
31635 ma_device_uninit__opensl(pDevice);
31636 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.", ma_result_from_OpenSL(resultSL));
31637 }
31638
31639 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix);
31640 if (resultSL != SL_RESULT_SUCCESS) {
31641 ma_device_uninit__opensl(pDevice);
31642 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.", ma_result_from_OpenSL(resultSL));
31643 }
31644
31645 /* Set the output device. */
31646 if (pDescriptorPlayback->pDeviceID != NULL) {
31647 SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl;
31648 MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL);
31649 }
31650
31651 queue.numBuffers = pDescriptorPlayback->periodCount;
31652
31653 source.pLocator = &queue;
31654 source.pFormat = (SLDataFormat_PCM*)&pcm;
31655
31656 outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
31657 outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj;
31658
31659 sink.pLocator = &outmixLocator;
31660 sink.pFormat = NULL;
31661
31662 resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
31663 if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) {
31664 /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
31665 pcm.formatType = SL_DATAFORMAT_PCM;
31666 pcm.numChannels = 2;
31667 ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;
31668 pcm.bitsPerSample = 16;
31669 pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
31670 pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
31671 resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
31672 }
31673
31674 if (resultSL != SL_RESULT_SUCCESS) {
31675 ma_device_uninit__opensl(pDevice);
31676 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player.", ma_result_from_OpenSL(resultSL));
31677 }
31678
31679
31680 /* Set the stream type before realizing the player. */
31681 if (pConfig->opensl.streamType != ma_opensl_stream_type_default) {
31682 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig);
31683 if (resultSL == SL_RESULT_SUCCESS) {
31684 SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType);
31685 resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
31686 if (resultSL != SL_RESULT_SUCCESS) {
31687 /* Failed to set the configuration. Just keep going. */
31688 }
31689 }
31690 }
31691
31692 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE);
31693 if (resultSL != SL_RESULT_SUCCESS) {
31694 ma_device_uninit__opensl(pDevice);
31695 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.", ma_result_from_OpenSL(resultSL));
31696 }
31697
31698 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer);
31699 if (resultSL != SL_RESULT_SUCCESS) {
31700 ma_device_uninit__opensl(pDevice);
31701 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.", ma_result_from_OpenSL(resultSL));
31702 }
31703
31704 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback);
31705 if (resultSL != SL_RESULT_SUCCESS) {
31706 ma_device_uninit__opensl(pDevice);
31707 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", ma_result_from_OpenSL(resultSL));
31708 }
31709
31710 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice);
31711 if (resultSL != SL_RESULT_SUCCESS) {
31712 ma_device_uninit__opensl(pDevice);
31713 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", ma_result_from_OpenSL(resultSL));
31714 }
31715
31716 /* The internal format is determined by the "pcm" object. */
31717 ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap));
31718
31719 /* Buffer. */
31720 pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
31721 pDevice->opensl.currentBufferIndexPlayback = 0;
31722
31723 bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount;
31724 pDevice->opensl.pBufferPlayback = (ma_uint8*)ma__calloc_from_callbacks(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);
31725 if (pDevice->opensl.pBufferPlayback == NULL) {
31726 ma_device_uninit__opensl(pDevice);
31727 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.", MA_OUT_OF_MEMORY);
31728 }
31729 MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes);
31730 }
31731
31732 return MA_SUCCESS;
31733#else
31734 return MA_NO_BACKEND; /* Non-Android implementations are not supported. */
31735#endif
31736}
31737
31738static ma_result ma_device_start__opensl(ma_device* pDevice)
31739{
31740 SLresult resultSL;
31741 size_t periodSizeInBytes;
31742 ma_uint32 iPeriod;
31743
31744 MA_ASSERT(pDevice != NULL);
31745
31746 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. */
31747 if (g_maOpenSLInitCounter == 0) {
31748 return MA_INVALID_OPERATION;
31749 }
31750
31751 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31752 resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING);
31753 if (resultSL != SL_RESULT_SUCCESS) {
31754 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device.", ma_result_from_OpenSL(resultSL));
31755 }
31756
31757 periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
31758 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
31759 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes);
31760 if (resultSL != SL_RESULT_SUCCESS) {
31761 MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
31762 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device.", ma_result_from_OpenSL(resultSL));
31763 }
31764 }
31765 }
31766
31767 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31768 resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING);
31769 if (resultSL != SL_RESULT_SUCCESS) {
31770 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device.", ma_result_from_OpenSL(resultSL));
31771 }
31772
31773 /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueu silent buffers. */
31774 if (pDevice->type == ma_device_type_duplex) {
31775 MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
31776 } else {
31777 ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback);
31778 }
31779
31780 periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
31781 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
31782 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes);
31783 if (resultSL != SL_RESULT_SUCCESS) {
31784 MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
31785 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device.", ma_result_from_OpenSL(resultSL));
31786 }
31787 }
31788 }
31789
31790 return MA_SUCCESS;
31791}
31792
31793static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType)
31794{
31795 SLAndroidSimpleBufferQueueItf pBufferQueue;
31796
31797 MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback);
31798
31799 if (pDevice->type == ma_device_type_capture) {
31800 pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture;
31801 pDevice->opensl.isDrainingCapture = MA_TRUE;
31802 } else {
31803 pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback;
31804 pDevice->opensl.isDrainingPlayback = MA_TRUE;
31805 }
31806
31807 for (;;) {
31808 SLAndroidSimpleBufferQueueState state;
31809
31810 MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state);
31811 if (state.count == 0) {
31812 break;
31813 }
31814
31815 ma_sleep(10);
31816 }
31817
31818 if (pDevice->type == ma_device_type_capture) {
31819 pDevice->opensl.isDrainingCapture = MA_FALSE;
31820 } else {
31821 pDevice->opensl.isDrainingPlayback = MA_FALSE;
31822 }
31823
31824 return MA_SUCCESS;
31825}
31826
31827static ma_result ma_device_stop__opensl(ma_device* pDevice)
31828{
31829 SLresult resultSL;
31830 ma_stop_proc onStop;
31831
31832 MA_ASSERT(pDevice != NULL);
31833
31834 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. */
31835 if (g_maOpenSLInitCounter == 0) {
31836 return MA_INVALID_OPERATION;
31837 }
31838
31839 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31840 ma_device_drain__opensl(pDevice, ma_device_type_capture);
31841
31842 resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
31843 if (resultSL != SL_RESULT_SUCCESS) {
31844 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device.", ma_result_from_OpenSL(resultSL));
31845 }
31846
31847 MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture);
31848 }
31849
31850 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31851 ma_device_drain__opensl(pDevice, ma_device_type_playback);
31852
31853 resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
31854 if (resultSL != SL_RESULT_SUCCESS) {
31855 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device.", ma_result_from_OpenSL(resultSL));
31856 }
31857
31858 MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback);
31859 }
31860
31861 /* 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. */
31862 onStop = pDevice->onStop;
31863 if (onStop) {
31864 onStop(pDevice);
31865 }
31866
31867 return MA_SUCCESS;
31868}
31869
31870
31871static ma_result ma_context_uninit__opensl(ma_context* pContext)
31872{
31873 MA_ASSERT(pContext != NULL);
31874 MA_ASSERT(pContext->backend == ma_backend_opensl);
31875 (void)pContext;
31876
31877 /* Uninit global data. */
31878 ma_spinlock_lock(&g_maOpenSLSpinlock);
31879 {
31880 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(). */
31881
31882 g_maOpenSLInitCounter -= 1;
31883 if (g_maOpenSLInitCounter == 0) {
31884 (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
31885 }
31886 }
31887 ma_spinlock_unlock(&g_maOpenSLSpinlock);
31888
31889 return MA_SUCCESS;
31890}
31891
31892static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle)
31893{
31894 /* 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. */
31895 ma_handle* p = (ma_handle*)ma_dlsym(pContext, pContext->opensl.libOpenSLES, pName);
31896 if (p == NULL) {
31897 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL|ES] Cannot find symbol %s", pName);
31898 return MA_NO_BACKEND;
31899 }
31900
31901 *pHandle = *p;
31902 return MA_SUCCESS;
31903}
31904
31905static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext)
31906{
31907 g_maOpenSLInitCounter += 1;
31908 if (g_maOpenSLInitCounter == 1) {
31909 SLresult resultSL;
31910
31911 resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL);
31912 if (resultSL != SL_RESULT_SUCCESS) {
31913 g_maOpenSLInitCounter -= 1;
31914 return ma_result_from_OpenSL(resultSL);
31915 }
31916
31917 (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE);
31918
31919 resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL);
31920 if (resultSL != SL_RESULT_SUCCESS) {
31921 (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
31922 g_maOpenSLInitCounter -= 1;
31923 return ma_result_from_OpenSL(resultSL);
31924 }
31925 }
31926
31927 return MA_SUCCESS;
31928}
31929
31930static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
31931{
31932 ma_result result;
31933
31934#if !defined(MA_NO_RUNTIME_LINKING)
31935 size_t i;
31936 const char* libOpenSLESNames[] = {
31937 "libOpenSLES.so"
31938 };
31939#endif
31940
31941 MA_ASSERT(pContext != NULL);
31942
31943 (void)pConfig;
31944
31945#if !defined(MA_NO_RUNTIME_LINKING)
31946 /*
31947 Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One
31948 report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime
31949 and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any
31950 references to the symbols and will hopefully skip the checks.
31951 */
31952 for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) {
31953 pContext->opensl.libOpenSLES = ma_dlopen(pContext, libOpenSLESNames[i]);
31954 if (pContext->opensl.libOpenSLES != NULL) {
31955 break;
31956 }
31957 }
31958
31959 if (pContext->opensl.libOpenSLES == NULL) {
31960 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Could not find libOpenSLES.so");
31961 return MA_NO_BACKEND;
31962 }
31963
31964 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE);
31965 if (result != MA_SUCCESS) {
31966 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
31967 return result;
31968 }
31969
31970 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES);
31971 if (result != MA_SUCCESS) {
31972 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
31973 return result;
31974 }
31975
31976 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE);
31977 if (result != MA_SUCCESS) {
31978 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
31979 return result;
31980 }
31981
31982 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD);
31983 if (result != MA_SUCCESS) {
31984 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
31985 return result;
31986 }
31987
31988 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY);
31989 if (result != MA_SUCCESS) {
31990 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
31991 return result;
31992 }
31993
31994 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX);
31995 if (result != MA_SUCCESS) {
31996 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
31997 return result;
31998 }
31999
32000 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION);
32001 if (result != MA_SUCCESS) {
32002 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
32003 return result;
32004 }
32005
32006 pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(pContext, pContext->opensl.libOpenSLES, "slCreateEngine");
32007 if (pContext->opensl.slCreateEngine == NULL) {
32008 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
32009 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Cannot find symbol slCreateEngine.");
32010 return MA_NO_BACKEND;
32011 }
32012#else
32013 pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE;
32014 pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES;
32015 pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
32016 pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD;
32017 pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY;
32018 pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX;
32019 pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION;
32020 pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine;
32021#endif
32022
32023
32024 /* Initialize global data first if applicable. */
32025 ma_spinlock_lock(&g_maOpenSLSpinlock);
32026 {
32027 result = ma_context_init_engine_nolock__opensl(pContext);
32028 }
32029 ma_spinlock_unlock(&g_maOpenSLSpinlock);
32030
32031 if (result != MA_SUCCESS) {
32032 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
32033 ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Failed to initialize OpenSL engine.");
32034 return result;
32035 }
32036
32037 pCallbacks->onContextInit = ma_context_init__opensl;
32038 pCallbacks->onContextUninit = ma_context_uninit__opensl;
32039 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl;
32040 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl;
32041 pCallbacks->onDeviceInit = ma_device_init__opensl;
32042 pCallbacks->onDeviceUninit = ma_device_uninit__opensl;
32043 pCallbacks->onDeviceStart = ma_device_start__opensl;
32044 pCallbacks->onDeviceStop = ma_device_stop__opensl;
32045 pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */
32046 pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */
32047 pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */
32048
32049 return MA_SUCCESS;
32050}
32051#endif /* OpenSL|ES */
32052
32053
32054/******************************************************************************
32055
32056Web Audio Backend
32057
32058******************************************************************************/
32059#ifdef MA_HAS_WEBAUDIO
32060#include <emscripten/emscripten.h>
32061
32062static ma_bool32 ma_is_capture_supported__webaudio()
32063{
32064 return EM_ASM_INT({
32065 return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined);
32066 }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */
32067}
32068
32069#ifdef __cplusplus
32070extern "C" {
32071#endif
32072void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
32073{
32074 ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount);
32075}
32076
32077void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
32078{
32079 ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount);
32080}
32081#ifdef __cplusplus
32082}
32083#endif
32084
32085static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
32086{
32087 ma_bool32 cbResult = MA_TRUE;
32088
32089 MA_ASSERT(pContext != NULL);
32090 MA_ASSERT(callback != NULL);
32091
32092 /* Only supporting default devices for now. */
32093
32094 /* Playback. */
32095 if (cbResult) {
32096 ma_device_info deviceInfo;
32097 MA_ZERO_OBJECT(&deviceInfo);
32098 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
32099 deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */
32100 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
32101 }
32102
32103 /* Capture. */
32104 if (cbResult) {
32105 if (ma_is_capture_supported__webaudio()) {
32106 ma_device_info deviceInfo;
32107 MA_ZERO_OBJECT(&deviceInfo);
32108 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
32109 deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */
32110 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
32111 }
32112 }
32113
32114 return MA_SUCCESS;
32115}
32116
32117static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
32118{
32119 MA_ASSERT(pContext != NULL);
32120
32121 if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
32122 return MA_NO_DEVICE;
32123 }
32124
32125 MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio));
32126
32127 /* Only supporting default devices for now. */
32128 (void)pDeviceID;
32129 if (deviceType == ma_device_type_playback) {
32130 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
32131 } else {
32132 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
32133 }
32134
32135 /* Only supporting default devices. */
32136 pDeviceInfo->isDefault = MA_TRUE;
32137
32138 /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */
32139 pDeviceInfo->nativeDataFormats[0].flags = 0;
32140 pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown;
32141 pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channels are supported. */
32142 pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({
32143 try {
32144 var temp = new (window.AudioContext || window.webkitAudioContext)();
32145 var sampleRate = temp.sampleRate;
32146 temp.close();
32147 return sampleRate;
32148 } catch(e) {
32149 return 0;
32150 }
32151 }, 0); /* Must pass in a dummy argument for C99 compatibility. */
32152
32153 if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) {
32154 return MA_NO_DEVICE;
32155 }
32156
32157 pDeviceInfo->nativeDataFormatCount = 1;
32158
32159 return MA_SUCCESS;
32160}
32161
32162
32163static void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_type deviceType, int deviceIndex)
32164{
32165 MA_ASSERT(pDevice != NULL);
32166
32167 EM_ASM({
32168 var device = miniaudio.get_device_by_index($0);
32169
32170 /* Make sure all nodes are disconnected and marked for collection. */
32171 if (device.scriptNode !== undefined) {
32172 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... */
32173 device.scriptNode.disconnect();
32174 device.scriptNode = undefined;
32175 }
32176 if (device.streamNode !== undefined) {
32177 device.streamNode.disconnect();
32178 device.streamNode = undefined;
32179 }
32180
32181 /*
32182 Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want
32183 to clear the callback before closing.
32184 */
32185 device.webaudio.close();
32186 device.webaudio = undefined;
32187
32188 /* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */
32189 if (device.intermediaryBuffer !== undefined) {
32190 Module._free(device.intermediaryBuffer);
32191 device.intermediaryBuffer = undefined;
32192 device.intermediaryBufferView = undefined;
32193 device.intermediaryBufferSizeInBytes = undefined;
32194 }
32195
32196 /* Make sure the device is untracked so the slot can be reused later. */
32197 miniaudio.untrack_device_by_index($0);
32198 }, deviceIndex, deviceType);
32199}
32200
32201static ma_result ma_device_uninit__webaudio(ma_device* pDevice)
32202{
32203 MA_ASSERT(pDevice != NULL);
32204
32205 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
32206 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
32207 }
32208
32209 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
32210 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback);
32211 }
32212
32213 return MA_SUCCESS;
32214}
32215
32216static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
32217{
32218 /*
32219 There have been reports of the default buffer size being too small on some browsers. There have been reports of the default buffer
32220 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
32221 standard defaults.
32222 */
32223 ma_uint32 periodSizeInFrames;
32224
32225 if (pDescriptor->periodSizeInFrames == 0) {
32226 if (pDescriptor->periodSizeInMilliseconds == 0) {
32227 if (performanceProfile == ma_performance_profile_low_latency) {
32228 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */
32229 } else {
32230 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate);
32231 }
32232 } else {
32233 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
32234 }
32235 } else {
32236 periodSizeInFrames = pDescriptor->periodSizeInFrames;
32237 }
32238
32239 /* The size of the buffer must be a power of 2 and between 256 and 16384. */
32240 if (periodSizeInFrames < 256) {
32241 periodSizeInFrames = 256;
32242 } else if (periodSizeInFrames > 16384) {
32243 periodSizeInFrames = 16384;
32244 } else {
32245 periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames);
32246 }
32247
32248 return periodSizeInFrames;
32249}
32250
32251static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
32252{
32253 int deviceIndex;
32254 ma_uint32 channels;
32255 ma_uint32 sampleRate;
32256 ma_uint32 periodSizeInFrames;
32257
32258 MA_ASSERT(pDevice != NULL);
32259 MA_ASSERT(pConfig != NULL);
32260 MA_ASSERT(deviceType != ma_device_type_duplex);
32261
32262 if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
32263 return MA_NO_DEVICE;
32264 }
32265
32266 /* We're going to calculate some stuff in C just to simplify the JS code. */
32267 channels = (pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS;
32268 sampleRate = (pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE;
32269 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptor, sampleRate, pConfig->performanceProfile);
32270
32271
32272 /* 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. */
32273 deviceIndex = EM_ASM_INT({
32274 var channels = $0;
32275 var sampleRate = $1;
32276 var bufferSize = $2; /* In PCM frames. */
32277 var isCapture = $3;
32278 var pDevice = $4;
32279
32280 if (typeof(miniaudio) === 'undefined') {
32281 return -1; /* Context not initialized. */
32282 }
32283
32284 var device = {};
32285
32286 /* The AudioContext must be created in a suspended state. */
32287 device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate});
32288 device.webaudio.suspend();
32289 device.state = 1; /* MA_STATE_STOPPED */
32290
32291 /*
32292 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
32293 JavaScript and C it needs to be allocated and freed using Module._malloc() and Module._free().
32294 */
32295 device.intermediaryBufferSizeInBytes = channels * bufferSize * 4;
32296 device.intermediaryBuffer = Module._malloc(device.intermediaryBufferSizeInBytes);
32297 device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
32298
32299 /*
32300 Both playback and capture devices use a ScriptProcessorNode for performing per-sample operations.
32301
32302 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
32303 that's periodically fired, just like a normal audio callback function. But apparently this design is "flawed" and is now deprecated in favour of
32304 something called AudioWorklets which _forces_ you to load a _separate_ .js file at run time... nice... Hopefully ScriptProcessorNode will continue to
32305 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
32306 implementation. I'll be avoiding that insane AudioWorklet API like the plague...
32307
32308 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
32309 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
32310 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
32311 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
32312 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
32313 this for now. If anyone knows how I could get raw PCM data using the MediaRecorder API please let me know!
32314 */
32315 device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channels, channels);
32316
32317 if (isCapture) {
32318 device.scriptNode.onaudioprocess = function(e) {
32319 if (device.intermediaryBuffer === undefined) {
32320 return; /* This means the device has been uninitialized. */
32321 }
32322
32323 if(device.intermediaryBufferView.length == 0) {
32324 /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */
32325 device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
32326 }
32327
32328 /* Make sure silence it output to the AudioContext destination. Not doing this will cause sound to come out of the speakers! */
32329 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
32330 e.outputBuffer.getChannelData(iChannel).fill(0.0);
32331 }
32332
32333 /* There are some situations where we may want to send silence to the client. */
32334 var sendSilence = false;
32335 if (device.streamNode === undefined) {
32336 sendSilence = true;
32337 }
32338
32339 /* Sanity check. This will never happen, right? */
32340 if (e.inputBuffer.numberOfChannels != channels) {
32341 console.log("Capture: Channel count mismatch. " + e.inputBufer.numberOfChannels + " != " + channels + ". Sending silence.");
32342 sendSilence = true;
32343 }
32344
32345 /* This looped design guards against the situation where e.inputBuffer is a different size to the original buffer size. Should never happen in practice. */
32346 var totalFramesProcessed = 0;
32347 while (totalFramesProcessed < e.inputBuffer.length) {
32348 var framesRemaining = e.inputBuffer.length - totalFramesProcessed;
32349 var framesToProcess = framesRemaining;
32350 if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
32351 framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
32352 }
32353
32354 /* 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. */
32355 if (sendSilence) {
32356 device.intermediaryBufferView.fill(0.0);
32357 } else {
32358 for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
32359 for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) {
32360 device.intermediaryBufferView[iFrame*channels + iChannel] = e.inputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame];
32361 }
32362 }
32363 }
32364
32365 /* Send data to the client from our intermediary buffer. */
32366 ccall("ma_device_process_pcm_frames_capture__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]);
32367
32368 totalFramesProcessed += framesToProcess;
32369 }
32370 };
32371
32372 navigator.mediaDevices.getUserMedia({audio:true, video:false})
32373 .then(function(stream) {
32374 device.streamNode = device.webaudio.createMediaStreamSource(stream);
32375 device.streamNode.connect(device.scriptNode);
32376 device.scriptNode.connect(device.webaudio.destination);
32377 })
32378 .catch(function(error) {
32379 /* I think this should output silence... */
32380 device.scriptNode.connect(device.webaudio.destination);
32381 });
32382 } else {
32383 device.scriptNode.onaudioprocess = function(e) {
32384 if (device.intermediaryBuffer === undefined) {
32385 return; /* This means the device has been uninitialized. */
32386 }
32387
32388 if(device.intermediaryBufferView.length == 0) {
32389 /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */
32390 device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
32391 }
32392
32393 var outputSilence = false;
32394
32395 /* Sanity check. This will never happen, right? */
32396 if (e.outputBuffer.numberOfChannels != channels) {
32397 console.log("Playback: Channel count mismatch. " + e.outputBufer.numberOfChannels + " != " + channels + ". Outputting silence.");
32398 outputSilence = true;
32399 return;
32400 }
32401
32402 /* This looped design guards against the situation where e.outputBuffer is a different size to the original buffer size. Should never happen in practice. */
32403 var totalFramesProcessed = 0;
32404 while (totalFramesProcessed < e.outputBuffer.length) {
32405 var framesRemaining = e.outputBuffer.length - totalFramesProcessed;
32406 var framesToProcess = framesRemaining;
32407 if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
32408 framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
32409 }
32410
32411 /* Read data from the client into our intermediary buffer. */
32412 ccall("ma_device_process_pcm_frames_playback__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]);
32413
32414 /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */
32415 if (outputSilence) {
32416 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
32417 e.outputBuffer.getChannelData(iChannel).fill(0.0);
32418 }
32419 } else {
32420 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
32421 for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
32422 e.outputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame] = device.intermediaryBufferView[iFrame*channels + iChannel];
32423 }
32424 }
32425 }
32426
32427 totalFramesProcessed += framesToProcess;
32428 }
32429 };
32430
32431 device.scriptNode.connect(device.webaudio.destination);
32432 }
32433
32434 return miniaudio.track_device(device);
32435 }, channels, sampleRate, periodSizeInFrames, deviceType == ma_device_type_capture, pDevice);
32436
32437 if (deviceIndex < 0) {
32438 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
32439 }
32440
32441 if (deviceType == ma_device_type_capture) {
32442 pDevice->webaudio.indexCapture = deviceIndex;
32443 } else {
32444 pDevice->webaudio.indexPlayback = deviceIndex;
32445 }
32446
32447 pDescriptor->format = ma_format_f32;
32448 pDescriptor->channels = channels;
32449 ma_get_standard_channel_map(ma_standard_channel_map_webaudio, pDescriptor->channels, pDescriptor->channelMap);
32450 pDescriptor->sampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
32451 pDescriptor->periodSizeInFrames = periodSizeInFrames;
32452 pDescriptor->periodCount = 1;
32453
32454 return MA_SUCCESS;
32455}
32456
32457static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
32458{
32459 ma_result result;
32460
32461 if (pConfig->deviceType == ma_device_type_loopback) {
32462 return MA_DEVICE_TYPE_NOT_SUPPORTED;
32463 }
32464
32465 /* No exclusive mode with Web Audio. */
32466 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
32467 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
32468 return MA_SHARE_MODE_NOT_SUPPORTED;
32469 }
32470
32471 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
32472 result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
32473 if (result != MA_SUCCESS) {
32474 return result;
32475 }
32476 }
32477
32478 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
32479 result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
32480 if (result != MA_SUCCESS) {
32481 if (pConfig->deviceType == ma_device_type_duplex) {
32482 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
32483 }
32484 return result;
32485 }
32486 }
32487
32488 return MA_SUCCESS;
32489}
32490
32491static ma_result ma_device_start__webaudio(ma_device* pDevice)
32492{
32493 MA_ASSERT(pDevice != NULL);
32494
32495 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
32496 EM_ASM({
32497 var device = miniaudio.get_device_by_index($0);
32498 device.webaudio.resume();
32499 device.state = 2; /* MA_STATE_STARTED */
32500 }, pDevice->webaudio.indexCapture);
32501 }
32502
32503 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
32504 EM_ASM({
32505 var device = miniaudio.get_device_by_index($0);
32506 device.webaudio.resume();
32507 device.state = 2; /* MA_STATE_STARTED */
32508 }, pDevice->webaudio.indexPlayback);
32509 }
32510
32511 return MA_SUCCESS;
32512}
32513
32514static ma_result ma_device_stop__webaudio(ma_device* pDevice)
32515{
32516 MA_ASSERT(pDevice != NULL);
32517
32518 /*
32519 From the WebAudio API documentation for AudioContext.suspend():
32520
32521 Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the
32522 destination, and then allows the system to release its claim on audio hardware.
32523
32524 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
32525 do any kind of explicit draining.
32526 */
32527
32528 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
32529 EM_ASM({
32530 var device = miniaudio.get_device_by_index($0);
32531 device.webaudio.suspend();
32532 device.state = 1; /* MA_STATE_STOPPED */
32533 }, pDevice->webaudio.indexCapture);
32534 }
32535
32536 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
32537 EM_ASM({
32538 var device = miniaudio.get_device_by_index($0);
32539 device.webaudio.suspend();
32540 device.state = 1; /* MA_STATE_STOPPED */
32541 }, pDevice->webaudio.indexPlayback);
32542 }
32543
32544 ma_stop_proc onStop = pDevice->onStop;
32545 if (onStop) {
32546 onStop(pDevice);
32547 }
32548
32549 return MA_SUCCESS;
32550}
32551
32552static ma_result ma_context_uninit__webaudio(ma_context* pContext)
32553{
32554 MA_ASSERT(pContext != NULL);
32555 MA_ASSERT(pContext->backend == ma_backend_webaudio);
32556
32557 /* Nothing needs to be done here. */
32558 (void)pContext;
32559
32560 return MA_SUCCESS;
32561}
32562
32563static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
32564{
32565 int resultFromJS;
32566
32567 MA_ASSERT(pContext != NULL);
32568
32569 (void)pConfig; /* Unused. */
32570
32571 /* Here is where our global JavaScript object is initialized. */
32572 resultFromJS = EM_ASM_INT({
32573 if ((window.AudioContext || window.webkitAudioContext) === undefined) {
32574 return 0; /* Web Audio not supported. */
32575 }
32576
32577 if (typeof(miniaudio) === 'undefined') {
32578 miniaudio = {};
32579 miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */
32580
32581 miniaudio.track_device = function(device) {
32582 /* Try inserting into a free slot first. */
32583 for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
32584 if (miniaudio.devices[iDevice] == null) {
32585 miniaudio.devices[iDevice] = device;
32586 return iDevice;
32587 }
32588 }
32589
32590 /* Getting here means there is no empty slots in the array so we just push to the end. */
32591 miniaudio.devices.push(device);
32592 return miniaudio.devices.length - 1;
32593 };
32594
32595 miniaudio.untrack_device_by_index = function(deviceIndex) {
32596 /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */
32597 miniaudio.devices[deviceIndex] = null;
32598
32599 /* Trim the array if possible. */
32600 while (miniaudio.devices.length > 0) {
32601 if (miniaudio.devices[miniaudio.devices.length-1] == null) {
32602 miniaudio.devices.pop();
32603 } else {
32604 break;
32605 }
32606 }
32607 };
32608
32609 miniaudio.untrack_device = function(device) {
32610 for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
32611 if (miniaudio.devices[iDevice] == device) {
32612 return miniaudio.untrack_device_by_index(iDevice);
32613 }
32614 }
32615 };
32616
32617 miniaudio.get_device_by_index = function(deviceIndex) {
32618 return miniaudio.devices[deviceIndex];
32619 };
32620
32621 miniaudio.unlock_event_types = (function(){
32622 return ['touchstart', 'touchend', 'click'];
32623 })();
32624
32625 miniaudio.unlock = function() {
32626 for(var i = 0; i < miniaudio.devices.length; ++i) {
32627 var device = miniaudio.devices[i];
32628 if (device != null && device.webaudio != null && device.state === 2 /* MA_STATE_STARTED */) {
32629 device.webaudio.resume();
32630 }
32631 }
32632 miniaudio.unlock_event_types.map(function(event_type) {
32633 document.removeEventListener(event_type, miniaudio.unlock, true);
32634 });
32635 };
32636
32637 miniaudio.unlock_event_types.map(function(event_type) {
32638 document.addEventListener(event_type, miniaudio.unlock, true);
32639 });
32640 }
32641
32642 return 1;
32643 }, 0); /* Must pass in a dummy argument for C99 compatibility. */
32644
32645 if (resultFromJS != 1) {
32646 return MA_FAILED_TO_INIT_BACKEND;
32647 }
32648
32649 pCallbacks->onContextInit = ma_context_init__webaudio;
32650 pCallbacks->onContextUninit = ma_context_uninit__webaudio;
32651 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio;
32652 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio;
32653 pCallbacks->onDeviceInit = ma_device_init__webaudio;
32654 pCallbacks->onDeviceUninit = ma_device_uninit__webaudio;
32655 pCallbacks->onDeviceStart = ma_device_start__webaudio;
32656 pCallbacks->onDeviceStop = ma_device_stop__webaudio;
32657 pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */
32658 pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */
32659 pCallbacks->onDeviceDataLoop = NULL; /* Not needed because WebAudio is asynchronous. */
32660
32661 return MA_SUCCESS;
32662}
32663#endif /* Web Audio */
32664
32665
32666
32667static ma_bool32 ma__is_channel_map_valid(const ma_channel* channelMap, ma_uint32 channels)
32668{
32669 /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */
32670 if (channelMap[0] != MA_CHANNEL_NONE) {
32671 ma_uint32 iChannel;
32672
32673 if (channels == 0 || channels > MA_MAX_CHANNELS) {
32674 return MA_FALSE; /* Channel count out of range. */
32675 }
32676
32677 /* A channel cannot be present in the channel map more than once. */
32678 for (iChannel = 0; iChannel < channels; ++iChannel) {
32679 ma_uint32 jChannel;
32680 for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) {
32681 if (channelMap[iChannel] == channelMap[jChannel]) {
32682 return MA_FALSE;
32683 }
32684 }
32685 }
32686 }
32687
32688 return MA_TRUE;
32689}
32690
32691
32692static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType)
32693{
32694 ma_result result;
32695
32696 MA_ASSERT(pDevice != NULL);
32697
32698 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
32699 if (pDevice->capture.format == ma_format_unknown) {
32700 pDevice->capture.format = pDevice->capture.internalFormat;
32701 }
32702 if (pDevice->capture.channels == 0) {
32703 pDevice->capture.channels = pDevice->capture.internalChannels;
32704 }
32705 if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) {
32706 MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS);
32707 if (pDevice->capture.internalChannels == pDevice->capture.channels) {
32708 ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels);
32709 } else {
32710 if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) {
32711 ma_channel_map_init_blank(pDevice->capture.channels, pDevice->capture.channelMap);
32712 } else {
32713 ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->capture.channels, pDevice->capture.channelMap);
32714 }
32715 }
32716 }
32717 }
32718
32719 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
32720 if (pDevice->playback.format == ma_format_unknown) {
32721 pDevice->playback.format = pDevice->playback.internalFormat;
32722 }
32723 if (pDevice->playback.channels == 0) {
32724 pDevice->playback.channels = pDevice->playback.internalChannels;
32725 }
32726 if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) {
32727 MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS);
32728 if (pDevice->playback.internalChannels == pDevice->playback.channels) {
32729 ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels);
32730 } else {
32731 if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) {
32732 ma_channel_map_init_blank(pDevice->playback.channels, pDevice->playback.channelMap);
32733 } else {
32734 ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->playback.channels, pDevice->playback.channelMap);
32735 }
32736 }
32737 }
32738 }
32739
32740 if (pDevice->sampleRate == 0) {
32741 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
32742 pDevice->sampleRate = pDevice->capture.internalSampleRate;
32743 } else {
32744 pDevice->sampleRate = pDevice->playback.internalSampleRate;
32745 }
32746 }
32747
32748 /* Data converters. */
32749 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
32750 /* Converting from internal device format to client format. */
32751 ma_data_converter_config converterConfig = ma_data_converter_config_init_default();
32752 converterConfig.formatIn = pDevice->capture.internalFormat;
32753 converterConfig.channelsIn = pDevice->capture.internalChannels;
32754 converterConfig.sampleRateIn = pDevice->capture.internalSampleRate;
32755 ma_channel_map_copy(converterConfig.channelMapIn, pDevice->capture.internalChannelMap, ma_min(pDevice->capture.internalChannels, MA_MAX_CHANNELS));
32756 converterConfig.formatOut = pDevice->capture.format;
32757 converterConfig.channelsOut = pDevice->capture.channels;
32758 converterConfig.sampleRateOut = pDevice->sampleRate;
32759 ma_channel_map_copy(converterConfig.channelMapOut, pDevice->capture.channelMap, ma_min(pDevice->capture.channels, MA_MAX_CHANNELS));
32760 converterConfig.channelMixMode = pDevice->capture.channelMixMode;
32761 converterConfig.resampling.allowDynamicSampleRate = MA_FALSE;
32762 converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
32763 converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
32764 converterConfig.resampling.speex.quality = pDevice->resampling.speex.quality;
32765
32766 result = ma_data_converter_init(&converterConfig, &pDevice->capture.converter);
32767 if (result != MA_SUCCESS) {
32768 return result;
32769 }
32770 }
32771
32772 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
32773 /* Converting from client format to device format. */
32774 ma_data_converter_config converterConfig = ma_data_converter_config_init_default();
32775 converterConfig.formatIn = pDevice->playback.format;
32776 converterConfig.channelsIn = pDevice->playback.channels;
32777 converterConfig.sampleRateIn = pDevice->sampleRate;
32778 ma_channel_map_copy(converterConfig.channelMapIn, pDevice->playback.channelMap, ma_min(pDevice->playback.channels, MA_MAX_CHANNELS));
32779 converterConfig.formatOut = pDevice->playback.internalFormat;
32780 converterConfig.channelsOut = pDevice->playback.internalChannels;
32781 converterConfig.sampleRateOut = pDevice->playback.internalSampleRate;
32782 ma_channel_map_copy(converterConfig.channelMapOut, pDevice->playback.internalChannelMap, ma_min(pDevice->playback.internalChannels, MA_MAX_CHANNELS));
32783 converterConfig.channelMixMode = pDevice->playback.channelMixMode;
32784 converterConfig.resampling.allowDynamicSampleRate = MA_FALSE;
32785 converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
32786 converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
32787 converterConfig.resampling.speex.quality = pDevice->resampling.speex.quality;
32788
32789 result = ma_data_converter_init(&converterConfig, &pDevice->playback.converter);
32790 if (result != MA_SUCCESS) {
32791 return result;
32792 }
32793 }
32794
32795 return MA_SUCCESS;
32796}
32797
32798
32799static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
32800{
32801 ma_device* pDevice = (ma_device*)pData;
32802 MA_ASSERT(pDevice != NULL);
32803
32804#ifdef MA_WIN32
32805 ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE);
32806#endif
32807
32808 /*
32809 When the device is being initialized it's initial state is set to MA_STATE_UNINITIALIZED. Before returning from
32810 ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately
32811 after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker
32812 thread to signal an event to know when the worker thread is ready for action.
32813 */
32814 ma_device__set_state(pDevice, MA_STATE_STOPPED);
32815 ma_event_signal(&pDevice->stopEvent);
32816
32817 for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */
32818 ma_result startResult;
32819 ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the onStop callback. */
32820
32821 /* We wait on an event to know when something has requested that the device be started and the main loop entered. */
32822 ma_event_wait(&pDevice->wakeupEvent);
32823
32824 /* Default result code. */
32825 pDevice->workResult = MA_SUCCESS;
32826
32827 /* If the reason for the wake up is that we are terminating, just break from the loop. */
32828 if (ma_device_get_state(pDevice) == MA_STATE_UNINITIALIZED) {
32829 break;
32830 }
32831
32832 /*
32833 Getting to this point means the device is wanting to get started. The function that has requested that the device
32834 be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event
32835 in both the success and error case. It's important that the state of the device is set _before_ signaling the event.
32836 */
32837 MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STARTING);
32838
32839 /* If the device has a start callback, start it now. */
32840 if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
32841 startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice);
32842 } else {
32843 startResult = MA_SUCCESS;
32844 }
32845
32846 if (startResult != MA_SUCCESS) {
32847 pDevice->workResult = startResult;
32848 continue; /* Failed to start. Loop back to the start and wait for something to happen (pDevice->wakeupEvent). */
32849 }
32850
32851 /* Make sure the state is set appropriately. */
32852 ma_device__set_state(pDevice, MA_STATE_STARTED);
32853 ma_event_signal(&pDevice->startEvent);
32854
32855 if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) {
32856 pDevice->pContext->callbacks.onDeviceDataLoop(pDevice);
32857 } else {
32858 /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */
32859 ma_device_audio_thread__default_read_write(pDevice);
32860 }
32861
32862 /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */
32863 if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
32864 stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice);
32865 } else {
32866 stopResult = MA_SUCCESS; /* No stop callback with the backend. Just assume successful. */
32867 }
32868
32869 /*
32870 After the device has stopped, make sure an event is posted. Don't post an onStop event if
32871 stopping failed. This can happen on some backends when the underlying stream has been
32872 stopped due to the device being physically unplugged or disabled via an OS setting.
32873 */
32874 if (pDevice->onStop && stopResult != MA_SUCCESS) {
32875 pDevice->onStop(pDevice);
32876 }
32877
32878 /* 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. */
32879 ma_device__set_state(pDevice, MA_STATE_STOPPED);
32880 ma_event_signal(&pDevice->stopEvent);
32881 }
32882
32883#ifdef MA_WIN32
32884 ma_CoUninitialize(pDevice->pContext);
32885#endif
32886
32887 return (ma_thread_result)0;
32888}
32889
32890
32891/* Helper for determining whether or not the given device is initialized. */
32892static ma_bool32 ma_device__is_initialized(ma_device* pDevice)
32893{
32894 if (pDevice == NULL) {
32895 return MA_FALSE;
32896 }
32897
32898 return ma_device_get_state(pDevice) != MA_STATE_UNINITIALIZED;
32899}
32900
32901
32902#ifdef MA_WIN32
32903static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext)
32904{
32905 ma_CoUninitialize(pContext);
32906 ma_dlclose(pContext, pContext->win32.hUser32DLL);
32907 ma_dlclose(pContext, pContext->win32.hOle32DLL);
32908 ma_dlclose(pContext, pContext->win32.hAdvapi32DLL);
32909
32910 return MA_SUCCESS;
32911}
32912
32913static ma_result ma_context_init_backend_apis__win32(ma_context* pContext)
32914{
32915#ifdef MA_WIN32_DESKTOP
32916 /* Ole32.dll */
32917 pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll");
32918 if (pContext->win32.hOle32DLL == NULL) {
32919 return MA_FAILED_TO_INIT_BACKEND;
32920 }
32921
32922 pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitializeEx");
32923 pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoUninitialize");
32924 pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoCreateInstance");
32925 pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree");
32926 pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear");
32927 pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2");
32928
32929
32930 /* User32.dll */
32931 pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll");
32932 if (pContext->win32.hUser32DLL == NULL) {
32933 return MA_FAILED_TO_INIT_BACKEND;
32934 }
32935
32936 pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow");
32937 pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow");
32938
32939
32940 /* Advapi32.dll */
32941 pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll");
32942 if (pContext->win32.hAdvapi32DLL == NULL) {
32943 return MA_FAILED_TO_INIT_BACKEND;
32944 }
32945
32946 pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA");
32947 pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey");
32948 pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA");
32949#endif
32950
32951 ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);
32952 return MA_SUCCESS;
32953}
32954#else
32955static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext)
32956{
32957#if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING)
32958 ma_dlclose(pContext, pContext->posix.pthreadSO);
32959#else
32960 (void)pContext;
32961#endif
32962
32963 return MA_SUCCESS;
32964}
32965
32966static ma_result ma_context_init_backend_apis__nix(ma_context* pContext)
32967{
32968 /* pthread */
32969#if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING)
32970 const char* libpthreadFileNames[] = {
32971 "libpthread.so",
32972 "libpthread.so.0",
32973 "libpthread.dylib"
32974 };
32975 size_t i;
32976
32977 for (i = 0; i < sizeof(libpthreadFileNames) / sizeof(libpthreadFileNames[0]); ++i) {
32978 pContext->posix.pthreadSO = ma_dlopen(pContext, libpthreadFileNames[i]);
32979 if (pContext->posix.pthreadSO != NULL) {
32980 break;
32981 }
32982 }
32983
32984 if (pContext->posix.pthreadSO == NULL) {
32985 return MA_FAILED_TO_INIT_BACKEND;
32986 }
32987
32988 pContext->posix.pthread_create = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_create");
32989 pContext->posix.pthread_join = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_join");
32990 pContext->posix.pthread_mutex_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_init");
32991 pContext->posix.pthread_mutex_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_destroy");
32992 pContext->posix.pthread_mutex_lock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_lock");
32993 pContext->posix.pthread_mutex_unlock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_unlock");
32994 pContext->posix.pthread_cond_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_init");
32995 pContext->posix.pthread_cond_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_destroy");
32996 pContext->posix.pthread_cond_wait = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_wait");
32997 pContext->posix.pthread_cond_signal = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_signal");
32998 pContext->posix.pthread_attr_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_init");
32999 pContext->posix.pthread_attr_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_destroy");
33000 pContext->posix.pthread_attr_setschedpolicy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedpolicy");
33001 pContext->posix.pthread_attr_getschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_getschedparam");
33002 pContext->posix.pthread_attr_setschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedparam");
33003#else
33004 pContext->posix.pthread_create = (ma_proc)pthread_create;
33005 pContext->posix.pthread_join = (ma_proc)pthread_join;
33006 pContext->posix.pthread_mutex_init = (ma_proc)pthread_mutex_init;
33007 pContext->posix.pthread_mutex_destroy = (ma_proc)pthread_mutex_destroy;
33008 pContext->posix.pthread_mutex_lock = (ma_proc)pthread_mutex_lock;
33009 pContext->posix.pthread_mutex_unlock = (ma_proc)pthread_mutex_unlock;
33010 pContext->posix.pthread_cond_init = (ma_proc)pthread_cond_init;
33011 pContext->posix.pthread_cond_destroy = (ma_proc)pthread_cond_destroy;
33012 pContext->posix.pthread_cond_wait = (ma_proc)pthread_cond_wait;
33013 pContext->posix.pthread_cond_signal = (ma_proc)pthread_cond_signal;
33014 pContext->posix.pthread_attr_init = (ma_proc)pthread_attr_init;
33015 pContext->posix.pthread_attr_destroy = (ma_proc)pthread_attr_destroy;
33016#if !defined(__EMSCRIPTEN__)
33017 pContext->posix.pthread_attr_setschedpolicy = (ma_proc)pthread_attr_setschedpolicy;
33018 pContext->posix.pthread_attr_getschedparam = (ma_proc)pthread_attr_getschedparam;
33019 pContext->posix.pthread_attr_setschedparam = (ma_proc)pthread_attr_setschedparam;
33020#endif
33021#endif
33022
33023 return MA_SUCCESS;
33024}
33025#endif
33026
33027static ma_result ma_context_init_backend_apis(ma_context* pContext)
33028{
33029 ma_result result;
33030#ifdef MA_WIN32
33031 result = ma_context_init_backend_apis__win32(pContext);
33032#else
33033 result = ma_context_init_backend_apis__nix(pContext);
33034#endif
33035
33036 return result;
33037}
33038
33039static ma_result ma_context_uninit_backend_apis(ma_context* pContext)
33040{
33041 ma_result result;
33042#ifdef MA_WIN32
33043 result = ma_context_uninit_backend_apis__win32(pContext);
33044#else
33045 result = ma_context_uninit_backend_apis__nix(pContext);
33046#endif
33047
33048 return result;
33049}
33050
33051
33052static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext)
33053{
33054 MA_ASSERT(pContext != NULL);
33055
33056 if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) {
33057 if (pContext->callbacks.onDeviceDataLoop == NULL) {
33058 return MA_TRUE;
33059 } else {
33060 return MA_FALSE;
33061 }
33062 } else {
33063 return MA_FALSE;
33064 }
33065}
33066
33067
33068MA_API ma_context_config ma_context_config_init()
33069{
33070 ma_context_config config;
33071 MA_ZERO_OBJECT(&config);
33072
33073 return config;
33074}
33075
33076MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext)
33077{
33078 ma_result result;
33079 ma_context_config defaultConfig;
33080 ma_backend defaultBackends[ma_backend_null+1];
33081 ma_uint32 iBackend;
33082 ma_backend* pBackendsToIterate;
33083 ma_uint32 backendsToIterateCount;
33084
33085 if (pContext == NULL) {
33086 return MA_INVALID_ARGS;
33087 }
33088
33089 MA_ZERO_OBJECT(pContext);
33090
33091 /* Always make sure the config is set first to ensure properties are available as soon as possible. */
33092 if (pConfig == NULL) {
33093 defaultConfig = ma_context_config_init();
33094 pConfig = &defaultConfig;
33095 }
33096
33097 /* Allocation callbacks need to come first because they'll be passed around to other areas. */
33098 result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks);
33099 if (result != MA_SUCCESS) {
33100 return result;
33101 }
33102
33103 /* Get a lot set up first so we can start logging ASAP. */
33104 if (pConfig->pLog != NULL) {
33105 pContext->pLog = pConfig->pLog;
33106 } else {
33107 result = ma_log_init(&pContext->allocationCallbacks, &pContext->log);
33108 if (result == MA_SUCCESS) {
33109 pContext->pLog = &pContext->log;
33110 } else {
33111 pContext->pLog = NULL; /* Logging is not available. */
33112 }
33113 }
33114
33115 pContext->logCallback = pConfig->logCallback;
33116 pContext->threadPriority = pConfig->threadPriority;
33117 pContext->threadStackSize = pConfig->threadStackSize;
33118 pContext->pUserData = pConfig->pUserData;
33119
33120 /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */
33121 result = ma_context_init_backend_apis(pContext);
33122 if (result != MA_SUCCESS) {
33123 return result;
33124 }
33125
33126 for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
33127 defaultBackends[iBackend] = (ma_backend)iBackend;
33128 }
33129
33130 pBackendsToIterate = (ma_backend*)backends;
33131 backendsToIterateCount = backendCount;
33132 if (pBackendsToIterate == NULL) {
33133 pBackendsToIterate = (ma_backend*)defaultBackends;
33134 backendsToIterateCount = ma_countof(defaultBackends);
33135 }
33136
33137 MA_ASSERT(pBackendsToIterate != NULL);
33138
33139 for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) {
33140 ma_backend backend = pBackendsToIterate[iBackend];
33141
33142 /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */
33143 MA_ZERO_OBJECT(&pContext->callbacks);
33144
33145 /* These backends are using the new callback system. */
33146 switch (backend) {
33147 #ifdef MA_HAS_WASAPI
33148 case ma_backend_wasapi:
33149 {
33150 pContext->callbacks.onContextInit = ma_context_init__wasapi;
33151 } break;
33152 #endif
33153 #ifdef MA_HAS_DSOUND
33154 case ma_backend_dsound:
33155 {
33156 pContext->callbacks.onContextInit = ma_context_init__dsound;
33157 } break;
33158 #endif
33159 #ifdef MA_HAS_WINMM
33160 case ma_backend_winmm:
33161 {
33162 pContext->callbacks.onContextInit = ma_context_init__winmm;
33163 } break;
33164 #endif
33165 #ifdef MA_HAS_COREAUDIO
33166 case ma_backend_coreaudio:
33167 {
33168 pContext->callbacks.onContextInit = ma_context_init__coreaudio;
33169 } break;
33170 #endif
33171 #ifdef MA_HAS_SNDIO
33172 case ma_backend_sndio:
33173 {
33174 pContext->callbacks.onContextInit = ma_context_init__sndio;
33175 } break;
33176 #endif
33177 #ifdef MA_HAS_AUDIO4
33178 case ma_backend_audio4:
33179 {
33180 pContext->callbacks.onContextInit = ma_context_init__audio4;
33181 } break;
33182 #endif
33183 #ifdef MA_HAS_OSS
33184 case ma_backend_oss:
33185 {
33186 pContext->callbacks.onContextInit = ma_context_init__oss;
33187 } break;
33188 #endif
33189 #ifdef MA_HAS_PULSEAUDIO
33190 case ma_backend_pulseaudio:
33191 {
33192 pContext->callbacks.onContextInit = ma_context_init__pulse;
33193 } break;
33194 #endif
33195 #ifdef MA_HAS_ALSA
33196 case ma_backend_alsa:
33197 {
33198 pContext->callbacks.onContextInit = ma_context_init__alsa;
33199 } break;
33200 #endif
33201 #ifdef MA_HAS_JACK
33202 case ma_backend_jack:
33203 {
33204 pContext->callbacks.onContextInit = ma_context_init__jack;
33205 } break;
33206 #endif
33207 #ifdef MA_HAS_AAUDIO
33208 case ma_backend_aaudio:
33209 {
33210 pContext->callbacks.onContextInit = ma_context_init__aaudio;
33211 } break;
33212 #endif
33213 #ifdef MA_HAS_OPENSL
33214 case ma_backend_opensl:
33215 {
33216 pContext->callbacks.onContextInit = ma_context_init__opensl;
33217 } break;
33218 #endif
33219 #ifdef MA_HAS_WEBAUDIO
33220 case ma_backend_webaudio:
33221 {
33222 pContext->callbacks.onContextInit = ma_context_init__webaudio;
33223 } break;
33224 #endif
33225 #ifdef MA_HAS_CUSTOM
33226 case ma_backend_custom:
33227 {
33228 /* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */
33229 pContext->callbacks = pConfig->custom;
33230 } break;
33231 #endif
33232 #ifdef MA_HAS_NULL
33233 case ma_backend_null:
33234 {
33235 pContext->callbacks.onContextInit = ma_context_init__null;
33236 } break;
33237 #endif
33238
33239 default: break;
33240 }
33241
33242 if (pContext->callbacks.onContextInit != NULL) {
33243 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend));
33244 result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks);
33245 } else {
33246 result = MA_NO_BACKEND;
33247 }
33248
33249 /* If this iteration was successful, return. */
33250 if (result == MA_SUCCESS) {
33251 result = ma_mutex_init(&pContext->deviceEnumLock);
33252 if (result != MA_SUCCESS) {
33253 ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\n", result);
33254 }
33255
33256 result = ma_mutex_init(&pContext->deviceInfoLock);
33257 if (result != MA_SUCCESS) {
33258 ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\n", result);
33259 }
33260
33261 #ifdef MA_DEBUG_OUTPUT
33262 {
33263 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[miniaudio] Endian: %s\n", ma_is_little_endian() ? "LE" : "BE");
33264 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[miniaudio] SSE2: %s\n", ma_has_sse2() ? "YES" : "NO");
33265 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[miniaudio] AVX2: %s\n", ma_has_avx2() ? "YES" : "NO");
33266 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[miniaudio] AVX512F: %s\n", ma_has_avx512f() ? "YES" : "NO");
33267 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[miniaudio] NEON: %s\n", ma_has_neon() ? "YES" : "NO");
33268 }
33269 #endif
33270
33271 pContext->backend = backend;
33272 return result;
33273 } else {
33274 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend));
33275 }
33276 }
33277
33278 /* If we get here it means an error occurred. */
33279 MA_ZERO_OBJECT(pContext); /* Safety. */
33280 return MA_NO_BACKEND;
33281}
33282
33283MA_API ma_result ma_context_uninit(ma_context* pContext)
33284{
33285 if (pContext == NULL) {
33286 return MA_INVALID_ARGS;
33287 }
33288
33289 if (pContext->callbacks.onContextUninit != NULL) {
33290 pContext->callbacks.onContextUninit(pContext);
33291 }
33292
33293 ma_mutex_uninit(&pContext->deviceEnumLock);
33294 ma_mutex_uninit(&pContext->deviceInfoLock);
33295 ma__free_from_callbacks(pContext->pDeviceInfos, &pContext->allocationCallbacks);
33296 ma_context_uninit_backend_apis(pContext);
33297
33298 if (pContext->pLog == &pContext->log) {
33299 ma_log_uninit(&pContext->log);
33300 }
33301
33302 return MA_SUCCESS;
33303}
33304
33305MA_API size_t ma_context_sizeof()
33306{
33307 return sizeof(ma_context);
33308}
33309
33310
33311MA_API ma_log* ma_context_get_log(ma_context* pContext)
33312{
33313 if (pContext == NULL) {
33314 return NULL;
33315 }
33316
33317 return pContext->pLog;
33318}
33319
33320
33321MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
33322{
33323 ma_result result;
33324
33325 if (pContext == NULL || callback == NULL) {
33326 return MA_INVALID_ARGS;
33327 }
33328
33329 if (pContext->callbacks.onContextEnumerateDevices == NULL) {
33330 return MA_INVALID_OPERATION;
33331 }
33332
33333 ma_mutex_lock(&pContext->deviceEnumLock);
33334 {
33335 result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData);
33336 }
33337 ma_mutex_unlock(&pContext->deviceEnumLock);
33338
33339 return result;
33340}
33341
33342
33343static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
33344{
33345 /*
33346 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
33347 it's just appended to the end. If it's a playback device it's inserted just before the first capture device.
33348 */
33349
33350 /*
33351 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
33352 simple fixed size increment for buffer expansion.
33353 */
33354 const ma_uint32 bufferExpansionCount = 2;
33355 const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount;
33356
33357 if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) {
33358 ma_uint32 oldCapacity = pContext->deviceInfoCapacity;
33359 ma_uint32 newCapacity = oldCapacity + bufferExpansionCount;
33360 ma_device_info* pNewInfos = (ma_device_info*)ma__realloc_from_callbacks(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, sizeof(*pContext->pDeviceInfos)*oldCapacity, &pContext->allocationCallbacks);
33361 if (pNewInfos == NULL) {
33362 return MA_FALSE; /* Out of memory. */
33363 }
33364
33365 pContext->pDeviceInfos = pNewInfos;
33366 pContext->deviceInfoCapacity = newCapacity;
33367 }
33368
33369 if (deviceType == ma_device_type_playback) {
33370 /* Playback. Insert just before the first capture device. */
33371
33372 /* The first thing to do is move all of the capture devices down a slot. */
33373 ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount;
33374 size_t iCaptureDevice;
33375 for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) {
33376 pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1];
33377 }
33378
33379 /* Now just insert where the first capture device was before moving it down a slot. */
33380 pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo;
33381 pContext->playbackDeviceInfoCount += 1;
33382 } else {
33383 /* Capture. Insert at the end. */
33384 pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo;
33385 pContext->captureDeviceInfoCount += 1;
33386 }
33387
33388 (void)pUserData;
33389 return MA_TRUE;
33390}
33391
33392MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount)
33393{
33394 ma_result result;
33395
33396 /* Safety. */
33397 if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL;
33398 if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0;
33399 if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL;
33400 if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0;
33401
33402 if (pContext == NULL) {
33403 return MA_INVALID_ARGS;
33404 }
33405
33406 if (pContext->callbacks.onContextEnumerateDevices == NULL) {
33407 return MA_INVALID_OPERATION;
33408 }
33409
33410 /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */
33411 ma_mutex_lock(&pContext->deviceEnumLock);
33412 {
33413 /* Reset everything first. */
33414 pContext->playbackDeviceInfoCount = 0;
33415 pContext->captureDeviceInfoCount = 0;
33416
33417 /* Now enumerate over available devices. */
33418 result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL);
33419 if (result == MA_SUCCESS) {
33420 /* Playback devices. */
33421 if (ppPlaybackDeviceInfos != NULL) {
33422 *ppPlaybackDeviceInfos = pContext->pDeviceInfos;
33423 }
33424 if (pPlaybackDeviceCount != NULL) {
33425 *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount;
33426 }
33427
33428 /* Capture devices. */
33429 if (ppCaptureDeviceInfos != NULL) {
33430 *ppCaptureDeviceInfos = pContext->pDeviceInfos + pContext->playbackDeviceInfoCount; /* Capture devices come after playback devices. */
33431 }
33432 if (pCaptureDeviceCount != NULL) {
33433 *pCaptureDeviceCount = pContext->captureDeviceInfoCount;
33434 }
33435 }
33436 }
33437 ma_mutex_unlock(&pContext->deviceEnumLock);
33438
33439 return result;
33440}
33441
33442MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
33443{
33444 ma_result result;
33445 ma_device_info deviceInfo;
33446
33447 (void)shareMode; /* Unused. This parameter will be removed in version 0.11. */
33448
33449 /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */
33450 if (pContext == NULL || pDeviceInfo == NULL) {
33451 return MA_INVALID_ARGS;
33452 }
33453
33454 MA_ZERO_OBJECT(&deviceInfo);
33455
33456 /* Help the backend out by copying over the device ID if we have one. */
33457 if (pDeviceID != NULL) {
33458 MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID));
33459 }
33460
33461 if (pContext->callbacks.onContextGetDeviceInfo == NULL) {
33462 return MA_INVALID_OPERATION;
33463 }
33464
33465 ma_mutex_lock(&pContext->deviceInfoLock);
33466 {
33467 result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo);
33468 }
33469 ma_mutex_unlock(&pContext->deviceInfoLock);
33470
33471 /*
33472 If the backend is using the new device info system, do a pass to fill out the old settings for backwards compatibility. This will be removed in
33473 the future when all backends have implemented the new device info system.
33474 */
33475 if (deviceInfo.nativeDataFormatCount > 0) {
33476 ma_uint32 iNativeFormat;
33477 ma_uint32 iSampleFormat;
33478
33479 deviceInfo.minChannels = 0xFFFFFFFF;
33480 deviceInfo.maxChannels = 0;
33481 deviceInfo.minSampleRate = 0xFFFFFFFF;
33482 deviceInfo.maxSampleRate = 0;
33483
33484 for (iNativeFormat = 0; iNativeFormat < deviceInfo.nativeDataFormatCount; iNativeFormat += 1) {
33485 /* Formats. */
33486 if (deviceInfo.nativeDataFormats[iNativeFormat].format == ma_format_unknown) {
33487 /* All formats are supported. */
33488 deviceInfo.formats[0] = ma_format_u8;
33489 deviceInfo.formats[1] = ma_format_s16;
33490 deviceInfo.formats[2] = ma_format_s24;
33491 deviceInfo.formats[3] = ma_format_s32;
33492 deviceInfo.formats[4] = ma_format_f32;
33493 deviceInfo.formatCount = 5;
33494 } else {
33495 /* Make sure the format isn't already in the list. If so, skip. */
33496 ma_bool32 alreadyExists = MA_FALSE;
33497 for (iSampleFormat = 0; iSampleFormat < deviceInfo.formatCount; iSampleFormat += 1) {
33498 if (deviceInfo.formats[iSampleFormat] == deviceInfo.nativeDataFormats[iNativeFormat].format) {
33499 alreadyExists = MA_TRUE;
33500 break;
33501 }
33502 }
33503
33504 if (!alreadyExists) {
33505 deviceInfo.formats[deviceInfo.formatCount++] = deviceInfo.nativeDataFormats[iNativeFormat].format;
33506 }
33507 }
33508
33509 /* Channels. */
33510 if (deviceInfo.nativeDataFormats[iNativeFormat].channels == 0) {
33511 /* All channels supported. */
33512 deviceInfo.minChannels = MA_MIN_CHANNELS;
33513 deviceInfo.maxChannels = MA_MAX_CHANNELS;
33514 } else {
33515 if (deviceInfo.minChannels > deviceInfo.nativeDataFormats[iNativeFormat].channels) {
33516 deviceInfo.minChannels = deviceInfo.nativeDataFormats[iNativeFormat].channels;
33517 }
33518 if (deviceInfo.maxChannels < deviceInfo.nativeDataFormats[iNativeFormat].channels) {
33519 deviceInfo.maxChannels = deviceInfo.nativeDataFormats[iNativeFormat].channels;
33520 }
33521 }
33522
33523 /* Sample rate. */
33524 if (deviceInfo.nativeDataFormats[iNativeFormat].sampleRate == 0) {
33525 /* All sample rates supported. */
33526 deviceInfo.minSampleRate = (ma_uint32)ma_standard_sample_rate_min;
33527 deviceInfo.maxSampleRate = (ma_uint32)ma_standard_sample_rate_max;
33528 } else {
33529 if (deviceInfo.minSampleRate > deviceInfo.nativeDataFormats[iNativeFormat].sampleRate) {
33530 deviceInfo.minSampleRate = deviceInfo.nativeDataFormats[iNativeFormat].sampleRate;
33531 }
33532 if (deviceInfo.maxSampleRate < deviceInfo.nativeDataFormats[iNativeFormat].sampleRate) {
33533 deviceInfo.maxSampleRate = deviceInfo.nativeDataFormats[iNativeFormat].sampleRate;
33534 }
33535 }
33536 }
33537 }
33538
33539
33540 /* Clamp ranges. */
33541 deviceInfo.minChannels = ma_max(deviceInfo.minChannels, MA_MIN_CHANNELS);
33542 deviceInfo.maxChannels = ma_min(deviceInfo.maxChannels, MA_MAX_CHANNELS);
33543 deviceInfo.minSampleRate = ma_max(deviceInfo.minSampleRate, (ma_uint32)ma_standard_sample_rate_min);
33544 deviceInfo.maxSampleRate = ma_min(deviceInfo.maxSampleRate, (ma_uint32)ma_standard_sample_rate_max);
33545
33546 *pDeviceInfo = deviceInfo;
33547 return result;
33548}
33549
33550MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext)
33551{
33552 if (pContext == NULL) {
33553 return MA_FALSE;
33554 }
33555
33556 return ma_is_loopback_supported(pContext->backend);
33557}
33558
33559
33560MA_API ma_device_config ma_device_config_init(ma_device_type deviceType)
33561{
33562 ma_device_config config;
33563 MA_ZERO_OBJECT(&config);
33564 config.deviceType = deviceType;
33565
33566 /* Resampling defaults. We must never use the Speex backend by default because it uses licensed third party code. */
33567 config.resampling.algorithm = ma_resample_algorithm_linear;
33568 config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
33569 config.resampling.speex.quality = 3;
33570
33571 return config;
33572}
33573
33574MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
33575{
33576 ma_result result;
33577 ma_device_descriptor descriptorPlayback;
33578 ma_device_descriptor descriptorCapture;
33579
33580 /* The context can be null, in which case we self-manage it. */
33581 if (pContext == NULL) {
33582 return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice);
33583 }
33584
33585 if (pDevice == NULL) {
33586 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
33587 }
33588
33589 MA_ZERO_OBJECT(pDevice);
33590
33591 if (pConfig == NULL) {
33592 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pConfig == NULL).", MA_INVALID_ARGS);
33593 }
33594
33595
33596 /* Check that we have our callbacks defined. */
33597 if (pContext->callbacks.onDeviceInit == NULL) {
33598 return MA_INVALID_OPERATION;
33599 }
33600
33601
33602 /* Basic config validation. */
33603 if (pConfig->deviceType != ma_device_type_playback && pConfig->deviceType != ma_device_type_capture && pConfig->deviceType != ma_device_type_duplex && pConfig->deviceType != ma_device_type_loopback) {
33604 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with an invalid config. Device type is invalid. Make sure the device type has been set in the config.", MA_INVALID_DEVICE_CONFIG);
33605 }
33606
33607 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
33608 if (pConfig->capture.channels > MA_MAX_CHANNELS) {
33609 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with an invalid config. Capture channel count cannot exceed 32.", MA_INVALID_DEVICE_CONFIG);
33610 }
33611 if (!ma__is_channel_map_valid(pConfig->capture.channelMap, pConfig->capture.channels)) {
33612 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid config. Capture channel map is invalid.", MA_INVALID_DEVICE_CONFIG);
33613 }
33614 }
33615
33616 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
33617 if (pConfig->playback.channels > MA_MAX_CHANNELS) {
33618 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with an invalid config. Playback channel count cannot exceed 32.", MA_INVALID_DEVICE_CONFIG);
33619 }
33620 if (!ma__is_channel_map_valid(pConfig->playback.channelMap, pConfig->playback.channels)) {
33621 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid config. Playback channel map is invalid.", MA_INVALID_DEVICE_CONFIG);
33622 }
33623 }
33624
33625 pDevice->pContext = pContext;
33626
33627 /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */
33628 pDevice->pUserData = pConfig->pUserData;
33629 pDevice->onData = pConfig->dataCallback;
33630 pDevice->onStop = pConfig->stopCallback;
33631
33632 if (((ma_uintptr)pDevice % sizeof(pDevice)) != 0) {
33633 if (pContext->logCallback) {
33634 pContext->logCallback(pContext, pDevice, MA_LOG_LEVEL_WARNING, "WARNING: ma_device_init() called for a device that is not properly aligned. Thread safety is not supported.");
33635 }
33636 }
33637
33638 if (pConfig->playback.pDeviceID != NULL) {
33639 MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id));
33640 }
33641
33642 if (pConfig->capture.pDeviceID != NULL) {
33643 MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id));
33644 }
33645
33646 pDevice->noPreZeroedOutputBuffer = pConfig->noPreZeroedOutputBuffer;
33647 pDevice->noClip = pConfig->noClip;
33648 pDevice->masterVolumeFactor = 1;
33649
33650 pDevice->type = pConfig->deviceType;
33651 pDevice->sampleRate = pConfig->sampleRate;
33652 pDevice->resampling.algorithm = pConfig->resampling.algorithm;
33653 pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder;
33654 pDevice->resampling.speex.quality = pConfig->resampling.speex.quality;
33655
33656 pDevice->capture.shareMode = pConfig->capture.shareMode;
33657 pDevice->capture.format = pConfig->capture.format;
33658 pDevice->capture.channels = pConfig->capture.channels;
33659 ma_channel_map_copy(pDevice->capture.channelMap, pConfig->capture.channelMap, pConfig->capture.channels);
33660 pDevice->capture.channelMixMode = pConfig->capture.channelMixMode;
33661
33662 pDevice->playback.shareMode = pConfig->playback.shareMode;
33663 pDevice->playback.format = pConfig->playback.format;
33664 pDevice->playback.channels = pConfig->playback.channels;
33665 ma_channel_map_copy(pDevice->playback.channelMap, pConfig->playback.channelMap, pConfig->playback.channels);
33666 pDevice->playback.channelMixMode = pConfig->playback.channelMixMode;
33667
33668
33669 result = ma_mutex_init(&pDevice->startStopLock);
33670 if (result != MA_SUCCESS) {
33671 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create mutex.", result);
33672 }
33673
33674 /*
33675 When the device is started, the worker thread is the one that does the actual startup of the backend device. We
33676 use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device.
33677
33678 Each of these semaphores is released internally by the worker thread when the work is completed. The start
33679 semaphore is also used to wake up the worker thread.
33680 */
33681 result = ma_event_init(&pDevice->wakeupEvent);
33682 if (result != MA_SUCCESS) {
33683 ma_mutex_uninit(&pDevice->startStopLock);
33684 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread wakeup event.", result);
33685 }
33686
33687 result = ma_event_init(&pDevice->startEvent);
33688 if (result != MA_SUCCESS) {
33689 ma_event_uninit(&pDevice->wakeupEvent);
33690 ma_mutex_uninit(&pDevice->startStopLock);
33691 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread start event.", result);
33692 }
33693
33694 result = ma_event_init(&pDevice->stopEvent);
33695 if (result != MA_SUCCESS) {
33696 ma_event_uninit(&pDevice->startEvent);
33697 ma_event_uninit(&pDevice->wakeupEvent);
33698 ma_mutex_uninit(&pDevice->startStopLock);
33699 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread stop event.", result);
33700 }
33701
33702
33703 MA_ZERO_OBJECT(&descriptorPlayback);
33704 descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID;
33705 descriptorPlayback.shareMode = pConfig->playback.shareMode;
33706 descriptorPlayback.format = pConfig->playback.format;
33707 descriptorPlayback.channels = pConfig->playback.channels;
33708 descriptorPlayback.sampleRate = pConfig->sampleRate;
33709 ma_channel_map_copy(descriptorPlayback.channelMap, pConfig->playback.channelMap, pConfig->playback.channels);
33710 descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames;
33711 descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
33712 descriptorPlayback.periodCount = pConfig->periods;
33713
33714 if (descriptorPlayback.periodCount == 0) {
33715 descriptorPlayback.periodCount = MA_DEFAULT_PERIODS;
33716 }
33717
33718
33719 MA_ZERO_OBJECT(&descriptorCapture);
33720 descriptorCapture.pDeviceID = pConfig->capture.pDeviceID;
33721 descriptorCapture.shareMode = pConfig->capture.shareMode;
33722 descriptorCapture.format = pConfig->capture.format;
33723 descriptorCapture.channels = pConfig->capture.channels;
33724 descriptorCapture.sampleRate = pConfig->sampleRate;
33725 ma_channel_map_copy(descriptorCapture.channelMap, pConfig->capture.channelMap, pConfig->capture.channels);
33726 descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames;
33727 descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
33728 descriptorCapture.periodCount = pConfig->periods;
33729
33730 if (descriptorCapture.periodCount == 0) {
33731 descriptorCapture.periodCount = MA_DEFAULT_PERIODS;
33732 }
33733
33734
33735 result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture);
33736 if (result != MA_SUCCESS) {
33737 ma_event_uninit(&pDevice->startEvent);
33738 ma_event_uninit(&pDevice->wakeupEvent);
33739 ma_mutex_uninit(&pDevice->startStopLock);
33740 return result;
33741 }
33742
33743
33744 /*
33745 On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between
33746 the requested format and the internal format.
33747 */
33748 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
33749 if (!ma_device_descriptor_is_valid(&descriptorCapture)) {
33750 ma_device_uninit(pDevice);
33751 return MA_INVALID_ARGS;
33752 }
33753
33754 pDevice->capture.internalFormat = descriptorCapture.format;
33755 pDevice->capture.internalChannels = descriptorCapture.channels;
33756 pDevice->capture.internalSampleRate = descriptorCapture.sampleRate;
33757 ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels);
33758 pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames;
33759 pDevice->capture.internalPeriods = descriptorCapture.periodCount;
33760
33761 if (pDevice->capture.internalPeriodSizeInFrames == 0) {
33762 pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate);
33763 }
33764 }
33765
33766 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
33767 if (!ma_device_descriptor_is_valid(&descriptorPlayback)) {
33768 ma_device_uninit(pDevice);
33769 return MA_INVALID_ARGS;
33770 }
33771
33772 pDevice->playback.internalFormat = descriptorPlayback.format;
33773 pDevice->playback.internalChannels = descriptorPlayback.channels;
33774 pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate;
33775 ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels);
33776 pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames;
33777 pDevice->playback.internalPeriods = descriptorPlayback.periodCount;
33778
33779 if (pDevice->playback.internalPeriodSizeInFrames == 0) {
33780 pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate);
33781 }
33782 }
33783
33784
33785 /*
33786 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.
33787 For loopback devices, we need to retrieve the name of the playback device.
33788 */
33789 {
33790 ma_device_info deviceInfo;
33791
33792 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
33793 result = ma_context_get_device_info(pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, descriptorCapture.pDeviceID, descriptorCapture.shareMode, &deviceInfo);
33794 if (result == MA_SUCCESS) {
33795 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);
33796 } else {
33797 /* We failed to retrieve the device info. Fall back to a default name. */
33798 if (descriptorCapture.pDeviceID == NULL) {
33799 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
33800 } else {
33801 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1);
33802 }
33803 }
33804 }
33805
33806 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
33807 result = ma_context_get_device_info(pContext, ma_device_type_playback, descriptorPlayback.pDeviceID, descriptorPlayback.shareMode, &deviceInfo);
33808 if (result == MA_SUCCESS) {
33809 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1);
33810 } else {
33811 /* We failed to retrieve the device info. Fall back to a default name. */
33812 if (descriptorPlayback.pDeviceID == NULL) {
33813 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
33814 } else {
33815 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1);
33816 }
33817 }
33818 }
33819 }
33820
33821
33822 ma_device__post_init_setup(pDevice, pConfig->deviceType);
33823
33824
33825 /* Some backends don't require the worker thread. */
33826 if (!ma_context_is_backend_asynchronous(pContext)) {
33827 /* The worker thread. */
33828 result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks);
33829 if (result != MA_SUCCESS) {
33830 ma_device_uninit(pDevice);
33831 return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread.", result);
33832 }
33833
33834 /* Wait for the worker thread to put the device into it's stopped state for real. */
33835 ma_event_wait(&pDevice->stopEvent);
33836 MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STOPPED);
33837 } else {
33838 /*
33839 If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done
33840 after ma_device__post_init_setup().
33841 */
33842 if (ma_context_is_backend_asynchronous(pContext)) {
33843 if (pConfig->deviceType == ma_device_type_duplex) {
33844 result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);
33845 if (result != MA_SUCCESS) {
33846 ma_device_uninit(pDevice);
33847 return result;
33848 }
33849 }
33850 }
33851
33852 ma_device__set_state(pDevice, MA_STATE_STOPPED);
33853 }
33854
33855
33856 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend));
33857 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
33858 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", pDevice->capture.name, "Capture");
33859 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format));
33860 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels);
33861 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate);
33862 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods));
33863 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n");
33864 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO");
33865 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO");
33866 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO");
33867 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO");
33868 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO");
33869 }
33870 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
33871 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", pDevice->playback.name, "Playback");
33872 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat));
33873 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels);
33874 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate);
33875 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods));
33876 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n");
33877 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO");
33878 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO");
33879 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO");
33880 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO");
33881 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO");
33882 }
33883
33884 MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STOPPED);
33885 return MA_SUCCESS;
33886}
33887
33888MA_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)
33889{
33890 ma_result result;
33891 ma_context* pContext;
33892 ma_backend defaultBackends[ma_backend_null+1];
33893 ma_uint32 iBackend;
33894 ma_backend* pBackendsToIterate;
33895 ma_uint32 backendsToIterateCount;
33896 ma_allocation_callbacks allocationCallbacks;
33897
33898 if (pConfig == NULL) {
33899 return MA_INVALID_ARGS;
33900 }
33901
33902 if (pContextConfig != NULL) {
33903 result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks);
33904 if (result != MA_SUCCESS) {
33905 return result;
33906 }
33907 } else {
33908 allocationCallbacks = ma_allocation_callbacks_init_default();
33909 }
33910
33911
33912 pContext = (ma_context*)ma__malloc_from_callbacks(sizeof(*pContext), &allocationCallbacks);
33913 if (pContext == NULL) {
33914 return MA_OUT_OF_MEMORY;
33915 }
33916
33917 for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
33918 defaultBackends[iBackend] = (ma_backend)iBackend;
33919 }
33920
33921 pBackendsToIterate = (ma_backend*)backends;
33922 backendsToIterateCount = backendCount;
33923 if (pBackendsToIterate == NULL) {
33924 pBackendsToIterate = (ma_backend*)defaultBackends;
33925 backendsToIterateCount = ma_countof(defaultBackends);
33926 }
33927
33928 result = MA_NO_BACKEND;
33929
33930 for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
33931 result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext);
33932 if (result == MA_SUCCESS) {
33933 result = ma_device_init(pContext, pConfig, pDevice);
33934 if (result == MA_SUCCESS) {
33935 break; /* Success. */
33936 } else {
33937 ma_context_uninit(pContext); /* Failure. */
33938 }
33939 }
33940 }
33941
33942 if (result != MA_SUCCESS) {
33943 ma__free_from_callbacks(pContext, &allocationCallbacks);
33944 return result;
33945 }
33946
33947 pDevice->isOwnerOfContext = MA_TRUE;
33948 return result;
33949}
33950
33951MA_API void ma_device_uninit(ma_device* pDevice)
33952{
33953 if (!ma_device__is_initialized(pDevice)) {
33954 return;
33955 }
33956
33957 /* 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. */
33958 if (ma_device_is_started(pDevice)) {
33959 ma_device_stop(pDevice);
33960 }
33961
33962 /* Putting the device into an uninitialized state will make the worker thread return. */
33963 ma_device__set_state(pDevice, MA_STATE_UNINITIALIZED);
33964
33965 /* Wake up the worker thread and wait for it to properly terminate. */
33966 if (!ma_context_is_backend_asynchronous(pDevice->pContext)) {
33967 ma_event_signal(&pDevice->wakeupEvent);
33968 ma_thread_wait(&pDevice->thread);
33969 }
33970
33971 if (pDevice->pContext->callbacks.onDeviceUninit != NULL) {
33972 pDevice->pContext->callbacks.onDeviceUninit(pDevice);
33973 }
33974
33975
33976 ma_event_uninit(&pDevice->stopEvent);
33977 ma_event_uninit(&pDevice->startEvent);
33978 ma_event_uninit(&pDevice->wakeupEvent);
33979 ma_mutex_uninit(&pDevice->startStopLock);
33980
33981 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
33982 if (pDevice->type == ma_device_type_duplex) {
33983 ma_duplex_rb_uninit(&pDevice->duplexRB);
33984 }
33985 }
33986
33987 if (pDevice->isOwnerOfContext) {
33988 ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks;
33989
33990 ma_context_uninit(pDevice->pContext);
33991 ma__free_from_callbacks(pDevice->pContext, &allocationCallbacks);
33992 }
33993
33994 MA_ZERO_OBJECT(pDevice);
33995}
33996
33997MA_API ma_context* ma_device_get_context(ma_device* pDevice)
33998{
33999 if (pDevice == NULL) {
34000 return NULL;
34001 }
34002
34003 return pDevice->pContext;
34004}
34005
34006MA_API ma_log* ma_device_get_log(ma_device* pDevice)
34007{
34008 return ma_context_get_log(ma_device_get_context(pDevice));
34009}
34010
34011MA_API ma_result ma_device_start(ma_device* pDevice)
34012{
34013 ma_result result;
34014
34015 if (pDevice == NULL) {
34016 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
34017 }
34018
34019 if (ma_device_get_state(pDevice) == MA_STATE_UNINITIALIZED) {
34020 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called for an uninitialized device.", MA_DEVICE_NOT_INITIALIZED);
34021 }
34022
34023 if (ma_device_get_state(pDevice) == MA_STATE_STARTED) {
34024 return ma_post_error(pDevice, MA_LOG_LEVEL_WARNING, "ma_device_start() called when the device is already started.", MA_INVALID_OPERATION); /* Already started. Returning an error to let the application know because it probably means they're doing something wrong. */
34025 }
34026
34027 ma_mutex_lock(&pDevice->startStopLock);
34028 {
34029 /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */
34030 MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STOPPED);
34031
34032 ma_device__set_state(pDevice, MA_STATE_STARTING);
34033
34034 /* Asynchronous backends need to be handled differently. */
34035 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
34036 if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
34037 result = pDevice->pContext->callbacks.onDeviceStart(pDevice);
34038 } else {
34039 result = MA_INVALID_OPERATION;
34040 }
34041
34042 if (result == MA_SUCCESS) {
34043 ma_device__set_state(pDevice, MA_STATE_STARTED);
34044 }
34045 } else {
34046 /*
34047 Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the
34048 thread and then wait for the start event.
34049 */
34050 ma_event_signal(&pDevice->wakeupEvent);
34051
34052 /*
34053 Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device
34054 into the started state. Don't call ma_device__set_state() here.
34055 */
34056 ma_event_wait(&pDevice->startEvent);
34057 result = pDevice->workResult;
34058 }
34059
34060 /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */
34061 if (result != MA_SUCCESS) {
34062 ma_device__set_state(pDevice, MA_STATE_STOPPED);
34063 }
34064 }
34065 ma_mutex_unlock(&pDevice->startStopLock);
34066
34067 return result;
34068}
34069
34070MA_API ma_result ma_device_stop(ma_device* pDevice)
34071{
34072 ma_result result;
34073
34074 if (pDevice == NULL) {
34075 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_stop() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
34076 }
34077
34078 if (ma_device_get_state(pDevice) == MA_STATE_UNINITIALIZED) {
34079 return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_stop() called for an uninitialized device.", MA_DEVICE_NOT_INITIALIZED);
34080 }
34081
34082 if (ma_device_get_state(pDevice) == MA_STATE_STOPPED) {
34083 return ma_post_error(pDevice, MA_LOG_LEVEL_WARNING, "ma_device_stop() called when the device is already stopped.", MA_INVALID_OPERATION); /* Already stopped. Returning an error to let the application know because it probably means they're doing something wrong. */
34084 }
34085
34086 ma_mutex_lock(&pDevice->startStopLock);
34087 {
34088 /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */
34089 MA_ASSERT(ma_device_get_state(pDevice) == MA_STATE_STARTED);
34090
34091 ma_device__set_state(pDevice, MA_STATE_STOPPING);
34092
34093 /* Asynchronous backends need to be handled differently. */
34094 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
34095 /* Asynchronous backends must have a stop operation. */
34096 if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
34097 result = pDevice->pContext->callbacks.onDeviceStop(pDevice);
34098 } else {
34099 result = MA_INVALID_OPERATION;
34100 }
34101
34102 ma_device__set_state(pDevice, MA_STATE_STOPPED);
34103 } else {
34104 /*
34105 Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If
34106 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
34107 sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super
34108 important though, so I'm asserting it here as well for extra safety in case we accidentally change something later.
34109 */
34110 MA_ASSERT(ma_device_get_state(pDevice) != MA_STATE_STARTED);
34111
34112 if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) {
34113 pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice);
34114 }
34115
34116 /*
34117 We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be
34118 the one who puts the device into the stopped state. Don't call ma_device__set_state() here.
34119 */
34120 ma_event_wait(&pDevice->stopEvent);
34121 result = MA_SUCCESS;
34122 }
34123 }
34124 ma_mutex_unlock(&pDevice->startStopLock);
34125
34126 return result;
34127}
34128
34129MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice)
34130{
34131 return ma_device_get_state(pDevice) == MA_STATE_STARTED;
34132}
34133
34134MA_API ma_uint32 ma_device_get_state(const ma_device* pDevice)
34135{
34136 if (pDevice == NULL) {
34137 return MA_STATE_UNINITIALIZED;
34138 }
34139
34140 return c89atomic_load_32((ma_uint32*)&pDevice->state); /* Naughty cast to get rid of a const warning. */
34141}
34142
34143MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume)
34144{
34145 if (pDevice == NULL) {
34146 return MA_INVALID_ARGS;
34147 }
34148
34149 if (volume < 0.0f || volume > 1.0f) {
34150 return MA_INVALID_ARGS;
34151 }
34152
34153 c89atomic_exchange_f32(&pDevice->masterVolumeFactor, volume);
34154
34155 return MA_SUCCESS;
34156}
34157
34158MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume)
34159{
34160 if (pVolume == NULL) {
34161 return MA_INVALID_ARGS;
34162 }
34163
34164 if (pDevice == NULL) {
34165 *pVolume = 0;
34166 return MA_INVALID_ARGS;
34167 }
34168
34169 *pVolume = c89atomic_load_f32(&pDevice->masterVolumeFactor);
34170
34171 return MA_SUCCESS;
34172}
34173
34174MA_API ma_result ma_device_set_master_gain_db(ma_device* pDevice, float gainDB)
34175{
34176 if (gainDB > 0) {
34177 return MA_INVALID_ARGS;
34178 }
34179
34180 return ma_device_set_master_volume(pDevice, ma_gain_db_to_factor(gainDB));
34181}
34182
34183MA_API ma_result ma_device_get_master_gain_db(ma_device* pDevice, float* pGainDB)
34184{
34185 float factor;
34186 ma_result result;
34187
34188 if (pGainDB == NULL) {
34189 return MA_INVALID_ARGS;
34190 }
34191
34192 result = ma_device_get_master_volume(pDevice, &factor);
34193 if (result != MA_SUCCESS) {
34194 *pGainDB = 0;
34195 return result;
34196 }
34197
34198 *pGainDB = ma_factor_to_gain_db(factor);
34199
34200 return MA_SUCCESS;
34201}
34202
34203
34204MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
34205{
34206 if (pDevice == NULL) {
34207 return MA_INVALID_ARGS;
34208 }
34209
34210 if (pOutput == NULL && pInput == NULL) {
34211 return MA_INVALID_ARGS;
34212 }
34213
34214 if (pDevice->type == ma_device_type_duplex) {
34215 if (pInput != NULL) {
34216 ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb);
34217 }
34218
34219 if (pOutput != NULL) {
34220 ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb);
34221 }
34222 } else {
34223 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) {
34224 if (pInput == NULL) {
34225 return MA_INVALID_ARGS;
34226 }
34227
34228 ma_device__send_frames_to_client(pDevice, frameCount, pInput);
34229 }
34230
34231 if (pDevice->type == ma_device_type_playback) {
34232 if (pOutput == NULL) {
34233 return MA_INVALID_ARGS;
34234 }
34235
34236 ma_device__read_frames_from_client(pDevice, frameCount, pOutput);
34237 }
34238 }
34239
34240 return MA_SUCCESS;
34241}
34242
34243MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
34244{
34245 if (pDescriptor == NULL) {
34246 return 0;
34247 }
34248
34249 /*
34250 We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the
34251 time when the size of the buffer needs to be determined. In this case we need to just take a best
34252 guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll
34253 just fall back to MA_DEFAULT_SAMPLE_RATE.
34254 */
34255 if (nativeSampleRate == 0) {
34256 nativeSampleRate = pDescriptor->sampleRate;
34257 }
34258 if (nativeSampleRate == 0) {
34259 nativeSampleRate = MA_DEFAULT_SAMPLE_RATE;
34260 }
34261
34262 MA_ASSERT(nativeSampleRate != 0);
34263
34264 if (pDescriptor->periodSizeInFrames == 0) {
34265 if (pDescriptor->periodSizeInMilliseconds == 0) {
34266 if (performanceProfile == ma_performance_profile_low_latency) {
34267 return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate);
34268 } else {
34269 return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate);
34270 }
34271 } else {
34272 return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
34273 }
34274 } else {
34275 return pDescriptor->periodSizeInFrames;
34276 }
34277}
34278#endif /* MA_NO_DEVICE_IO */
34279
34280
34281MA_API ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale)
34282{
34283 return ma_max(1, (ma_uint32)(baseBufferSize*scale));
34284}
34285
34286MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate)
34287{
34288 /* Prevent a division by zero. */
34289 if (sampleRate == 0) {
34290 return 0;
34291 }
34292
34293 return bufferSizeInFrames*1000 / sampleRate;
34294}
34295
34296MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate)
34297{
34298 /* Prevent a division by zero. */
34299 if (sampleRate == 0) {
34300 return 0;
34301 }
34302
34303 return bufferSizeInMilliseconds*sampleRate / 1000;
34304}
34305
34306MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
34307{
34308 if (dst == src) {
34309 return; /* No-op. */
34310 }
34311
34312 ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels));
34313}
34314
34315MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
34316{
34317 if (format == ma_format_u8) {
34318 ma_uint64 sampleCount = frameCount * channels;
34319 ma_uint64 iSample;
34320 for (iSample = 0; iSample < sampleCount; iSample += 1) {
34321 ((ma_uint8*)p)[iSample] = 128;
34322 }
34323 } else {
34324 ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels));
34325 }
34326}
34327
34328MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
34329{
34330 return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels));
34331}
34332
34333MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
34334{
34335 return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels));
34336}
34337
34338
34339MA_API void ma_clip_samples_f32(float* p, ma_uint64 sampleCount)
34340{
34341 ma_uint32 iSample;
34342
34343 /* TODO: Research a branchless SSE implementation. */
34344 for (iSample = 0; iSample < sampleCount; iSample += 1) {
34345 p[iSample] = ma_clip_f32(p[iSample]);
34346 }
34347}
34348
34349
34350MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor)
34351{
34352 ma_uint64 iSample;
34353
34354 if (pSamplesOut == NULL || pSamplesIn == NULL) {
34355 return;
34356 }
34357
34358 for (iSample = 0; iSample < sampleCount; iSample += 1) {
34359 pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor);
34360 }
34361}
34362
34363MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor)
34364{
34365 ma_uint64 iSample;
34366
34367 if (pSamplesOut == NULL || pSamplesIn == NULL) {
34368 return;
34369 }
34370
34371 for (iSample = 0; iSample < sampleCount; iSample += 1) {
34372 pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor);
34373 }
34374}
34375
34376MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor)
34377{
34378 ma_uint64 iSample;
34379 ma_uint8* pSamplesOut8;
34380 ma_uint8* pSamplesIn8;
34381
34382 if (pSamplesOut == NULL || pSamplesIn == NULL) {
34383 return;
34384 }
34385
34386 pSamplesOut8 = (ma_uint8*)pSamplesOut;
34387 pSamplesIn8 = (ma_uint8*)pSamplesIn;
34388
34389 for (iSample = 0; iSample < sampleCount; iSample += 1) {
34390 ma_int32 sampleS32;
34391
34392 sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24);
34393 sampleS32 = (ma_int32)(sampleS32 * factor);
34394
34395 pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8);
34396 pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16);
34397 pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24);
34398 }
34399}
34400
34401MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor)
34402{
34403 ma_uint64 iSample;
34404
34405 if (pSamplesOut == NULL || pSamplesIn == NULL) {
34406 return;
34407 }
34408
34409 for (iSample = 0; iSample < sampleCount; iSample += 1) {
34410 pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor);
34411 }
34412}
34413
34414MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor)
34415{
34416 ma_uint64 iSample;
34417
34418 if (pSamplesOut == NULL || pSamplesIn == NULL) {
34419 return;
34420 }
34421
34422 for (iSample = 0; iSample < sampleCount; iSample += 1) {
34423 pSamplesOut[iSample] = pSamplesIn[iSample] * factor;
34424 }
34425}
34426
34427MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor)
34428{
34429 ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor);
34430}
34431
34432MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor)
34433{
34434 ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor);
34435}
34436
34437MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor)
34438{
34439 ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor);
34440}
34441
34442MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor)
34443{
34444 ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor);
34445}
34446
34447MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor)
34448{
34449 ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor);
34450}
34451
34452MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
34453{
34454 ma_copy_and_apply_volume_factor_u8(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
34455}
34456
34457MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
34458{
34459 ma_copy_and_apply_volume_factor_s16(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
34460}
34461
34462MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
34463{
34464 ma_copy_and_apply_volume_factor_s24(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
34465}
34466
34467MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
34468{
34469 ma_copy_and_apply_volume_factor_s32(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
34470}
34471
34472MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
34473{
34474 ma_copy_and_apply_volume_factor_f32(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
34475}
34476
34477MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor)
34478{
34479 switch (format)
34480 {
34481 case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pPCMFramesOut, (const ma_uint8*)pPCMFramesIn, frameCount, channels, factor); return;
34482 case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pPCMFramesOut, (const ma_int16*)pPCMFramesIn, frameCount, channels, factor); return;
34483 case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pPCMFramesOut, pPCMFramesIn, frameCount, channels, factor); return;
34484 case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pPCMFramesOut, (const ma_int32*)pPCMFramesIn, frameCount, channels, factor); return;
34485 case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pPCMFramesOut, (const float*)pPCMFramesIn, frameCount, channels, factor); return;
34486 default: return; /* Do nothing. */
34487 }
34488}
34489
34490MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
34491{
34492 ma_copy_and_apply_volume_factor_pcm_frames_u8(pPCMFrames, pPCMFrames, frameCount, channels, factor);
34493}
34494
34495MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
34496{
34497 ma_copy_and_apply_volume_factor_pcm_frames_s16(pPCMFrames, pPCMFrames, frameCount, channels, factor);
34498}
34499
34500MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
34501{
34502 ma_copy_and_apply_volume_factor_pcm_frames_s24(pPCMFrames, pPCMFrames, frameCount, channels, factor);
34503}
34504
34505MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
34506{
34507 ma_copy_and_apply_volume_factor_pcm_frames_s32(pPCMFrames, pPCMFrames, frameCount, channels, factor);
34508}
34509
34510MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
34511{
34512 ma_copy_and_apply_volume_factor_pcm_frames_f32(pPCMFrames, pPCMFrames, frameCount, channels, factor);
34513}
34514
34515MA_API void ma_apply_volume_factor_pcm_frames(void* pPCMFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor)
34516{
34517 ma_copy_and_apply_volume_factor_pcm_frames(pPCMFrames, pPCMFrames, frameCount, format, channels, factor);
34518}
34519
34520
34521MA_API float ma_factor_to_gain_db(float factor)
34522{
34523 return (float)(20*ma_log10f(factor));
34524}
34525
34526MA_API float ma_gain_db_to_factor(float gain)
34527{
34528 return (float)ma_powf(10, gain/20.0f);
34529}
34530
34531
34532/**************************************************************************************************************************************************************
34533
34534Format Conversion
34535
34536**************************************************************************************************************************************************************/
34537
34538static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x)
34539{
34540 return (ma_int16)(x * 32767.0f);
34541}
34542
34543static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x)
34544{
34545 return (ma_int16)((ma_int16)x - 128);
34546}
34547
34548static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x)
34549{
34550 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. */
34551}
34552
34553static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24)
34554{
34555 s24[0] = (ma_uint8)((x & 0x000000FF) >> 0);
34556 s24[1] = (ma_uint8)((x & 0x0000FF00) >> 8);
34557 s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16);
34558}
34559
34560
34561static MA_INLINE ma_uint8 ma_clip_u8(ma_int16 x)
34562{
34563 return (ma_uint8)(ma_clamp(x, -128, 127) + 128);
34564}
34565
34566static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x)
34567{
34568 return (ma_int16)ma_clamp(x, -32768, 32767);
34569}
34570
34571static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x)
34572{
34573 return (ma_int64)ma_clamp(x, -8388608, 8388607);
34574}
34575
34576static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x)
34577{
34578 /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */
34579 ma_int64 clipMin;
34580 ma_int64 clipMax;
34581 clipMin = -((ma_int64)2147483647 + 1);
34582 clipMax = (ma_int64)2147483647;
34583
34584 return (ma_int32)ma_clamp(x, clipMin, clipMax);
34585}
34586
34587
34588/* u8 */
34589MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34590{
34591 (void)ditherMode;
34592 ma_copy_memory_64(dst, src, count * sizeof(ma_uint8));
34593}
34594
34595
34596static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34597{
34598 ma_int16* dst_s16 = (ma_int16*)dst;
34599 const ma_uint8* src_u8 = (const ma_uint8*)src;
34600
34601 ma_uint64 i;
34602 for (i = 0; i < count; i += 1) {
34603 ma_int16 x = src_u8[i];
34604 x = (ma_int16)(x - 128);
34605 x = (ma_int16)(x << 8);
34606 dst_s16[i] = x;
34607 }
34608
34609 (void)ditherMode;
34610}
34611
34612static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34613{
34614 ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
34615}
34616
34617#if defined(MA_SUPPORT_SSE2)
34618static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34619{
34620 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
34621}
34622#endif
34623#if defined(MA_SUPPORT_AVX2)
34624static MA_INLINE void ma_pcm_u8_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34625{
34626 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
34627}
34628#endif
34629#if defined(MA_SUPPORT_NEON)
34630static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34631{
34632 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
34633}
34634#endif
34635
34636MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34637{
34638#ifdef MA_USE_REFERENCE_CONVERSION_APIS
34639 ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
34640#else
34641 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34642 if (ma_has_avx2()) {
34643 ma_pcm_u8_to_s16__avx2(dst, src, count, ditherMode);
34644 } else
34645 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34646 if (ma_has_sse2()) {
34647 ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode);
34648 } else
34649 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34650 if (ma_has_neon()) {
34651 ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode);
34652 } else
34653 #endif
34654 {
34655 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
34656 }
34657#endif
34658}
34659
34660
34661static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34662{
34663 ma_uint8* dst_s24 = (ma_uint8*)dst;
34664 const ma_uint8* src_u8 = (const ma_uint8*)src;
34665
34666 ma_uint64 i;
34667 for (i = 0; i < count; i += 1) {
34668 ma_int16 x = src_u8[i];
34669 x = (ma_int16)(x - 128);
34670
34671 dst_s24[i*3+0] = 0;
34672 dst_s24[i*3+1] = 0;
34673 dst_s24[i*3+2] = (ma_uint8)((ma_int8)x);
34674 }
34675
34676 (void)ditherMode;
34677}
34678
34679static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34680{
34681 ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
34682}
34683
34684#if defined(MA_SUPPORT_SSE2)
34685static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34686{
34687 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
34688}
34689#endif
34690#if defined(MA_SUPPORT_AVX2)
34691static MA_INLINE void ma_pcm_u8_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34692{
34693 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
34694}
34695#endif
34696#if defined(MA_SUPPORT_NEON)
34697static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34698{
34699 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
34700}
34701#endif
34702
34703MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34704{
34705#ifdef MA_USE_REFERENCE_CONVERSION_APIS
34706 ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
34707#else
34708 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34709 if (ma_has_avx2()) {
34710 ma_pcm_u8_to_s24__avx2(dst, src, count, ditherMode);
34711 } else
34712 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34713 if (ma_has_sse2()) {
34714 ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode);
34715 } else
34716 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34717 if (ma_has_neon()) {
34718 ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode);
34719 } else
34720 #endif
34721 {
34722 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
34723 }
34724#endif
34725}
34726
34727
34728static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34729{
34730 ma_int32* dst_s32 = (ma_int32*)dst;
34731 const ma_uint8* src_u8 = (const ma_uint8*)src;
34732
34733 ma_uint64 i;
34734 for (i = 0; i < count; i += 1) {
34735 ma_int32 x = src_u8[i];
34736 x = x - 128;
34737 x = x << 24;
34738 dst_s32[i] = x;
34739 }
34740
34741 (void)ditherMode;
34742}
34743
34744static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34745{
34746 ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
34747}
34748
34749#if defined(MA_SUPPORT_SSE2)
34750static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34751{
34752 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
34753}
34754#endif
34755#if defined(MA_SUPPORT_AVX2)
34756static MA_INLINE void ma_pcm_u8_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34757{
34758 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
34759}
34760#endif
34761#if defined(MA_SUPPORT_NEON)
34762static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34763{
34764 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
34765}
34766#endif
34767
34768MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34769{
34770#ifdef MA_USE_REFERENCE_CONVERSION_APIS
34771 ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
34772#else
34773 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34774 if (ma_has_avx2()) {
34775 ma_pcm_u8_to_s32__avx2(dst, src, count, ditherMode);
34776 } else
34777 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34778 if (ma_has_sse2()) {
34779 ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode);
34780 } else
34781 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34782 if (ma_has_neon()) {
34783 ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode);
34784 } else
34785 #endif
34786 {
34787 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
34788 }
34789#endif
34790}
34791
34792
34793static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34794{
34795 float* dst_f32 = (float*)dst;
34796 const ma_uint8* src_u8 = (const ma_uint8*)src;
34797
34798 ma_uint64 i;
34799 for (i = 0; i < count; i += 1) {
34800 float x = (float)src_u8[i];
34801 x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */
34802 x = x - 1; /* 0..2 to -1..1 */
34803
34804 dst_f32[i] = x;
34805 }
34806
34807 (void)ditherMode;
34808}
34809
34810static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34811{
34812 ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
34813}
34814
34815#if defined(MA_SUPPORT_SSE2)
34816static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34817{
34818 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
34819}
34820#endif
34821#if defined(MA_SUPPORT_AVX2)
34822static MA_INLINE void ma_pcm_u8_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34823{
34824 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
34825}
34826#endif
34827#if defined(MA_SUPPORT_NEON)
34828static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34829{
34830 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
34831}
34832#endif
34833
34834MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34835{
34836#ifdef MA_USE_REFERENCE_CONVERSION_APIS
34837 ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
34838#else
34839 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
34840 if (ma_has_avx2()) {
34841 ma_pcm_u8_to_f32__avx2(dst, src, count, ditherMode);
34842 } else
34843 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
34844 if (ma_has_sse2()) {
34845 ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode);
34846 } else
34847 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
34848 if (ma_has_neon()) {
34849 ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode);
34850 } else
34851 #endif
34852 {
34853 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
34854 }
34855#endif
34856}
34857
34858
34859#ifdef MA_USE_REFERENCE_CONVERSION_APIS
34860static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
34861{
34862 ma_uint8* dst_u8 = (ma_uint8*)dst;
34863 const ma_uint8** src_u8 = (const ma_uint8**)src;
34864
34865 ma_uint64 iFrame;
34866 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34867 ma_uint32 iChannel;
34868 for (iChannel = 0; iChannel < channels; iChannel += 1) {
34869 dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
34870 }
34871 }
34872}
34873#else
34874static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
34875{
34876 ma_uint8* dst_u8 = (ma_uint8*)dst;
34877 const ma_uint8** src_u8 = (const ma_uint8**)src;
34878
34879 if (channels == 1) {
34880 ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8));
34881 } else if (channels == 2) {
34882 ma_uint64 iFrame;
34883 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34884 dst_u8[iFrame*2 + 0] = src_u8[0][iFrame];
34885 dst_u8[iFrame*2 + 1] = src_u8[1][iFrame];
34886 }
34887 } else {
34888 ma_uint64 iFrame;
34889 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34890 ma_uint32 iChannel;
34891 for (iChannel = 0; iChannel < channels; iChannel += 1) {
34892 dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
34893 }
34894 }
34895 }
34896}
34897#endif
34898
34899MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
34900{
34901#ifdef MA_USE_REFERENCE_CONVERSION_APIS
34902 ma_pcm_interleave_u8__reference(dst, src, frameCount, channels);
34903#else
34904 ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels);
34905#endif
34906}
34907
34908
34909static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
34910{
34911 ma_uint8** dst_u8 = (ma_uint8**)dst;
34912 const ma_uint8* src_u8 = (const ma_uint8*)src;
34913
34914 ma_uint64 iFrame;
34915 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
34916 ma_uint32 iChannel;
34917 for (iChannel = 0; iChannel < channels; iChannel += 1) {
34918 dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel];
34919 }
34920 }
34921}
34922
34923static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
34924{
34925 ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
34926}
34927
34928MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
34929{
34930#ifdef MA_USE_REFERENCE_CONVERSION_APIS
34931 ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
34932#else
34933 ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels);
34934#endif
34935}
34936
34937
34938/* s16 */
34939static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34940{
34941 ma_uint8* dst_u8 = (ma_uint8*)dst;
34942 const ma_int16* src_s16 = (const ma_int16*)src;
34943
34944 if (ditherMode == ma_dither_mode_none) {
34945 ma_uint64 i;
34946 for (i = 0; i < count; i += 1) {
34947 ma_int16 x = src_s16[i];
34948 x = (ma_int16)(x >> 8);
34949 x = (ma_int16)(x + 128);
34950 dst_u8[i] = (ma_uint8)x;
34951 }
34952 } else {
34953 ma_uint64 i;
34954 for (i = 0; i < count; i += 1) {
34955 ma_int16 x = src_s16[i];
34956
34957 /* Dither. Don't overflow. */
34958 ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F);
34959 if ((x + dither) <= 0x7FFF) {
34960 x = (ma_int16)(x + dither);
34961 } else {
34962 x = 0x7FFF;
34963 }
34964
34965 x = (ma_int16)(x >> 8);
34966 x = (ma_int16)(x + 128);
34967 dst_u8[i] = (ma_uint8)x;
34968 }
34969 }
34970}
34971
34972static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34973{
34974 ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
34975}
34976
34977#if defined(MA_SUPPORT_SSE2)
34978static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34979{
34980 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
34981}
34982#endif
34983#if defined(MA_SUPPORT_AVX2)
34984static MA_INLINE void ma_pcm_s16_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34985{
34986 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
34987}
34988#endif
34989#if defined(MA_SUPPORT_NEON)
34990static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34991{
34992 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
34993}
34994#endif
34995
34996MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
34997{
34998#ifdef MA_USE_REFERENCE_CONVERSION_APIS
34999 ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
35000#else
35001 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35002 if (ma_has_avx2()) {
35003 ma_pcm_s16_to_u8__avx2(dst, src, count, ditherMode);
35004 } else
35005 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35006 if (ma_has_sse2()) {
35007 ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode);
35008 } else
35009 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35010 if (ma_has_neon()) {
35011 ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode);
35012 } else
35013 #endif
35014 {
35015 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
35016 }
35017#endif
35018}
35019
35020
35021MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35022{
35023 (void)ditherMode;
35024 ma_copy_memory_64(dst, src, count * sizeof(ma_int16));
35025}
35026
35027
35028static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35029{
35030 ma_uint8* dst_s24 = (ma_uint8*)dst;
35031 const ma_int16* src_s16 = (const ma_int16*)src;
35032
35033 ma_uint64 i;
35034 for (i = 0; i < count; i += 1) {
35035 dst_s24[i*3+0] = 0;
35036 dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF);
35037 dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8);
35038 }
35039
35040 (void)ditherMode;
35041}
35042
35043static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35044{
35045 ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
35046}
35047
35048#if defined(MA_SUPPORT_SSE2)
35049static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35050{
35051 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
35052}
35053#endif
35054#if defined(MA_SUPPORT_AVX2)
35055static MA_INLINE void ma_pcm_s16_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35056{
35057 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
35058}
35059#endif
35060#if defined(MA_SUPPORT_NEON)
35061static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35062{
35063 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
35064}
35065#endif
35066
35067MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35068{
35069#ifdef MA_USE_REFERENCE_CONVERSION_APIS
35070 ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
35071#else
35072 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35073 if (ma_has_avx2()) {
35074 ma_pcm_s16_to_s24__avx2(dst, src, count, ditherMode);
35075 } else
35076 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35077 if (ma_has_sse2()) {
35078 ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode);
35079 } else
35080 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35081 if (ma_has_neon()) {
35082 ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode);
35083 } else
35084 #endif
35085 {
35086 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
35087 }
35088#endif
35089}
35090
35091
35092static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35093{
35094 ma_int32* dst_s32 = (ma_int32*)dst;
35095 const ma_int16* src_s16 = (const ma_int16*)src;
35096
35097 ma_uint64 i;
35098 for (i = 0; i < count; i += 1) {
35099 dst_s32[i] = src_s16[i] << 16;
35100 }
35101
35102 (void)ditherMode;
35103}
35104
35105static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35106{
35107 ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
35108}
35109
35110#if defined(MA_SUPPORT_SSE2)
35111static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35112{
35113 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
35114}
35115#endif
35116#if defined(MA_SUPPORT_AVX2)
35117static MA_INLINE void ma_pcm_s16_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35118{
35119 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
35120}
35121#endif
35122#if defined(MA_SUPPORT_NEON)
35123static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35124{
35125 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
35126}
35127#endif
35128
35129MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35130{
35131#ifdef MA_USE_REFERENCE_CONVERSION_APIS
35132 ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
35133#else
35134 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35135 if (ma_has_avx2()) {
35136 ma_pcm_s16_to_s32__avx2(dst, src, count, ditherMode);
35137 } else
35138 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35139 if (ma_has_sse2()) {
35140 ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode);
35141 } else
35142 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35143 if (ma_has_neon()) {
35144 ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode);
35145 } else
35146 #endif
35147 {
35148 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
35149 }
35150#endif
35151}
35152
35153
35154static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35155{
35156 float* dst_f32 = (float*)dst;
35157 const ma_int16* src_s16 = (const ma_int16*)src;
35158
35159 ma_uint64 i;
35160 for (i = 0; i < count; i += 1) {
35161 float x = (float)src_s16[i];
35162
35163#if 0
35164 /* The accurate way. */
35165 x = x + 32768.0f; /* -32768..32767 to 0..65535 */
35166 x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */
35167 x = x - 1; /* 0..2 to -1..1 */
35168#else
35169 /* The fast way. */
35170 x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */
35171#endif
35172
35173 dst_f32[i] = x;
35174 }
35175
35176 (void)ditherMode;
35177}
35178
35179static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35180{
35181 ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
35182}
35183
35184#if defined(MA_SUPPORT_SSE2)
35185static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35186{
35187 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
35188}
35189#endif
35190#if defined(MA_SUPPORT_AVX2)
35191static MA_INLINE void ma_pcm_s16_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35192{
35193 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
35194}
35195#endif
35196#if defined(MA_SUPPORT_NEON)
35197static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35198{
35199 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
35200}
35201#endif
35202
35203MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35204{
35205#ifdef MA_USE_REFERENCE_CONVERSION_APIS
35206 ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
35207#else
35208 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35209 if (ma_has_avx2()) {
35210 ma_pcm_s16_to_f32__avx2(dst, src, count, ditherMode);
35211 } else
35212 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35213 if (ma_has_sse2()) {
35214 ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode);
35215 } else
35216 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35217 if (ma_has_neon()) {
35218 ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode);
35219 } else
35220 #endif
35221 {
35222 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
35223 }
35224#endif
35225}
35226
35227
35228static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
35229{
35230 ma_int16* dst_s16 = (ma_int16*)dst;
35231 const ma_int16** src_s16 = (const ma_int16**)src;
35232
35233 ma_uint64 iFrame;
35234 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
35235 ma_uint32 iChannel;
35236 for (iChannel = 0; iChannel < channels; iChannel += 1) {
35237 dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame];
35238 }
35239 }
35240}
35241
35242static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
35243{
35244 ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
35245}
35246
35247MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
35248{
35249#ifdef MA_USE_REFERENCE_CONVERSION_APIS
35250 ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
35251#else
35252 ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels);
35253#endif
35254}
35255
35256
35257static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
35258{
35259 ma_int16** dst_s16 = (ma_int16**)dst;
35260 const ma_int16* src_s16 = (const ma_int16*)src;
35261
35262 ma_uint64 iFrame;
35263 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
35264 ma_uint32 iChannel;
35265 for (iChannel = 0; iChannel < channels; iChannel += 1) {
35266 dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel];
35267 }
35268 }
35269}
35270
35271static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
35272{
35273 ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
35274}
35275
35276MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
35277{
35278#ifdef MA_USE_REFERENCE_CONVERSION_APIS
35279 ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
35280#else
35281 ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels);
35282#endif
35283}
35284
35285
35286/* s24 */
35287static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35288{
35289 ma_uint8* dst_u8 = (ma_uint8*)dst;
35290 const ma_uint8* src_s24 = (const ma_uint8*)src;
35291
35292 if (ditherMode == ma_dither_mode_none) {
35293 ma_uint64 i;
35294 for (i = 0; i < count; i += 1) {
35295 dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128);
35296 }
35297 } else {
35298 ma_uint64 i;
35299 for (i = 0; i < count; i += 1) {
35300 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);
35301
35302 /* Dither. Don't overflow. */
35303 ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
35304 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
35305 x = x + dither;
35306 } else {
35307 x = 0x7FFFFFFF;
35308 }
35309
35310 x = x >> 24;
35311 x = x + 128;
35312 dst_u8[i] = (ma_uint8)x;
35313 }
35314 }
35315}
35316
35317static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35318{
35319 ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
35320}
35321
35322#if defined(MA_SUPPORT_SSE2)
35323static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35324{
35325 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
35326}
35327#endif
35328#if defined(MA_SUPPORT_AVX2)
35329static MA_INLINE void ma_pcm_s24_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35330{
35331 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
35332}
35333#endif
35334#if defined(MA_SUPPORT_NEON)
35335static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35336{
35337 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
35338}
35339#endif
35340
35341MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35342{
35343#ifdef MA_USE_REFERENCE_CONVERSION_APIS
35344 ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
35345#else
35346 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35347 if (ma_has_avx2()) {
35348 ma_pcm_s24_to_u8__avx2(dst, src, count, ditherMode);
35349 } else
35350 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35351 if (ma_has_sse2()) {
35352 ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode);
35353 } else
35354 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35355 if (ma_has_neon()) {
35356 ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode);
35357 } else
35358 #endif
35359 {
35360 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
35361 }
35362#endif
35363}
35364
35365
35366static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35367{
35368 ma_int16* dst_s16 = (ma_int16*)dst;
35369 const ma_uint8* src_s24 = (const ma_uint8*)src;
35370
35371 if (ditherMode == ma_dither_mode_none) {
35372 ma_uint64 i;
35373 for (i = 0; i < count; i += 1) {
35374 ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]);
35375 ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8);
35376 dst_s16[i] = (ma_int16)(dst_lo | dst_hi);
35377 }
35378 } else {
35379 ma_uint64 i;
35380 for (i = 0; i < count; i += 1) {
35381 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);
35382
35383 /* Dither. Don't overflow. */
35384 ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
35385 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
35386 x = x + dither;
35387 } else {
35388 x = 0x7FFFFFFF;
35389 }
35390
35391 x = x >> 16;
35392 dst_s16[i] = (ma_int16)x;
35393 }
35394 }
35395}
35396
35397static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35398{
35399 ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
35400}
35401
35402#if defined(MA_SUPPORT_SSE2)
35403static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35404{
35405 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
35406}
35407#endif
35408#if defined(MA_SUPPORT_AVX2)
35409static MA_INLINE void ma_pcm_s24_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35410{
35411 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
35412}
35413#endif
35414#if defined(MA_SUPPORT_NEON)
35415static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35416{
35417 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
35418}
35419#endif
35420
35421MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35422{
35423#ifdef MA_USE_REFERENCE_CONVERSION_APIS
35424 ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
35425#else
35426 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35427 if (ma_has_avx2()) {
35428 ma_pcm_s24_to_s16__avx2(dst, src, count, ditherMode);
35429 } else
35430 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35431 if (ma_has_sse2()) {
35432 ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode);
35433 } else
35434 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35435 if (ma_has_neon()) {
35436 ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode);
35437 } else
35438 #endif
35439 {
35440 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
35441 }
35442#endif
35443}
35444
35445
35446MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35447{
35448 (void)ditherMode;
35449
35450 ma_copy_memory_64(dst, src, count * 3);
35451}
35452
35453
35454static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35455{
35456 ma_int32* dst_s32 = (ma_int32*)dst;
35457 const ma_uint8* src_s24 = (const ma_uint8*)src;
35458
35459 ma_uint64 i;
35460 for (i = 0; i < count; i += 1) {
35461 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);
35462 }
35463
35464 (void)ditherMode;
35465}
35466
35467static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35468{
35469 ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
35470}
35471
35472#if defined(MA_SUPPORT_SSE2)
35473static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35474{
35475 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
35476}
35477#endif
35478#if defined(MA_SUPPORT_AVX2)
35479static MA_INLINE void ma_pcm_s24_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35480{
35481 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
35482}
35483#endif
35484#if defined(MA_SUPPORT_NEON)
35485static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35486{
35487 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
35488}
35489#endif
35490
35491MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35492{
35493#ifdef MA_USE_REFERENCE_CONVERSION_APIS
35494 ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
35495#else
35496 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35497 if (ma_has_avx2()) {
35498 ma_pcm_s24_to_s32__avx2(dst, src, count, ditherMode);
35499 } else
35500 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35501 if (ma_has_sse2()) {
35502 ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode);
35503 } else
35504 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35505 if (ma_has_neon()) {
35506 ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode);
35507 } else
35508 #endif
35509 {
35510 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
35511 }
35512#endif
35513}
35514
35515
35516static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35517{
35518 float* dst_f32 = (float*)dst;
35519 const ma_uint8* src_s24 = (const ma_uint8*)src;
35520
35521 ma_uint64 i;
35522 for (i = 0; i < count; i += 1) {
35523 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);
35524
35525#if 0
35526 /* The accurate way. */
35527 x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */
35528 x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */
35529 x = x - 1; /* 0..2 to -1..1 */
35530#else
35531 /* The fast way. */
35532 x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */
35533#endif
35534
35535 dst_f32[i] = x;
35536 }
35537
35538 (void)ditherMode;
35539}
35540
35541static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35542{
35543 ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
35544}
35545
35546#if defined(MA_SUPPORT_SSE2)
35547static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35548{
35549 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
35550}
35551#endif
35552#if defined(MA_SUPPORT_AVX2)
35553static MA_INLINE void ma_pcm_s24_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35554{
35555 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
35556}
35557#endif
35558#if defined(MA_SUPPORT_NEON)
35559static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35560{
35561 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
35562}
35563#endif
35564
35565MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35566{
35567#ifdef MA_USE_REFERENCE_CONVERSION_APIS
35568 ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
35569#else
35570 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35571 if (ma_has_avx2()) {
35572 ma_pcm_s24_to_f32__avx2(dst, src, count, ditherMode);
35573 } else
35574 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35575 if (ma_has_sse2()) {
35576 ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode);
35577 } else
35578 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35579 if (ma_has_neon()) {
35580 ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode);
35581 } else
35582 #endif
35583 {
35584 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
35585 }
35586#endif
35587}
35588
35589
35590static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
35591{
35592 ma_uint8* dst8 = (ma_uint8*)dst;
35593 const ma_uint8** src8 = (const ma_uint8**)src;
35594
35595 ma_uint64 iFrame;
35596 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
35597 ma_uint32 iChannel;
35598 for (iChannel = 0; iChannel < channels; iChannel += 1) {
35599 dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0];
35600 dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1];
35601 dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2];
35602 }
35603 }
35604}
35605
35606static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
35607{
35608 ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
35609}
35610
35611MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
35612{
35613#ifdef MA_USE_REFERENCE_CONVERSION_APIS
35614 ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
35615#else
35616 ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels);
35617#endif
35618}
35619
35620
35621static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
35622{
35623 ma_uint8** dst8 = (ma_uint8**)dst;
35624 const ma_uint8* src8 = (const ma_uint8*)src;
35625
35626 ma_uint32 iFrame;
35627 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
35628 ma_uint32 iChannel;
35629 for (iChannel = 0; iChannel < channels; iChannel += 1) {
35630 dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0];
35631 dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1];
35632 dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2];
35633 }
35634 }
35635}
35636
35637static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
35638{
35639 ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
35640}
35641
35642MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
35643{
35644#ifdef MA_USE_REFERENCE_CONVERSION_APIS
35645 ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
35646#else
35647 ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels);
35648#endif
35649}
35650
35651
35652
35653/* s32 */
35654static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35655{
35656 ma_uint8* dst_u8 = (ma_uint8*)dst;
35657 const ma_int32* src_s32 = (const ma_int32*)src;
35658
35659 if (ditherMode == ma_dither_mode_none) {
35660 ma_uint64 i;
35661 for (i = 0; i < count; i += 1) {
35662 ma_int32 x = src_s32[i];
35663 x = x >> 24;
35664 x = x + 128;
35665 dst_u8[i] = (ma_uint8)x;
35666 }
35667 } else {
35668 ma_uint64 i;
35669 for (i = 0; i < count; i += 1) {
35670 ma_int32 x = src_s32[i];
35671
35672 /* Dither. Don't overflow. */
35673 ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
35674 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
35675 x = x + dither;
35676 } else {
35677 x = 0x7FFFFFFF;
35678 }
35679
35680 x = x >> 24;
35681 x = x + 128;
35682 dst_u8[i] = (ma_uint8)x;
35683 }
35684 }
35685}
35686
35687static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35688{
35689 ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
35690}
35691
35692#if defined(MA_SUPPORT_SSE2)
35693static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35694{
35695 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
35696}
35697#endif
35698#if defined(MA_SUPPORT_AVX2)
35699static MA_INLINE void ma_pcm_s32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35700{
35701 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
35702}
35703#endif
35704#if defined(MA_SUPPORT_NEON)
35705static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35706{
35707 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
35708}
35709#endif
35710
35711MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35712{
35713#ifdef MA_USE_REFERENCE_CONVERSION_APIS
35714 ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
35715#else
35716 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35717 if (ma_has_avx2()) {
35718 ma_pcm_s32_to_u8__avx2(dst, src, count, ditherMode);
35719 } else
35720 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35721 if (ma_has_sse2()) {
35722 ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode);
35723 } else
35724 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35725 if (ma_has_neon()) {
35726 ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode);
35727 } else
35728 #endif
35729 {
35730 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
35731 }
35732#endif
35733}
35734
35735
35736static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35737{
35738 ma_int16* dst_s16 = (ma_int16*)dst;
35739 const ma_int32* src_s32 = (const ma_int32*)src;
35740
35741 if (ditherMode == ma_dither_mode_none) {
35742 ma_uint64 i;
35743 for (i = 0; i < count; i += 1) {
35744 ma_int32 x = src_s32[i];
35745 x = x >> 16;
35746 dst_s16[i] = (ma_int16)x;
35747 }
35748 } else {
35749 ma_uint64 i;
35750 for (i = 0; i < count; i += 1) {
35751 ma_int32 x = src_s32[i];
35752
35753 /* Dither. Don't overflow. */
35754 ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
35755 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
35756 x = x + dither;
35757 } else {
35758 x = 0x7FFFFFFF;
35759 }
35760
35761 x = x >> 16;
35762 dst_s16[i] = (ma_int16)x;
35763 }
35764 }
35765}
35766
35767static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35768{
35769 ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
35770}
35771
35772#if defined(MA_SUPPORT_SSE2)
35773static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35774{
35775 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
35776}
35777#endif
35778#if defined(MA_SUPPORT_AVX2)
35779static MA_INLINE void ma_pcm_s32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35780{
35781 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
35782}
35783#endif
35784#if defined(MA_SUPPORT_NEON)
35785static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35786{
35787 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
35788}
35789#endif
35790
35791MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35792{
35793#ifdef MA_USE_REFERENCE_CONVERSION_APIS
35794 ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
35795#else
35796 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35797 if (ma_has_avx2()) {
35798 ma_pcm_s32_to_s16__avx2(dst, src, count, ditherMode);
35799 } else
35800 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35801 if (ma_has_sse2()) {
35802 ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode);
35803 } else
35804 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35805 if (ma_has_neon()) {
35806 ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode);
35807 } else
35808 #endif
35809 {
35810 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
35811 }
35812#endif
35813}
35814
35815
35816static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35817{
35818 ma_uint8* dst_s24 = (ma_uint8*)dst;
35819 const ma_int32* src_s32 = (const ma_int32*)src;
35820
35821 ma_uint64 i;
35822 for (i = 0; i < count; i += 1) {
35823 ma_uint32 x = (ma_uint32)src_s32[i];
35824 dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8);
35825 dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16);
35826 dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24);
35827 }
35828
35829 (void)ditherMode; /* No dithering for s32 -> s24. */
35830}
35831
35832static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35833{
35834 ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
35835}
35836
35837#if defined(MA_SUPPORT_SSE2)
35838static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35839{
35840 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
35841}
35842#endif
35843#if defined(MA_SUPPORT_AVX2)
35844static MA_INLINE void ma_pcm_s32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35845{
35846 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
35847}
35848#endif
35849#if defined(MA_SUPPORT_NEON)
35850static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35851{
35852 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
35853}
35854#endif
35855
35856MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35857{
35858#ifdef MA_USE_REFERENCE_CONVERSION_APIS
35859 ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
35860#else
35861 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35862 if (ma_has_avx2()) {
35863 ma_pcm_s32_to_s24__avx2(dst, src, count, ditherMode);
35864 } else
35865 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35866 if (ma_has_sse2()) {
35867 ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode);
35868 } else
35869 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35870 if (ma_has_neon()) {
35871 ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode);
35872 } else
35873 #endif
35874 {
35875 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
35876 }
35877#endif
35878}
35879
35880
35881MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35882{
35883 (void)ditherMode;
35884
35885 ma_copy_memory_64(dst, src, count * sizeof(ma_int32));
35886}
35887
35888
35889static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35890{
35891 float* dst_f32 = (float*)dst;
35892 const ma_int32* src_s32 = (const ma_int32*)src;
35893
35894 ma_uint64 i;
35895 for (i = 0; i < count; i += 1) {
35896 double x = src_s32[i];
35897
35898#if 0
35899 x = x + 2147483648.0;
35900 x = x * 0.0000000004656612873077392578125;
35901 x = x - 1;
35902#else
35903 x = x / 2147483648.0;
35904#endif
35905
35906 dst_f32[i] = (float)x;
35907 }
35908
35909 (void)ditherMode; /* No dithering for s32 -> f32. */
35910}
35911
35912static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35913{
35914 ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
35915}
35916
35917#if defined(MA_SUPPORT_SSE2)
35918static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35919{
35920 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
35921}
35922#endif
35923#if defined(MA_SUPPORT_AVX2)
35924static MA_INLINE void ma_pcm_s32_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35925{
35926 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
35927}
35928#endif
35929#if defined(MA_SUPPORT_NEON)
35930static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35931{
35932 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
35933}
35934#endif
35935
35936MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
35937{
35938#ifdef MA_USE_REFERENCE_CONVERSION_APIS
35939 ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
35940#else
35941 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
35942 if (ma_has_avx2()) {
35943 ma_pcm_s32_to_f32__avx2(dst, src, count, ditherMode);
35944 } else
35945 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
35946 if (ma_has_sse2()) {
35947 ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode);
35948 } else
35949 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
35950 if (ma_has_neon()) {
35951 ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode);
35952 } else
35953 #endif
35954 {
35955 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
35956 }
35957#endif
35958}
35959
35960
35961static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
35962{
35963 ma_int32* dst_s32 = (ma_int32*)dst;
35964 const ma_int32** src_s32 = (const ma_int32**)src;
35965
35966 ma_uint64 iFrame;
35967 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
35968 ma_uint32 iChannel;
35969 for (iChannel = 0; iChannel < channels; iChannel += 1) {
35970 dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame];
35971 }
35972 }
35973}
35974
35975static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
35976{
35977 ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
35978}
35979
35980MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
35981{
35982#ifdef MA_USE_REFERENCE_CONVERSION_APIS
35983 ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
35984#else
35985 ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels);
35986#endif
35987}
35988
35989
35990static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
35991{
35992 ma_int32** dst_s32 = (ma_int32**)dst;
35993 const ma_int32* src_s32 = (const ma_int32*)src;
35994
35995 ma_uint64 iFrame;
35996 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
35997 ma_uint32 iChannel;
35998 for (iChannel = 0; iChannel < channels; iChannel += 1) {
35999 dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel];
36000 }
36001 }
36002}
36003
36004static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36005{
36006 ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
36007}
36008
36009MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36010{
36011#ifdef MA_USE_REFERENCE_CONVERSION_APIS
36012 ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
36013#else
36014 ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels);
36015#endif
36016}
36017
36018
36019/* f32 */
36020static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36021{
36022 ma_uint64 i;
36023
36024 ma_uint8* dst_u8 = (ma_uint8*)dst;
36025 const float* src_f32 = (const float*)src;
36026
36027 float ditherMin = 0;
36028 float ditherMax = 0;
36029 if (ditherMode != ma_dither_mode_none) {
36030 ditherMin = 1.0f / -128;
36031 ditherMax = 1.0f / 127;
36032 }
36033
36034 for (i = 0; i < count; i += 1) {
36035 float x = src_f32[i];
36036 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
36037 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
36038 x = x + 1; /* -1..1 to 0..2 */
36039 x = x * 127.5f; /* 0..2 to 0..255 */
36040
36041 dst_u8[i] = (ma_uint8)x;
36042 }
36043}
36044
36045static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36046{
36047 ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
36048}
36049
36050#if defined(MA_SUPPORT_SSE2)
36051static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36052{
36053 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
36054}
36055#endif
36056#if defined(MA_SUPPORT_AVX2)
36057static MA_INLINE void ma_pcm_f32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36058{
36059 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
36060}
36061#endif
36062#if defined(MA_SUPPORT_NEON)
36063static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36064{
36065 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
36066}
36067#endif
36068
36069MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36070{
36071#ifdef MA_USE_REFERENCE_CONVERSION_APIS
36072 ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
36073#else
36074 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
36075 if (ma_has_avx2()) {
36076 ma_pcm_f32_to_u8__avx2(dst, src, count, ditherMode);
36077 } else
36078 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
36079 if (ma_has_sse2()) {
36080 ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode);
36081 } else
36082 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
36083 if (ma_has_neon()) {
36084 ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode);
36085 } else
36086 #endif
36087 {
36088 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
36089 }
36090#endif
36091}
36092
36093#ifdef MA_USE_REFERENCE_CONVERSION_APIS
36094static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36095{
36096 ma_uint64 i;
36097
36098 ma_int16* dst_s16 = (ma_int16*)dst;
36099 const float* src_f32 = (const float*)src;
36100
36101 float ditherMin = 0;
36102 float ditherMax = 0;
36103 if (ditherMode != ma_dither_mode_none) {
36104 ditherMin = 1.0f / -32768;
36105 ditherMax = 1.0f / 32767;
36106 }
36107
36108 for (i = 0; i < count; i += 1) {
36109 float x = src_f32[i];
36110 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
36111 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
36112
36113#if 0
36114 /* The accurate way. */
36115 x = x + 1; /* -1..1 to 0..2 */
36116 x = x * 32767.5f; /* 0..2 to 0..65535 */
36117 x = x - 32768.0f; /* 0...65535 to -32768..32767 */
36118#else
36119 /* The fast way. */
36120 x = x * 32767.0f; /* -1..1 to -32767..32767 */
36121#endif
36122
36123 dst_s16[i] = (ma_int16)x;
36124 }
36125}
36126#else
36127static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36128{
36129 ma_uint64 i;
36130 ma_uint64 i4;
36131 ma_uint64 count4;
36132
36133 ma_int16* dst_s16 = (ma_int16*)dst;
36134 const float* src_f32 = (const float*)src;
36135
36136 float ditherMin = 0;
36137 float ditherMax = 0;
36138 if (ditherMode != ma_dither_mode_none) {
36139 ditherMin = 1.0f / -32768;
36140 ditherMax = 1.0f / 32767;
36141 }
36142
36143 /* Unrolled. */
36144 i = 0;
36145 count4 = count >> 2;
36146 for (i4 = 0; i4 < count4; i4 += 1) {
36147 float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
36148 float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
36149 float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
36150 float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
36151
36152 float x0 = src_f32[i+0];
36153 float x1 = src_f32[i+1];
36154 float x2 = src_f32[i+2];
36155 float x3 = src_f32[i+3];
36156
36157 x0 = x0 + d0;
36158 x1 = x1 + d1;
36159 x2 = x2 + d2;
36160 x3 = x3 + d3;
36161
36162 x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
36163 x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
36164 x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
36165 x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
36166
36167 x0 = x0 * 32767.0f;
36168 x1 = x1 * 32767.0f;
36169 x2 = x2 * 32767.0f;
36170 x3 = x3 * 32767.0f;
36171
36172 dst_s16[i+0] = (ma_int16)x0;
36173 dst_s16[i+1] = (ma_int16)x1;
36174 dst_s16[i+2] = (ma_int16)x2;
36175 dst_s16[i+3] = (ma_int16)x3;
36176
36177 i += 4;
36178 }
36179
36180 /* Leftover. */
36181 for (; i < count; i += 1) {
36182 float x = src_f32[i];
36183 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
36184 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
36185 x = x * 32767.0f; /* -1..1 to -32767..32767 */
36186
36187 dst_s16[i] = (ma_int16)x;
36188 }
36189}
36190
36191#if defined(MA_SUPPORT_SSE2)
36192static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36193{
36194 ma_uint64 i;
36195 ma_uint64 i8;
36196 ma_uint64 count8;
36197 ma_int16* dst_s16;
36198 const float* src_f32;
36199 float ditherMin;
36200 float ditherMax;
36201
36202 /* Both the input and output buffers need to be aligned to 16 bytes. */
36203 if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
36204 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
36205 return;
36206 }
36207
36208 dst_s16 = (ma_int16*)dst;
36209 src_f32 = (const float*)src;
36210
36211 ditherMin = 0;
36212 ditherMax = 0;
36213 if (ditherMode != ma_dither_mode_none) {
36214 ditherMin = 1.0f / -32768;
36215 ditherMax = 1.0f / 32767;
36216 }
36217
36218 i = 0;
36219
36220 /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
36221 count8 = count >> 3;
36222 for (i8 = 0; i8 < count8; i8 += 1) {
36223 __m128 d0;
36224 __m128 d1;
36225 __m128 x0;
36226 __m128 x1;
36227
36228 if (ditherMode == ma_dither_mode_none) {
36229 d0 = _mm_set1_ps(0);
36230 d1 = _mm_set1_ps(0);
36231 } else if (ditherMode == ma_dither_mode_rectangle) {
36232 d0 = _mm_set_ps(
36233 ma_dither_f32_rectangle(ditherMin, ditherMax),
36234 ma_dither_f32_rectangle(ditherMin, ditherMax),
36235 ma_dither_f32_rectangle(ditherMin, ditherMax),
36236 ma_dither_f32_rectangle(ditherMin, ditherMax)
36237 );
36238 d1 = _mm_set_ps(
36239 ma_dither_f32_rectangle(ditherMin, ditherMax),
36240 ma_dither_f32_rectangle(ditherMin, ditherMax),
36241 ma_dither_f32_rectangle(ditherMin, ditherMax),
36242 ma_dither_f32_rectangle(ditherMin, ditherMax)
36243 );
36244 } else {
36245 d0 = _mm_set_ps(
36246 ma_dither_f32_triangle(ditherMin, ditherMax),
36247 ma_dither_f32_triangle(ditherMin, ditherMax),
36248 ma_dither_f32_triangle(ditherMin, ditherMax),
36249 ma_dither_f32_triangle(ditherMin, ditherMax)
36250 );
36251 d1 = _mm_set_ps(
36252 ma_dither_f32_triangle(ditherMin, ditherMax),
36253 ma_dither_f32_triangle(ditherMin, ditherMax),
36254 ma_dither_f32_triangle(ditherMin, ditherMax),
36255 ma_dither_f32_triangle(ditherMin, ditherMax)
36256 );
36257 }
36258
36259 x0 = *((__m128*)(src_f32 + i) + 0);
36260 x1 = *((__m128*)(src_f32 + i) + 1);
36261
36262 x0 = _mm_add_ps(x0, d0);
36263 x1 = _mm_add_ps(x1, d1);
36264
36265 x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f));
36266 x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f));
36267
36268 _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1)));
36269
36270 i += 8;
36271 }
36272
36273
36274 /* Leftover. */
36275 for (; i < count; i += 1) {
36276 float x = src_f32[i];
36277 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
36278 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
36279 x = x * 32767.0f; /* -1..1 to -32767..32767 */
36280
36281 dst_s16[i] = (ma_int16)x;
36282 }
36283}
36284#endif /* SSE2 */
36285
36286#if defined(MA_SUPPORT_AVX2)
36287static MA_INLINE void ma_pcm_f32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36288{
36289 ma_uint64 i;
36290 ma_uint64 i16;
36291 ma_uint64 count16;
36292 ma_int16* dst_s16;
36293 const float* src_f32;
36294 float ditherMin;
36295 float ditherMax;
36296
36297 /* Both the input and output buffers need to be aligned to 32 bytes. */
36298 if ((((ma_uintptr)dst & 31) != 0) || (((ma_uintptr)src & 31) != 0)) {
36299 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
36300 return;
36301 }
36302
36303 dst_s16 = (ma_int16*)dst;
36304 src_f32 = (const float*)src;
36305
36306 ditherMin = 0;
36307 ditherMax = 0;
36308 if (ditherMode != ma_dither_mode_none) {
36309 ditherMin = 1.0f / -32768;
36310 ditherMax = 1.0f / 32767;
36311 }
36312
36313 i = 0;
36314
36315 /* AVX2. AVX2 allows us to output 16 s16's at a time which means our loop is unrolled 16 times. */
36316 count16 = count >> 4;
36317 for (i16 = 0; i16 < count16; i16 += 1) {
36318 __m256 d0;
36319 __m256 d1;
36320 __m256 x0;
36321 __m256 x1;
36322 __m256i i0;
36323 __m256i i1;
36324 __m256i p0;
36325 __m256i p1;
36326 __m256i r;
36327
36328 if (ditherMode == ma_dither_mode_none) {
36329 d0 = _mm256_set1_ps(0);
36330 d1 = _mm256_set1_ps(0);
36331 } else if (ditherMode == ma_dither_mode_rectangle) {
36332 d0 = _mm256_set_ps(
36333 ma_dither_f32_rectangle(ditherMin, ditherMax),
36334 ma_dither_f32_rectangle(ditherMin, ditherMax),
36335 ma_dither_f32_rectangle(ditherMin, ditherMax),
36336 ma_dither_f32_rectangle(ditherMin, ditherMax),
36337 ma_dither_f32_rectangle(ditherMin, ditherMax),
36338 ma_dither_f32_rectangle(ditherMin, ditherMax),
36339 ma_dither_f32_rectangle(ditherMin, ditherMax),
36340 ma_dither_f32_rectangle(ditherMin, ditherMax)
36341 );
36342 d1 = _mm256_set_ps(
36343 ma_dither_f32_rectangle(ditherMin, ditherMax),
36344 ma_dither_f32_rectangle(ditherMin, ditherMax),
36345 ma_dither_f32_rectangle(ditherMin, ditherMax),
36346 ma_dither_f32_rectangle(ditherMin, ditherMax),
36347 ma_dither_f32_rectangle(ditherMin, ditherMax),
36348 ma_dither_f32_rectangle(ditherMin, ditherMax),
36349 ma_dither_f32_rectangle(ditherMin, ditherMax),
36350 ma_dither_f32_rectangle(ditherMin, ditherMax)
36351 );
36352 } else {
36353 d0 = _mm256_set_ps(
36354 ma_dither_f32_triangle(ditherMin, ditherMax),
36355 ma_dither_f32_triangle(ditherMin, ditherMax),
36356 ma_dither_f32_triangle(ditherMin, ditherMax),
36357 ma_dither_f32_triangle(ditherMin, ditherMax),
36358 ma_dither_f32_triangle(ditherMin, ditherMax),
36359 ma_dither_f32_triangle(ditherMin, ditherMax),
36360 ma_dither_f32_triangle(ditherMin, ditherMax),
36361 ma_dither_f32_triangle(ditherMin, ditherMax)
36362 );
36363 d1 = _mm256_set_ps(
36364 ma_dither_f32_triangle(ditherMin, ditherMax),
36365 ma_dither_f32_triangle(ditherMin, ditherMax),
36366 ma_dither_f32_triangle(ditherMin, ditherMax),
36367 ma_dither_f32_triangle(ditherMin, ditherMax),
36368 ma_dither_f32_triangle(ditherMin, ditherMax),
36369 ma_dither_f32_triangle(ditherMin, ditherMax),
36370 ma_dither_f32_triangle(ditherMin, ditherMax),
36371 ma_dither_f32_triangle(ditherMin, ditherMax)
36372 );
36373 }
36374
36375 x0 = *((__m256*)(src_f32 + i) + 0);
36376 x1 = *((__m256*)(src_f32 + i) + 1);
36377
36378 x0 = _mm256_add_ps(x0, d0);
36379 x1 = _mm256_add_ps(x1, d1);
36380
36381 x0 = _mm256_mul_ps(x0, _mm256_set1_ps(32767.0f));
36382 x1 = _mm256_mul_ps(x1, _mm256_set1_ps(32767.0f));
36383
36384 /* Computing the final result is a little more complicated for AVX2 than SSE2. */
36385 i0 = _mm256_cvttps_epi32(x0);
36386 i1 = _mm256_cvttps_epi32(x1);
36387 p0 = _mm256_permute2x128_si256(i0, i1, 0 | 32);
36388 p1 = _mm256_permute2x128_si256(i0, i1, 1 | 48);
36389 r = _mm256_packs_epi32(p0, p1);
36390
36391 _mm256_stream_si256(((__m256i*)(dst_s16 + i)), r);
36392
36393 i += 16;
36394 }
36395
36396
36397 /* Leftover. */
36398 for (; i < count; i += 1) {
36399 float x = src_f32[i];
36400 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
36401 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
36402 x = x * 32767.0f; /* -1..1 to -32767..32767 */
36403
36404 dst_s16[i] = (ma_int16)x;
36405 }
36406}
36407#endif /* AVX2 */
36408
36409#if defined(MA_SUPPORT_NEON)
36410static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36411{
36412 ma_uint64 i;
36413 ma_uint64 i8;
36414 ma_uint64 count8;
36415 ma_int16* dst_s16;
36416 const float* src_f32;
36417 float ditherMin;
36418 float ditherMax;
36419
36420 if (!ma_has_neon()) {
36421 return ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
36422 }
36423
36424 /* Both the input and output buffers need to be aligned to 16 bytes. */
36425 if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
36426 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
36427 return;
36428 }
36429
36430 dst_s16 = (ma_int16*)dst;
36431 src_f32 = (const float*)src;
36432
36433 ditherMin = 0;
36434 ditherMax = 0;
36435 if (ditherMode != ma_dither_mode_none) {
36436 ditherMin = 1.0f / -32768;
36437 ditherMax = 1.0f / 32767;
36438 }
36439
36440 i = 0;
36441
36442 /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
36443 count8 = count >> 3;
36444 for (i8 = 0; i8 < count8; i8 += 1) {
36445 float32x4_t d0;
36446 float32x4_t d1;
36447 float32x4_t x0;
36448 float32x4_t x1;
36449 int32x4_t i0;
36450 int32x4_t i1;
36451
36452 if (ditherMode == ma_dither_mode_none) {
36453 d0 = vmovq_n_f32(0);
36454 d1 = vmovq_n_f32(0);
36455 } else if (ditherMode == ma_dither_mode_rectangle) {
36456 float d0v[4];
36457 d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
36458 d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
36459 d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
36460 d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
36461 d0 = vld1q_f32(d0v);
36462
36463 float d1v[4];
36464 d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
36465 d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
36466 d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
36467 d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
36468 d1 = vld1q_f32(d1v);
36469 } else {
36470 float d0v[4];
36471 d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
36472 d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
36473 d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
36474 d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
36475 d0 = vld1q_f32(d0v);
36476
36477 float d1v[4];
36478 d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
36479 d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
36480 d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
36481 d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
36482 d1 = vld1q_f32(d1v);
36483 }
36484
36485 x0 = *((float32x4_t*)(src_f32 + i) + 0);
36486 x1 = *((float32x4_t*)(src_f32 + i) + 1);
36487
36488 x0 = vaddq_f32(x0, d0);
36489 x1 = vaddq_f32(x1, d1);
36490
36491 x0 = vmulq_n_f32(x0, 32767.0f);
36492 x1 = vmulq_n_f32(x1, 32767.0f);
36493
36494 i0 = vcvtq_s32_f32(x0);
36495 i1 = vcvtq_s32_f32(x1);
36496 *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1));
36497
36498 i += 8;
36499 }
36500
36501
36502 /* Leftover. */
36503 for (; i < count; i += 1) {
36504 float x = src_f32[i];
36505 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
36506 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
36507 x = x * 32767.0f; /* -1..1 to -32767..32767 */
36508
36509 dst_s16[i] = (ma_int16)x;
36510 }
36511}
36512#endif /* Neon */
36513#endif /* MA_USE_REFERENCE_CONVERSION_APIS */
36514
36515MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36516{
36517#ifdef MA_USE_REFERENCE_CONVERSION_APIS
36518 ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode);
36519#else
36520 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
36521 if (ma_has_avx2()) {
36522 ma_pcm_f32_to_s16__avx2(dst, src, count, ditherMode);
36523 } else
36524 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
36525 if (ma_has_sse2()) {
36526 ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode);
36527 } else
36528 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
36529 if (ma_has_neon()) {
36530 ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode);
36531 } else
36532 #endif
36533 {
36534 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
36535 }
36536#endif
36537}
36538
36539
36540static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36541{
36542 ma_uint8* dst_s24 = (ma_uint8*)dst;
36543 const float* src_f32 = (const float*)src;
36544
36545 ma_uint64 i;
36546 for (i = 0; i < count; i += 1) {
36547 ma_int32 r;
36548 float x = src_f32[i];
36549 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
36550
36551#if 0
36552 /* The accurate way. */
36553 x = x + 1; /* -1..1 to 0..2 */
36554 x = x * 8388607.5f; /* 0..2 to 0..16777215 */
36555 x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */
36556#else
36557 /* The fast way. */
36558 x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */
36559#endif
36560
36561 r = (ma_int32)x;
36562 dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0);
36563 dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8);
36564 dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16);
36565 }
36566
36567 (void)ditherMode; /* No dithering for f32 -> s24. */
36568}
36569
36570static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36571{
36572 ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
36573}
36574
36575#if defined(MA_SUPPORT_SSE2)
36576static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36577{
36578 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
36579}
36580#endif
36581#if defined(MA_SUPPORT_AVX2)
36582static MA_INLINE void ma_pcm_f32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36583{
36584 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
36585}
36586#endif
36587#if defined(MA_SUPPORT_NEON)
36588static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36589{
36590 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
36591}
36592#endif
36593
36594MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36595{
36596#ifdef MA_USE_REFERENCE_CONVERSION_APIS
36597 ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
36598#else
36599 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
36600 if (ma_has_avx2()) {
36601 ma_pcm_f32_to_s24__avx2(dst, src, count, ditherMode);
36602 } else
36603 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
36604 if (ma_has_sse2()) {
36605 ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode);
36606 } else
36607 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
36608 if (ma_has_neon()) {
36609 ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode);
36610 } else
36611 #endif
36612 {
36613 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
36614 }
36615#endif
36616}
36617
36618
36619static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36620{
36621 ma_int32* dst_s32 = (ma_int32*)dst;
36622 const float* src_f32 = (const float*)src;
36623
36624 ma_uint32 i;
36625 for (i = 0; i < count; i += 1) {
36626 double x = src_f32[i];
36627 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
36628
36629#if 0
36630 /* The accurate way. */
36631 x = x + 1; /* -1..1 to 0..2 */
36632 x = x * 2147483647.5; /* 0..2 to 0..4294967295 */
36633 x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */
36634#else
36635 /* The fast way. */
36636 x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */
36637#endif
36638
36639 dst_s32[i] = (ma_int32)x;
36640 }
36641
36642 (void)ditherMode; /* No dithering for f32 -> s32. */
36643}
36644
36645static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36646{
36647 ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
36648}
36649
36650#if defined(MA_SUPPORT_SSE2)
36651static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36652{
36653 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
36654}
36655#endif
36656#if defined(MA_SUPPORT_AVX2)
36657static MA_INLINE void ma_pcm_f32_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36658{
36659 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
36660}
36661#endif
36662#if defined(MA_SUPPORT_NEON)
36663static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36664{
36665 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
36666}
36667#endif
36668
36669MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36670{
36671#ifdef MA_USE_REFERENCE_CONVERSION_APIS
36672 ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
36673#else
36674 # if MA_PREFERRED_SIMD == MA_SIMD_AVX2
36675 if (ma_has_avx2()) {
36676 ma_pcm_f32_to_s32__avx2(dst, src, count, ditherMode);
36677 } else
36678 #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2
36679 if (ma_has_sse2()) {
36680 ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode);
36681 } else
36682 #elif MA_PREFERRED_SIMD == MA_SIMD_NEON
36683 if (ma_has_neon()) {
36684 ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode);
36685 } else
36686 #endif
36687 {
36688 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
36689 }
36690#endif
36691}
36692
36693
36694MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
36695{
36696 (void)ditherMode;
36697
36698 ma_copy_memory_64(dst, src, count * sizeof(float));
36699}
36700
36701
36702static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
36703{
36704 float* dst_f32 = (float*)dst;
36705 const float** src_f32 = (const float**)src;
36706
36707 ma_uint64 iFrame;
36708 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
36709 ma_uint32 iChannel;
36710 for (iChannel = 0; iChannel < channels; iChannel += 1) {
36711 dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame];
36712 }
36713 }
36714}
36715
36716static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
36717{
36718 ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
36719}
36720
36721MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
36722{
36723#ifdef MA_USE_REFERENCE_CONVERSION_APIS
36724 ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
36725#else
36726 ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels);
36727#endif
36728}
36729
36730
36731static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36732{
36733 float** dst_f32 = (float**)dst;
36734 const float* src_f32 = (const float*)src;
36735
36736 ma_uint64 iFrame;
36737 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
36738 ma_uint32 iChannel;
36739 for (iChannel = 0; iChannel < channels; iChannel += 1) {
36740 dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel];
36741 }
36742 }
36743}
36744
36745static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36746{
36747 ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
36748}
36749
36750MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
36751{
36752#ifdef MA_USE_REFERENCE_CONVERSION_APIS
36753 ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
36754#else
36755 ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels);
36756#endif
36757}
36758
36759
36760MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
36761{
36762 if (formatOut == formatIn) {
36763 ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut));
36764 return;
36765 }
36766
36767 switch (formatIn)
36768 {
36769 case ma_format_u8:
36770 {
36771 switch (formatOut)
36772 {
36773 case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return;
36774 case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return;
36775 case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return;
36776 case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return;
36777 default: break;
36778 }
36779 } break;
36780
36781 case ma_format_s16:
36782 {
36783 switch (formatOut)
36784 {
36785 case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return;
36786 case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return;
36787 case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return;
36788 case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return;
36789 default: break;
36790 }
36791 } break;
36792
36793 case ma_format_s24:
36794 {
36795 switch (formatOut)
36796 {
36797 case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return;
36798 case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return;
36799 case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return;
36800 case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return;
36801 default: break;
36802 }
36803 } break;
36804
36805 case ma_format_s32:
36806 {
36807 switch (formatOut)
36808 {
36809 case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
36810 case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
36811 case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
36812 case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return;
36813 default: break;
36814 }
36815 } break;
36816
36817 case ma_format_f32:
36818 {
36819 switch (formatOut)
36820 {
36821 case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
36822 case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
36823 case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
36824 case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return;
36825 default: break;
36826 }
36827 } break;
36828
36829 default: break;
36830 }
36831}
36832
36833MA_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)
36834{
36835 ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode);
36836}
36837
36838MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
36839{
36840 if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {
36841 return; /* Invalid args. */
36842 }
36843
36844 /* For efficiency we do this per format. */
36845 switch (format) {
36846 case ma_format_s16:
36847 {
36848 const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames;
36849 ma_uint64 iPCMFrame;
36850 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
36851 ma_uint32 iChannel;
36852 for (iChannel = 0; iChannel < channels; ++iChannel) {
36853 ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel];
36854 pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];
36855 }
36856 }
36857 } break;
36858
36859 case ma_format_f32:
36860 {
36861 const float* pSrcF32 = (const float*)pInterleavedPCMFrames;
36862 ma_uint64 iPCMFrame;
36863 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
36864 ma_uint32 iChannel;
36865 for (iChannel = 0; iChannel < channels; ++iChannel) {
36866 float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];
36867 pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];
36868 }
36869 }
36870 } break;
36871
36872 default:
36873 {
36874 ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
36875 ma_uint64 iPCMFrame;
36876 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
36877 ma_uint32 iChannel;
36878 for (iChannel = 0; iChannel < channels; ++iChannel) {
36879 void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
36880 const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
36881 memcpy(pDst, pSrc, sampleSizeInBytes);
36882 }
36883 }
36884 } break;
36885 }
36886}
36887
36888MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
36889{
36890 switch (format)
36891 {
36892 case ma_format_s16:
36893 {
36894 ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames;
36895 ma_uint64 iPCMFrame;
36896 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
36897 ma_uint32 iChannel;
36898 for (iChannel = 0; iChannel < channels; ++iChannel) {
36899 const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel];
36900 pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];
36901 }
36902 }
36903 } break;
36904
36905 case ma_format_f32:
36906 {
36907 float* pDstF32 = (float*)pInterleavedPCMFrames;
36908 ma_uint64 iPCMFrame;
36909 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
36910 ma_uint32 iChannel;
36911 for (iChannel = 0; iChannel < channels; ++iChannel) {
36912 const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];
36913 pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];
36914 }
36915 }
36916 } break;
36917
36918 default:
36919 {
36920 ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
36921 ma_uint64 iPCMFrame;
36922 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
36923 ma_uint32 iChannel;
36924 for (iChannel = 0; iChannel < channels; ++iChannel) {
36925 void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
36926 const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
36927 memcpy(pDst, pSrc, sampleSizeInBytes);
36928 }
36929 }
36930 } break;
36931 }
36932}
36933
36934
36935/**************************************************************************************************************************************************************
36936
36937Biquad Filter
36938
36939**************************************************************************************************************************************************************/
36940#ifndef MA_BIQUAD_FIXED_POINT_SHIFT
36941#define MA_BIQUAD_FIXED_POINT_SHIFT 14
36942#endif
36943
36944static ma_int32 ma_biquad_float_to_fp(double x)
36945{
36946 return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT));
36947}
36948
36949MA_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)
36950{
36951 ma_biquad_config config;
36952
36953 MA_ZERO_OBJECT(&config);
36954 config.format = format;
36955 config.channels = channels;
36956 config.b0 = b0;
36957 config.b1 = b1;
36958 config.b2 = b2;
36959 config.a0 = a0;
36960 config.a1 = a1;
36961 config.a2 = a2;
36962
36963 return config;
36964}
36965
36966MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ)
36967{
36968 if (pBQ == NULL) {
36969 return MA_INVALID_ARGS;
36970 }
36971
36972 MA_ZERO_OBJECT(pBQ);
36973
36974 if (pConfig == NULL) {
36975 return MA_INVALID_ARGS;
36976 }
36977
36978 if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) {
36979 return MA_INVALID_ARGS;
36980 }
36981
36982 return ma_biquad_reinit(pConfig, pBQ);
36983}
36984
36985MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ)
36986{
36987 if (pBQ == NULL || pConfig == NULL) {
36988 return MA_INVALID_ARGS;
36989 }
36990
36991 if (pConfig->a0 == 0) {
36992 return MA_INVALID_ARGS; /* Division by zero. */
36993 }
36994
36995 /* Only supporting f32 and s16. */
36996 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
36997 return MA_INVALID_ARGS;
36998 }
36999
37000 /* The format cannot be changed after initialization. */
37001 if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) {
37002 return MA_INVALID_OPERATION;
37003 }
37004
37005 /* The channel count cannot be changed after initialization. */
37006 if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) {
37007 return MA_INVALID_OPERATION;
37008 }
37009
37010
37011 pBQ->format = pConfig->format;
37012 pBQ->channels = pConfig->channels;
37013
37014 /* Normalize. */
37015 if (pConfig->format == ma_format_f32) {
37016 pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0);
37017 pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0);
37018 pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0);
37019 pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0);
37020 pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0);
37021 } else {
37022 pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0);
37023 pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0);
37024 pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0);
37025 pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0);
37026 pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0);
37027 }
37028
37029 return MA_SUCCESS;
37030}
37031
37032static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX)
37033{
37034 ma_uint32 c;
37035 const ma_uint32 channels = pBQ->channels;
37036 const float b0 = pBQ->b0.f32;
37037 const float b1 = pBQ->b1.f32;
37038 const float b2 = pBQ->b2.f32;
37039 const float a1 = pBQ->a1.f32;
37040 const float a2 = pBQ->a2.f32;
37041
37042 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
37043 for (c = 0; c < channels; c += 1) {
37044 float r1 = pBQ->r1[c].f32;
37045 float r2 = pBQ->r2[c].f32;
37046 float x = pX[c];
37047 float y;
37048
37049 y = b0*x + r1;
37050 r1 = b1*x - a1*y + r2;
37051 r2 = b2*x - a2*y;
37052
37053 pY[c] = y;
37054 pBQ->r1[c].f32 = r1;
37055 pBQ->r2[c].f32 = r2;
37056 }
37057}
37058
37059static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX)
37060{
37061 ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
37062}
37063
37064static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
37065{
37066 ma_uint32 c;
37067 const ma_uint32 channels = pBQ->channels;
37068 const ma_int32 b0 = pBQ->b0.s32;
37069 const ma_int32 b1 = pBQ->b1.s32;
37070 const ma_int32 b2 = pBQ->b2.s32;
37071 const ma_int32 a1 = pBQ->a1.s32;
37072 const ma_int32 a2 = pBQ->a2.s32;
37073
37074 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
37075 for (c = 0; c < channels; c += 1) {
37076 ma_int32 r1 = pBQ->r1[c].s32;
37077 ma_int32 r2 = pBQ->r2[c].s32;
37078 ma_int32 x = pX[c];
37079 ma_int32 y;
37080
37081 y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
37082 r1 = (b1*x - a1*y + r2);
37083 r2 = (b2*x - a2*y);
37084
37085 pY[c] = (ma_int16)ma_clamp(y, -32768, 32767);
37086 pBQ->r1[c].s32 = r1;
37087 pBQ->r2[c].s32 = r2;
37088 }
37089}
37090
37091static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
37092{
37093 ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
37094}
37095
37096MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
37097{
37098 ma_uint32 n;
37099
37100 if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) {
37101 return MA_INVALID_ARGS;
37102 }
37103
37104 /* 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. */
37105
37106 if (pBQ->format == ma_format_f32) {
37107 /* */ float* pY = ( float*)pFramesOut;
37108 const float* pX = (const float*)pFramesIn;
37109
37110 for (n = 0; n < frameCount; n += 1) {
37111 ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
37112 pY += pBQ->channels;
37113 pX += pBQ->channels;
37114 }
37115 } else if (pBQ->format == ma_format_s16) {
37116 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
37117 const ma_int16* pX = (const ma_int16*)pFramesIn;
37118
37119 for (n = 0; n < frameCount; n += 1) {
37120 ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
37121 pY += pBQ->channels;
37122 pX += pBQ->channels;
37123 }
37124 } else {
37125 MA_ASSERT(MA_FALSE);
37126 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
37127 }
37128
37129 return MA_SUCCESS;
37130}
37131
37132MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ)
37133{
37134 if (pBQ == NULL) {
37135 return 0;
37136 }
37137
37138 return 2;
37139}
37140
37141
37142/**************************************************************************************************************************************************************
37143
37144Low-Pass Filter
37145
37146**************************************************************************************************************************************************************/
37147MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
37148{
37149 ma_lpf1_config config;
37150
37151 MA_ZERO_OBJECT(&config);
37152 config.format = format;
37153 config.channels = channels;
37154 config.sampleRate = sampleRate;
37155 config.cutoffFrequency = cutoffFrequency;
37156 config.q = 0.5;
37157
37158 return config;
37159}
37160
37161MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
37162{
37163 ma_lpf2_config config;
37164
37165 MA_ZERO_OBJECT(&config);
37166 config.format = format;
37167 config.channels = channels;
37168 config.sampleRate = sampleRate;
37169 config.cutoffFrequency = cutoffFrequency;
37170 config.q = q;
37171
37172 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
37173 if (config.q == 0) {
37174 config.q = 0.707107;
37175 }
37176
37177 return config;
37178}
37179
37180
37181MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, ma_lpf1* pLPF)
37182{
37183 if (pLPF == NULL) {
37184 return MA_INVALID_ARGS;
37185 }
37186
37187 MA_ZERO_OBJECT(pLPF);
37188
37189 if (pConfig == NULL) {
37190 return MA_INVALID_ARGS;
37191 }
37192
37193 if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) {
37194 return MA_INVALID_ARGS;
37195 }
37196
37197 return ma_lpf1_reinit(pConfig, pLPF);
37198}
37199
37200MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF)
37201{
37202 double a;
37203
37204 if (pLPF == NULL || pConfig == NULL) {
37205 return MA_INVALID_ARGS;
37206 }
37207
37208 /* Only supporting f32 and s16. */
37209 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
37210 return MA_INVALID_ARGS;
37211 }
37212
37213 /* The format cannot be changed after initialization. */
37214 if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {
37215 return MA_INVALID_OPERATION;
37216 }
37217
37218 /* The channel count cannot be changed after initialization. */
37219 if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {
37220 return MA_INVALID_OPERATION;
37221 }
37222
37223 pLPF->format = pConfig->format;
37224 pLPF->channels = pConfig->channels;
37225
37226 a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);
37227 if (pConfig->format == ma_format_f32) {
37228 pLPF->a.f32 = (float)a;
37229 } else {
37230 pLPF->a.s32 = ma_biquad_float_to_fp(a);
37231 }
37232
37233 return MA_SUCCESS;
37234}
37235
37236static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX)
37237{
37238 ma_uint32 c;
37239 const ma_uint32 channels = pLPF->channels;
37240 const float a = pLPF->a.f32;
37241 const float b = 1 - a;
37242
37243 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
37244 for (c = 0; c < channels; c += 1) {
37245 float r1 = pLPF->r1[c].f32;
37246 float x = pX[c];
37247 float y;
37248
37249 y = b*x + a*r1;
37250
37251 pY[c] = y;
37252 pLPF->r1[c].f32 = y;
37253 }
37254}
37255
37256static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX)
37257{
37258 ma_uint32 c;
37259 const ma_uint32 channels = pLPF->channels;
37260 const ma_int32 a = pLPF->a.s32;
37261 const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);
37262
37263 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
37264 for (c = 0; c < channels; c += 1) {
37265 ma_int32 r1 = pLPF->r1[c].s32;
37266 ma_int32 x = pX[c];
37267 ma_int32 y;
37268
37269 y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
37270
37271 pY[c] = (ma_int16)y;
37272 pLPF->r1[c].s32 = (ma_int32)y;
37273 }
37274}
37275
37276MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
37277{
37278 ma_uint32 n;
37279
37280 if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {
37281 return MA_INVALID_ARGS;
37282 }
37283
37284 /* 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. */
37285
37286 if (pLPF->format == ma_format_f32) {
37287 /* */ float* pY = ( float*)pFramesOut;
37288 const float* pX = (const float*)pFramesIn;
37289
37290 for (n = 0; n < frameCount; n += 1) {
37291 ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX);
37292 pY += pLPF->channels;
37293 pX += pLPF->channels;
37294 }
37295 } else if (pLPF->format == ma_format_s16) {
37296 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
37297 const ma_int16* pX = (const ma_int16*)pFramesIn;
37298
37299 for (n = 0; n < frameCount; n += 1) {
37300 ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX);
37301 pY += pLPF->channels;
37302 pX += pLPF->channels;
37303 }
37304 } else {
37305 MA_ASSERT(MA_FALSE);
37306 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
37307 }
37308
37309 return MA_SUCCESS;
37310}
37311
37312MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF)
37313{
37314 if (pLPF == NULL) {
37315 return 0;
37316 }
37317
37318 return 1;
37319}
37320
37321
37322static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig)
37323{
37324 ma_biquad_config bqConfig;
37325 double q;
37326 double w;
37327 double s;
37328 double c;
37329 double a;
37330
37331 MA_ASSERT(pConfig != NULL);
37332
37333 q = pConfig->q;
37334 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
37335 s = ma_sind(w);
37336 c = ma_cosd(w);
37337 a = s / (2*q);
37338
37339 bqConfig.b0 = (1 - c) / 2;
37340 bqConfig.b1 = 1 - c;
37341 bqConfig.b2 = (1 - c) / 2;
37342 bqConfig.a0 = 1 + a;
37343 bqConfig.a1 = -2 * c;
37344 bqConfig.a2 = 1 - a;
37345
37346 bqConfig.format = pConfig->format;
37347 bqConfig.channels = pConfig->channels;
37348
37349 return bqConfig;
37350}
37351
37352MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, ma_lpf2* pLPF)
37353{
37354 ma_result result;
37355 ma_biquad_config bqConfig;
37356
37357 if (pLPF == NULL) {
37358 return MA_INVALID_ARGS;
37359 }
37360
37361 MA_ZERO_OBJECT(pLPF);
37362
37363 if (pConfig == NULL) {
37364 return MA_INVALID_ARGS;
37365 }
37366
37367 bqConfig = ma_lpf2__get_biquad_config(pConfig);
37368 result = ma_biquad_init(&bqConfig, &pLPF->bq);
37369 if (result != MA_SUCCESS) {
37370 return result;
37371 }
37372
37373 return MA_SUCCESS;
37374}
37375
37376MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF)
37377{
37378 ma_result result;
37379 ma_biquad_config bqConfig;
37380
37381 if (pLPF == NULL || pConfig == NULL) {
37382 return MA_INVALID_ARGS;
37383 }
37384
37385 bqConfig = ma_lpf2__get_biquad_config(pConfig);
37386 result = ma_biquad_reinit(&bqConfig, &pLPF->bq);
37387 if (result != MA_SUCCESS) {
37388 return result;
37389 }
37390
37391 return MA_SUCCESS;
37392}
37393
37394static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
37395{
37396 ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn);
37397}
37398
37399static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn)
37400{
37401 ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn);
37402}
37403
37404MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
37405{
37406 if (pLPF == NULL) {
37407 return MA_INVALID_ARGS;
37408 }
37409
37410 return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount);
37411}
37412
37413MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF)
37414{
37415 if (pLPF == NULL) {
37416 return 0;
37417 }
37418
37419 return ma_biquad_get_latency(&pLPF->bq);
37420}
37421
37422
37423MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
37424{
37425 ma_lpf_config config;
37426
37427 MA_ZERO_OBJECT(&config);
37428 config.format = format;
37429 config.channels = channels;
37430 config.sampleRate = sampleRate;
37431 config.cutoffFrequency = cutoffFrequency;
37432 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
37433
37434 return config;
37435}
37436
37437static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, ma_lpf* pLPF, ma_bool32 isNew)
37438{
37439 ma_result result;
37440 ma_uint32 lpf1Count;
37441 ma_uint32 lpf2Count;
37442 ma_uint32 ilpf1;
37443 ma_uint32 ilpf2;
37444
37445 if (pLPF == NULL || pConfig == NULL) {
37446 return MA_INVALID_ARGS;
37447 }
37448
37449 /* Only supporting f32 and s16. */
37450 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
37451 return MA_INVALID_ARGS;
37452 }
37453
37454 /* The format cannot be changed after initialization. */
37455 if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {
37456 return MA_INVALID_OPERATION;
37457 }
37458
37459 /* The channel count cannot be changed after initialization. */
37460 if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {
37461 return MA_INVALID_OPERATION;
37462 }
37463
37464 if (pConfig->order > MA_MAX_FILTER_ORDER) {
37465 return MA_INVALID_ARGS;
37466 }
37467
37468 lpf1Count = pConfig->order % 2;
37469 lpf2Count = pConfig->order / 2;
37470
37471 MA_ASSERT(lpf1Count <= ma_countof(pLPF->lpf1));
37472 MA_ASSERT(lpf2Count <= ma_countof(pLPF->lpf2));
37473
37474 /* The filter order can't change between reinits. */
37475 if (!isNew) {
37476 if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) {
37477 return MA_INVALID_OPERATION;
37478 }
37479 }
37480
37481 for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) {
37482 ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
37483
37484 if (isNew) {
37485 result = ma_lpf1_init(&lpf1Config, &pLPF->lpf1[ilpf1]);
37486 } else {
37487 result = ma_lpf1_reinit(&lpf1Config, &pLPF->lpf1[ilpf1]);
37488 }
37489
37490 if (result != MA_SUCCESS) {
37491 return result;
37492 }
37493 }
37494
37495 for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) {
37496 ma_lpf2_config lpf2Config;
37497 double q;
37498 double a;
37499
37500 /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */
37501 if (lpf1Count == 1) {
37502 a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */
37503 } else {
37504 a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */
37505 }
37506 q = 1 / (2*ma_cosd(a));
37507
37508 lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
37509
37510 if (isNew) {
37511 result = ma_lpf2_init(&lpf2Config, &pLPF->lpf2[ilpf2]);
37512 } else {
37513 result = ma_lpf2_reinit(&lpf2Config, &pLPF->lpf2[ilpf2]);
37514 }
37515
37516 if (result != MA_SUCCESS) {
37517 return result;
37518 }
37519 }
37520
37521 pLPF->lpf1Count = lpf1Count;
37522 pLPF->lpf2Count = lpf2Count;
37523 pLPF->format = pConfig->format;
37524 pLPF->channels = pConfig->channels;
37525 pLPF->sampleRate = pConfig->sampleRate;
37526
37527 return MA_SUCCESS;
37528}
37529
37530MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF)
37531{
37532 if (pLPF == NULL) {
37533 return MA_INVALID_ARGS;
37534 }
37535
37536 MA_ZERO_OBJECT(pLPF);
37537
37538 if (pConfig == NULL) {
37539 return MA_INVALID_ARGS;
37540 }
37541
37542 return ma_lpf_reinit__internal(pConfig, pLPF, /*isNew*/MA_TRUE);
37543}
37544
37545MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF)
37546{
37547 return ma_lpf_reinit__internal(pConfig, pLPF, /*isNew*/MA_FALSE);
37548}
37549
37550static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX)
37551{
37552 ma_uint32 ilpf1;
37553 ma_uint32 ilpf2;
37554
37555 MA_ASSERT(pLPF->format == ma_format_f32);
37556
37557 MA_COPY_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));
37558
37559 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
37560 ma_lpf1_process_pcm_frame_f32(&pLPF->lpf1[ilpf1], pY, pY);
37561 }
37562
37563 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
37564 ma_lpf2_process_pcm_frame_f32(&pLPF->lpf2[ilpf2], pY, pY);
37565 }
37566}
37567
37568static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX)
37569{
37570 ma_uint32 ilpf1;
37571 ma_uint32 ilpf2;
37572
37573 MA_ASSERT(pLPF->format == ma_format_s16);
37574
37575 MA_COPY_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));
37576
37577 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
37578 ma_lpf1_process_pcm_frame_s16(&pLPF->lpf1[ilpf1], pY, pY);
37579 }
37580
37581 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
37582 ma_lpf2_process_pcm_frame_s16(&pLPF->lpf2[ilpf2], pY, pY);
37583 }
37584}
37585
37586MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
37587{
37588 ma_result result;
37589 ma_uint32 ilpf1;
37590 ma_uint32 ilpf2;
37591
37592 if (pLPF == NULL) {
37593 return MA_INVALID_ARGS;
37594 }
37595
37596 /* Faster path for in-place. */
37597 if (pFramesOut == pFramesIn) {
37598 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
37599 result = ma_lpf1_process_pcm_frames(&pLPF->lpf1[ilpf1], pFramesOut, pFramesOut, frameCount);
37600 if (result != MA_SUCCESS) {
37601 return result;
37602 }
37603 }
37604
37605 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
37606 result = ma_lpf2_process_pcm_frames(&pLPF->lpf2[ilpf2], pFramesOut, pFramesOut, frameCount);
37607 if (result != MA_SUCCESS) {
37608 return result;
37609 }
37610 }
37611 }
37612
37613 /* Slightly slower path for copying. */
37614 if (pFramesOut != pFramesIn) {
37615 ma_uint32 iFrame;
37616
37617 /* */ if (pLPF->format == ma_format_f32) {
37618 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
37619 const float* pFramesInF32 = (const float*)pFramesIn;
37620
37621 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
37622 ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32);
37623 pFramesOutF32 += pLPF->channels;
37624 pFramesInF32 += pLPF->channels;
37625 }
37626 } else if (pLPF->format == ma_format_s16) {
37627 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
37628 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
37629
37630 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
37631 ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16);
37632 pFramesOutS16 += pLPF->channels;
37633 pFramesInS16 += pLPF->channels;
37634 }
37635 } else {
37636 MA_ASSERT(MA_FALSE);
37637 return MA_INVALID_OPERATION; /* Should never hit this. */
37638 }
37639 }
37640
37641 return MA_SUCCESS;
37642}
37643
37644MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF)
37645{
37646 if (pLPF == NULL) {
37647 return 0;
37648 }
37649
37650 return pLPF->lpf2Count*2 + pLPF->lpf1Count;
37651}
37652
37653
37654/**************************************************************************************************************************************************************
37655
37656High-Pass Filtering
37657
37658**************************************************************************************************************************************************************/
37659MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
37660{
37661 ma_hpf1_config config;
37662
37663 MA_ZERO_OBJECT(&config);
37664 config.format = format;
37665 config.channels = channels;
37666 config.sampleRate = sampleRate;
37667 config.cutoffFrequency = cutoffFrequency;
37668
37669 return config;
37670}
37671
37672MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
37673{
37674 ma_hpf2_config config;
37675
37676 MA_ZERO_OBJECT(&config);
37677 config.format = format;
37678 config.channels = channels;
37679 config.sampleRate = sampleRate;
37680 config.cutoffFrequency = cutoffFrequency;
37681 config.q = q;
37682
37683 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
37684 if (config.q == 0) {
37685 config.q = 0.707107;
37686 }
37687
37688 return config;
37689}
37690
37691
37692MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, ma_hpf1* pHPF)
37693{
37694 if (pHPF == NULL) {
37695 return MA_INVALID_ARGS;
37696 }
37697
37698 MA_ZERO_OBJECT(pHPF);
37699
37700 if (pConfig == NULL) {
37701 return MA_INVALID_ARGS;
37702 }
37703
37704 if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) {
37705 return MA_INVALID_ARGS;
37706 }
37707
37708 return ma_hpf1_reinit(pConfig, pHPF);
37709}
37710
37711MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF)
37712{
37713 double a;
37714
37715 if (pHPF == NULL || pConfig == NULL) {
37716 return MA_INVALID_ARGS;
37717 }
37718
37719 /* Only supporting f32 and s16. */
37720 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
37721 return MA_INVALID_ARGS;
37722 }
37723
37724 /* The format cannot be changed after initialization. */
37725 if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {
37726 return MA_INVALID_OPERATION;
37727 }
37728
37729 /* The channel count cannot be changed after initialization. */
37730 if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {
37731 return MA_INVALID_OPERATION;
37732 }
37733
37734 pHPF->format = pConfig->format;
37735 pHPF->channels = pConfig->channels;
37736
37737 a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);
37738 if (pConfig->format == ma_format_f32) {
37739 pHPF->a.f32 = (float)a;
37740 } else {
37741 pHPF->a.s32 = ma_biquad_float_to_fp(a);
37742 }
37743
37744 return MA_SUCCESS;
37745}
37746
37747static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX)
37748{
37749 ma_uint32 c;
37750 const ma_uint32 channels = pHPF->channels;
37751 const float a = 1 - pHPF->a.f32;
37752 const float b = 1 - a;
37753
37754 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
37755 for (c = 0; c < channels; c += 1) {
37756 float r1 = pHPF->r1[c].f32;
37757 float x = pX[c];
37758 float y;
37759
37760 y = b*x - a*r1;
37761
37762 pY[c] = y;
37763 pHPF->r1[c].f32 = y;
37764 }
37765}
37766
37767static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX)
37768{
37769 ma_uint32 c;
37770 const ma_uint32 channels = pHPF->channels;
37771 const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32);
37772 const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);
37773
37774 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
37775 for (c = 0; c < channels; c += 1) {
37776 ma_int32 r1 = pHPF->r1[c].s32;
37777 ma_int32 x = pX[c];
37778 ma_int32 y;
37779
37780 y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
37781
37782 pY[c] = (ma_int16)y;
37783 pHPF->r1[c].s32 = (ma_int32)y;
37784 }
37785}
37786
37787MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
37788{
37789 ma_uint32 n;
37790
37791 if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {
37792 return MA_INVALID_ARGS;
37793 }
37794
37795 /* 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. */
37796
37797 if (pHPF->format == ma_format_f32) {
37798 /* */ float* pY = ( float*)pFramesOut;
37799 const float* pX = (const float*)pFramesIn;
37800
37801 for (n = 0; n < frameCount; n += 1) {
37802 ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX);
37803 pY += pHPF->channels;
37804 pX += pHPF->channels;
37805 }
37806 } else if (pHPF->format == ma_format_s16) {
37807 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
37808 const ma_int16* pX = (const ma_int16*)pFramesIn;
37809
37810 for (n = 0; n < frameCount; n += 1) {
37811 ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX);
37812 pY += pHPF->channels;
37813 pX += pHPF->channels;
37814 }
37815 } else {
37816 MA_ASSERT(MA_FALSE);
37817 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
37818 }
37819
37820 return MA_SUCCESS;
37821}
37822
37823MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF)
37824{
37825 if (pHPF == NULL) {
37826 return 0;
37827 }
37828
37829 return 1;
37830}
37831
37832
37833static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig)
37834{
37835 ma_biquad_config bqConfig;
37836 double q;
37837 double w;
37838 double s;
37839 double c;
37840 double a;
37841
37842 MA_ASSERT(pConfig != NULL);
37843
37844 q = pConfig->q;
37845 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
37846 s = ma_sind(w);
37847 c = ma_cosd(w);
37848 a = s / (2*q);
37849
37850 bqConfig.b0 = (1 + c) / 2;
37851 bqConfig.b1 = -(1 + c);
37852 bqConfig.b2 = (1 + c) / 2;
37853 bqConfig.a0 = 1 + a;
37854 bqConfig.a1 = -2 * c;
37855 bqConfig.a2 = 1 - a;
37856
37857 bqConfig.format = pConfig->format;
37858 bqConfig.channels = pConfig->channels;
37859
37860 return bqConfig;
37861}
37862
37863MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, ma_hpf2* pHPF)
37864{
37865 ma_result result;
37866 ma_biquad_config bqConfig;
37867
37868 if (pHPF == NULL) {
37869 return MA_INVALID_ARGS;
37870 }
37871
37872 MA_ZERO_OBJECT(pHPF);
37873
37874 if (pConfig == NULL) {
37875 return MA_INVALID_ARGS;
37876 }
37877
37878 bqConfig = ma_hpf2__get_biquad_config(pConfig);
37879 result = ma_biquad_init(&bqConfig, &pHPF->bq);
37880 if (result != MA_SUCCESS) {
37881 return result;
37882 }
37883
37884 return MA_SUCCESS;
37885}
37886
37887MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF)
37888{
37889 ma_result result;
37890 ma_biquad_config bqConfig;
37891
37892 if (pHPF == NULL || pConfig == NULL) {
37893 return MA_INVALID_ARGS;
37894 }
37895
37896 bqConfig = ma_hpf2__get_biquad_config(pConfig);
37897 result = ma_biquad_reinit(&bqConfig, &pHPF->bq);
37898 if (result != MA_SUCCESS) {
37899 return result;
37900 }
37901
37902 return MA_SUCCESS;
37903}
37904
37905static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
37906{
37907 ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn);
37908}
37909
37910static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn)
37911{
37912 ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn);
37913}
37914
37915MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
37916{
37917 if (pHPF == NULL) {
37918 return MA_INVALID_ARGS;
37919 }
37920
37921 return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount);
37922}
37923
37924MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF)
37925{
37926 if (pHPF == NULL) {
37927 return 0;
37928 }
37929
37930 return ma_biquad_get_latency(&pHPF->bq);
37931}
37932
37933
37934MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
37935{
37936 ma_hpf_config config;
37937
37938 MA_ZERO_OBJECT(&config);
37939 config.format = format;
37940 config.channels = channels;
37941 config.sampleRate = sampleRate;
37942 config.cutoffFrequency = cutoffFrequency;
37943 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
37944
37945 return config;
37946}
37947
37948static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, ma_hpf* pHPF, ma_bool32 isNew)
37949{
37950 ma_result result;
37951 ma_uint32 hpf1Count;
37952 ma_uint32 hpf2Count;
37953 ma_uint32 ihpf1;
37954 ma_uint32 ihpf2;
37955
37956 if (pHPF == NULL || pConfig == NULL) {
37957 return MA_INVALID_ARGS;
37958 }
37959
37960 /* Only supporting f32 and s16. */
37961 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
37962 return MA_INVALID_ARGS;
37963 }
37964
37965 /* The format cannot be changed after initialization. */
37966 if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {
37967 return MA_INVALID_OPERATION;
37968 }
37969
37970 /* The channel count cannot be changed after initialization. */
37971 if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {
37972 return MA_INVALID_OPERATION;
37973 }
37974
37975 if (pConfig->order > MA_MAX_FILTER_ORDER) {
37976 return MA_INVALID_ARGS;
37977 }
37978
37979 hpf1Count = pConfig->order % 2;
37980 hpf2Count = pConfig->order / 2;
37981
37982 MA_ASSERT(hpf1Count <= ma_countof(pHPF->hpf1));
37983 MA_ASSERT(hpf2Count <= ma_countof(pHPF->hpf2));
37984
37985 /* The filter order can't change between reinits. */
37986 if (!isNew) {
37987 if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) {
37988 return MA_INVALID_OPERATION;
37989 }
37990 }
37991
37992 for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) {
37993 ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
37994
37995 if (isNew) {
37996 result = ma_hpf1_init(&hpf1Config, &pHPF->hpf1[ihpf1]);
37997 } else {
37998 result = ma_hpf1_reinit(&hpf1Config, &pHPF->hpf1[ihpf1]);
37999 }
38000
38001 if (result != MA_SUCCESS) {
38002 return result;
38003 }
38004 }
38005
38006 for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) {
38007 ma_hpf2_config hpf2Config;
38008 double q;
38009 double a;
38010
38011 /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */
38012 if (hpf1Count == 1) {
38013 a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */
38014 } else {
38015 a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */
38016 }
38017 q = 1 / (2*ma_cosd(a));
38018
38019 hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
38020
38021 if (isNew) {
38022 result = ma_hpf2_init(&hpf2Config, &pHPF->hpf2[ihpf2]);
38023 } else {
38024 result = ma_hpf2_reinit(&hpf2Config, &pHPF->hpf2[ihpf2]);
38025 }
38026
38027 if (result != MA_SUCCESS) {
38028 return result;
38029 }
38030 }
38031
38032 pHPF->hpf1Count = hpf1Count;
38033 pHPF->hpf2Count = hpf2Count;
38034 pHPF->format = pConfig->format;
38035 pHPF->channels = pConfig->channels;
38036 pHPF->sampleRate = pConfig->sampleRate;
38037
38038 return MA_SUCCESS;
38039}
38040
38041MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, ma_hpf* pHPF)
38042{
38043 if (pHPF == NULL) {
38044 return MA_INVALID_ARGS;
38045 }
38046
38047 MA_ZERO_OBJECT(pHPF);
38048
38049 if (pConfig == NULL) {
38050 return MA_INVALID_ARGS;
38051 }
38052
38053 return ma_hpf_reinit__internal(pConfig, pHPF, /*isNew*/MA_TRUE);
38054}
38055
38056MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF)
38057{
38058 return ma_hpf_reinit__internal(pConfig, pHPF, /*isNew*/MA_FALSE);
38059}
38060
38061MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
38062{
38063 ma_result result;
38064 ma_uint32 ihpf1;
38065 ma_uint32 ihpf2;
38066
38067 if (pHPF == NULL) {
38068 return MA_INVALID_ARGS;
38069 }
38070
38071 /* Faster path for in-place. */
38072 if (pFramesOut == pFramesIn) {
38073 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
38074 result = ma_hpf1_process_pcm_frames(&pHPF->hpf1[ihpf1], pFramesOut, pFramesOut, frameCount);
38075 if (result != MA_SUCCESS) {
38076 return result;
38077 }
38078 }
38079
38080 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
38081 result = ma_hpf2_process_pcm_frames(&pHPF->hpf2[ihpf2], pFramesOut, pFramesOut, frameCount);
38082 if (result != MA_SUCCESS) {
38083 return result;
38084 }
38085 }
38086 }
38087
38088 /* Slightly slower path for copying. */
38089 if (pFramesOut != pFramesIn) {
38090 ma_uint32 iFrame;
38091
38092 /* */ if (pHPF->format == ma_format_f32) {
38093 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
38094 const float* pFramesInF32 = (const float*)pFramesIn;
38095
38096 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
38097 MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));
38098
38099 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
38100 ma_hpf1_process_pcm_frame_f32(&pHPF->hpf1[ihpf1], pFramesOutF32, pFramesOutF32);
38101 }
38102
38103 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
38104 ma_hpf2_process_pcm_frame_f32(&pHPF->hpf2[ihpf2], pFramesOutF32, pFramesOutF32);
38105 }
38106
38107 pFramesOutF32 += pHPF->channels;
38108 pFramesInF32 += pHPF->channels;
38109 }
38110 } else if (pHPF->format == ma_format_s16) {
38111 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
38112 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
38113
38114 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
38115 MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));
38116
38117 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
38118 ma_hpf1_process_pcm_frame_s16(&pHPF->hpf1[ihpf1], pFramesOutS16, pFramesOutS16);
38119 }
38120
38121 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
38122 ma_hpf2_process_pcm_frame_s16(&pHPF->hpf2[ihpf2], pFramesOutS16, pFramesOutS16);
38123 }
38124
38125 pFramesOutS16 += pHPF->channels;
38126 pFramesInS16 += pHPF->channels;
38127 }
38128 } else {
38129 MA_ASSERT(MA_FALSE);
38130 return MA_INVALID_OPERATION; /* Should never hit this. */
38131 }
38132 }
38133
38134 return MA_SUCCESS;
38135}
38136
38137MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF)
38138{
38139 if (pHPF == NULL) {
38140 return 0;
38141 }
38142
38143 return pHPF->hpf2Count*2 + pHPF->hpf1Count;
38144}
38145
38146
38147/**************************************************************************************************************************************************************
38148
38149Band-Pass Filtering
38150
38151**************************************************************************************************************************************************************/
38152MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
38153{
38154 ma_bpf2_config config;
38155
38156 MA_ZERO_OBJECT(&config);
38157 config.format = format;
38158 config.channels = channels;
38159 config.sampleRate = sampleRate;
38160 config.cutoffFrequency = cutoffFrequency;
38161 config.q = q;
38162
38163 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
38164 if (config.q == 0) {
38165 config.q = 0.707107;
38166 }
38167
38168 return config;
38169}
38170
38171
38172static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig)
38173{
38174 ma_biquad_config bqConfig;
38175 double q;
38176 double w;
38177 double s;
38178 double c;
38179 double a;
38180
38181 MA_ASSERT(pConfig != NULL);
38182
38183 q = pConfig->q;
38184 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
38185 s = ma_sind(w);
38186 c = ma_cosd(w);
38187 a = s / (2*q);
38188
38189 bqConfig.b0 = q * a;
38190 bqConfig.b1 = 0;
38191 bqConfig.b2 = -q * a;
38192 bqConfig.a0 = 1 + a;
38193 bqConfig.a1 = -2 * c;
38194 bqConfig.a2 = 1 - a;
38195
38196 bqConfig.format = pConfig->format;
38197 bqConfig.channels = pConfig->channels;
38198
38199 return bqConfig;
38200}
38201
38202MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, ma_bpf2* pBPF)
38203{
38204 ma_result result;
38205 ma_biquad_config bqConfig;
38206
38207 if (pBPF == NULL) {
38208 return MA_INVALID_ARGS;
38209 }
38210
38211 MA_ZERO_OBJECT(pBPF);
38212
38213 if (pConfig == NULL) {
38214 return MA_INVALID_ARGS;
38215 }
38216
38217 bqConfig = ma_bpf2__get_biquad_config(pConfig);
38218 result = ma_biquad_init(&bqConfig, &pBPF->bq);
38219 if (result != MA_SUCCESS) {
38220 return result;
38221 }
38222
38223 return MA_SUCCESS;
38224}
38225
38226MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF)
38227{
38228 ma_result result;
38229 ma_biquad_config bqConfig;
38230
38231 if (pBPF == NULL || pConfig == NULL) {
38232 return MA_INVALID_ARGS;
38233 }
38234
38235 bqConfig = ma_bpf2__get_biquad_config(pConfig);
38236 result = ma_biquad_reinit(&bqConfig, &pBPF->bq);
38237 if (result != MA_SUCCESS) {
38238 return result;
38239 }
38240
38241 return MA_SUCCESS;
38242}
38243
38244static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
38245{
38246 ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn);
38247}
38248
38249static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn)
38250{
38251 ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn);
38252}
38253
38254MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
38255{
38256 if (pBPF == NULL) {
38257 return MA_INVALID_ARGS;
38258 }
38259
38260 return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount);
38261}
38262
38263MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF)
38264{
38265 if (pBPF == NULL) {
38266 return 0;
38267 }
38268
38269 return ma_biquad_get_latency(&pBPF->bq);
38270}
38271
38272
38273MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
38274{
38275 ma_bpf_config config;
38276
38277 MA_ZERO_OBJECT(&config);
38278 config.format = format;
38279 config.channels = channels;
38280 config.sampleRate = sampleRate;
38281 config.cutoffFrequency = cutoffFrequency;
38282 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
38283
38284 return config;
38285}
38286
38287static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, ma_bpf* pBPF, ma_bool32 isNew)
38288{
38289 ma_result result;
38290 ma_uint32 bpf2Count;
38291 ma_uint32 ibpf2;
38292
38293 if (pBPF == NULL || pConfig == NULL) {
38294 return MA_INVALID_ARGS;
38295 }
38296
38297 /* Only supporting f32 and s16. */
38298 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
38299 return MA_INVALID_ARGS;
38300 }
38301
38302 /* The format cannot be changed after initialization. */
38303 if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) {
38304 return MA_INVALID_OPERATION;
38305 }
38306
38307 /* The channel count cannot be changed after initialization. */
38308 if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) {
38309 return MA_INVALID_OPERATION;
38310 }
38311
38312 if (pConfig->order > MA_MAX_FILTER_ORDER) {
38313 return MA_INVALID_ARGS;
38314 }
38315
38316 /* We must have an even number of order. */
38317 if ((pConfig->order & 0x1) != 0) {
38318 return MA_INVALID_ARGS;
38319 }
38320
38321 bpf2Count = pConfig->order / 2;
38322
38323 MA_ASSERT(bpf2Count <= ma_countof(pBPF->bpf2));
38324
38325 /* The filter order can't change between reinits. */
38326 if (!isNew) {
38327 if (pBPF->bpf2Count != bpf2Count) {
38328 return MA_INVALID_OPERATION;
38329 }
38330 }
38331
38332 for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) {
38333 ma_bpf2_config bpf2Config;
38334 double q;
38335
38336 /* TODO: Calculate Q to make this a proper Butterworth filter. */
38337 q = 0.707107;
38338
38339 bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
38340
38341 if (isNew) {
38342 result = ma_bpf2_init(&bpf2Config, &pBPF->bpf2[ibpf2]);
38343 } else {
38344 result = ma_bpf2_reinit(&bpf2Config, &pBPF->bpf2[ibpf2]);
38345 }
38346
38347 if (result != MA_SUCCESS) {
38348 return result;
38349 }
38350 }
38351
38352 pBPF->bpf2Count = bpf2Count;
38353 pBPF->format = pConfig->format;
38354 pBPF->channels = pConfig->channels;
38355
38356 return MA_SUCCESS;
38357}
38358
38359MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, ma_bpf* pBPF)
38360{
38361 if (pBPF == NULL) {
38362 return MA_INVALID_ARGS;
38363 }
38364
38365 MA_ZERO_OBJECT(pBPF);
38366
38367 if (pConfig == NULL) {
38368 return MA_INVALID_ARGS;
38369 }
38370
38371 return ma_bpf_reinit__internal(pConfig, pBPF, /*isNew*/MA_TRUE);
38372}
38373
38374MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF)
38375{
38376 return ma_bpf_reinit__internal(pConfig, pBPF, /*isNew*/MA_FALSE);
38377}
38378
38379MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
38380{
38381 ma_result result;
38382 ma_uint32 ibpf2;
38383
38384 if (pBPF == NULL) {
38385 return MA_INVALID_ARGS;
38386 }
38387
38388 /* Faster path for in-place. */
38389 if (pFramesOut == pFramesIn) {
38390 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
38391 result = ma_bpf2_process_pcm_frames(&pBPF->bpf2[ibpf2], pFramesOut, pFramesOut, frameCount);
38392 if (result != MA_SUCCESS) {
38393 return result;
38394 }
38395 }
38396 }
38397
38398 /* Slightly slower path for copying. */
38399 if (pFramesOut != pFramesIn) {
38400 ma_uint32 iFrame;
38401
38402 /* */ if (pBPF->format == ma_format_f32) {
38403 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
38404 const float* pFramesInF32 = (const float*)pFramesIn;
38405
38406 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
38407 MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));
38408
38409 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
38410 ma_bpf2_process_pcm_frame_f32(&pBPF->bpf2[ibpf2], pFramesOutF32, pFramesOutF32);
38411 }
38412
38413 pFramesOutF32 += pBPF->channels;
38414 pFramesInF32 += pBPF->channels;
38415 }
38416 } else if (pBPF->format == ma_format_s16) {
38417 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
38418 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
38419
38420 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
38421 MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));
38422
38423 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
38424 ma_bpf2_process_pcm_frame_s16(&pBPF->bpf2[ibpf2], pFramesOutS16, pFramesOutS16);
38425 }
38426
38427 pFramesOutS16 += pBPF->channels;
38428 pFramesInS16 += pBPF->channels;
38429 }
38430 } else {
38431 MA_ASSERT(MA_FALSE);
38432 return MA_INVALID_OPERATION; /* Should never hit this. */
38433 }
38434 }
38435
38436 return MA_SUCCESS;
38437}
38438
38439MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF)
38440{
38441 if (pBPF == NULL) {
38442 return 0;
38443 }
38444
38445 return pBPF->bpf2Count*2;
38446}
38447
38448
38449/**************************************************************************************************************************************************************
38450
38451Notching Filter
38452
38453**************************************************************************************************************************************************************/
38454MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
38455{
38456 ma_notch2_config config;
38457
38458 MA_ZERO_OBJECT(&config);
38459 config.format = format;
38460 config.channels = channels;
38461 config.sampleRate = sampleRate;
38462 config.q = q;
38463 config.frequency = frequency;
38464
38465 if (config.q == 0) {
38466 config.q = 0.707107;
38467 }
38468
38469 return config;
38470}
38471
38472
38473static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig)
38474{
38475 ma_biquad_config bqConfig;
38476 double q;
38477 double w;
38478 double s;
38479 double c;
38480 double a;
38481
38482 MA_ASSERT(pConfig != NULL);
38483
38484 q = pConfig->q;
38485 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
38486 s = ma_sind(w);
38487 c = ma_cosd(w);
38488 a = s / (2*q);
38489
38490 bqConfig.b0 = 1;
38491 bqConfig.b1 = -2 * c;
38492 bqConfig.b2 = 1;
38493 bqConfig.a0 = 1 + a;
38494 bqConfig.a1 = -2 * c;
38495 bqConfig.a2 = 1 - a;
38496
38497 bqConfig.format = pConfig->format;
38498 bqConfig.channels = pConfig->channels;
38499
38500 return bqConfig;
38501}
38502
38503MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, ma_notch2* pFilter)
38504{
38505 ma_result result;
38506 ma_biquad_config bqConfig;
38507
38508 if (pFilter == NULL) {
38509 return MA_INVALID_ARGS;
38510 }
38511
38512 MA_ZERO_OBJECT(pFilter);
38513
38514 if (pConfig == NULL) {
38515 return MA_INVALID_ARGS;
38516 }
38517
38518 bqConfig = ma_notch2__get_biquad_config(pConfig);
38519 result = ma_biquad_init(&bqConfig, &pFilter->bq);
38520 if (result != MA_SUCCESS) {
38521 return result;
38522 }
38523
38524 return MA_SUCCESS;
38525}
38526
38527MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter)
38528{
38529 ma_result result;
38530 ma_biquad_config bqConfig;
38531
38532 if (pFilter == NULL || pConfig == NULL) {
38533 return MA_INVALID_ARGS;
38534 }
38535
38536 bqConfig = ma_notch2__get_biquad_config(pConfig);
38537 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
38538 if (result != MA_SUCCESS) {
38539 return result;
38540 }
38541
38542 return MA_SUCCESS;
38543}
38544
38545static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
38546{
38547 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
38548}
38549
38550static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn)
38551{
38552 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
38553}
38554
38555MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
38556{
38557 if (pFilter == NULL) {
38558 return MA_INVALID_ARGS;
38559 }
38560
38561 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
38562}
38563
38564MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter)
38565{
38566 if (pFilter == NULL) {
38567 return 0;
38568 }
38569
38570 return ma_biquad_get_latency(&pFilter->bq);
38571}
38572
38573
38574
38575/**************************************************************************************************************************************************************
38576
38577Peaking EQ Filter
38578
38579**************************************************************************************************************************************************************/
38580MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
38581{
38582 ma_peak2_config config;
38583
38584 MA_ZERO_OBJECT(&config);
38585 config.format = format;
38586 config.channels = channels;
38587 config.sampleRate = sampleRate;
38588 config.gainDB = gainDB;
38589 config.q = q;
38590 config.frequency = frequency;
38591
38592 if (config.q == 0) {
38593 config.q = 0.707107;
38594 }
38595
38596 return config;
38597}
38598
38599
38600static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig)
38601{
38602 ma_biquad_config bqConfig;
38603 double q;
38604 double w;
38605 double s;
38606 double c;
38607 double a;
38608 double A;
38609
38610 MA_ASSERT(pConfig != NULL);
38611
38612 q = pConfig->q;
38613 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
38614 s = ma_sind(w);
38615 c = ma_cosd(w);
38616 a = s / (2*q);
38617 A = ma_powd(10, (pConfig->gainDB / 40));
38618
38619 bqConfig.b0 = 1 + (a * A);
38620 bqConfig.b1 = -2 * c;
38621 bqConfig.b2 = 1 - (a * A);
38622 bqConfig.a0 = 1 + (a / A);
38623 bqConfig.a1 = -2 * c;
38624 bqConfig.a2 = 1 - (a / A);
38625
38626 bqConfig.format = pConfig->format;
38627 bqConfig.channels = pConfig->channels;
38628
38629 return bqConfig;
38630}
38631
38632MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, ma_peak2* pFilter)
38633{
38634 ma_result result;
38635 ma_biquad_config bqConfig;
38636
38637 if (pFilter == NULL) {
38638 return MA_INVALID_ARGS;
38639 }
38640
38641 MA_ZERO_OBJECT(pFilter);
38642
38643 if (pConfig == NULL) {
38644 return MA_INVALID_ARGS;
38645 }
38646
38647 bqConfig = ma_peak2__get_biquad_config(pConfig);
38648 result = ma_biquad_init(&bqConfig, &pFilter->bq);
38649 if (result != MA_SUCCESS) {
38650 return result;
38651 }
38652
38653 return MA_SUCCESS;
38654}
38655
38656MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter)
38657{
38658 ma_result result;
38659 ma_biquad_config bqConfig;
38660
38661 if (pFilter == NULL || pConfig == NULL) {
38662 return MA_INVALID_ARGS;
38663 }
38664
38665 bqConfig = ma_peak2__get_biquad_config(pConfig);
38666 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
38667 if (result != MA_SUCCESS) {
38668 return result;
38669 }
38670
38671 return MA_SUCCESS;
38672}
38673
38674static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
38675{
38676 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
38677}
38678
38679static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn)
38680{
38681 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
38682}
38683
38684MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
38685{
38686 if (pFilter == NULL) {
38687 return MA_INVALID_ARGS;
38688 }
38689
38690 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
38691}
38692
38693MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter)
38694{
38695 if (pFilter == NULL) {
38696 return 0;
38697 }
38698
38699 return ma_biquad_get_latency(&pFilter->bq);
38700}
38701
38702
38703/**************************************************************************************************************************************************************
38704
38705Low Shelf Filter
38706
38707**************************************************************************************************************************************************************/
38708MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
38709{
38710 ma_loshelf2_config config;
38711
38712 MA_ZERO_OBJECT(&config);
38713 config.format = format;
38714 config.channels = channels;
38715 config.sampleRate = sampleRate;
38716 config.gainDB = gainDB;
38717 config.shelfSlope = shelfSlope;
38718 config.frequency = frequency;
38719
38720 return config;
38721}
38722
38723
38724static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig)
38725{
38726 ma_biquad_config bqConfig;
38727 double w;
38728 double s;
38729 double c;
38730 double A;
38731 double S;
38732 double a;
38733 double sqrtA;
38734
38735 MA_ASSERT(pConfig != NULL);
38736
38737 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
38738 s = ma_sind(w);
38739 c = ma_cosd(w);
38740 A = ma_powd(10, (pConfig->gainDB / 40));
38741 S = pConfig->shelfSlope;
38742 a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2);
38743 sqrtA = 2*ma_sqrtd(A)*a;
38744
38745 bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA);
38746 bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c);
38747 bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA);
38748 bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA;
38749 bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c);
38750 bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA;
38751
38752 bqConfig.format = pConfig->format;
38753 bqConfig.channels = pConfig->channels;
38754
38755 return bqConfig;
38756}
38757
38758MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter)
38759{
38760 ma_result result;
38761 ma_biquad_config bqConfig;
38762
38763 if (pFilter == NULL) {
38764 return MA_INVALID_ARGS;
38765 }
38766
38767 MA_ZERO_OBJECT(pFilter);
38768
38769 if (pConfig == NULL) {
38770 return MA_INVALID_ARGS;
38771 }
38772
38773 bqConfig = ma_loshelf2__get_biquad_config(pConfig);
38774 result = ma_biquad_init(&bqConfig, &pFilter->bq);
38775 if (result != MA_SUCCESS) {
38776 return result;
38777 }
38778
38779 return MA_SUCCESS;
38780}
38781
38782MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter)
38783{
38784 ma_result result;
38785 ma_biquad_config bqConfig;
38786
38787 if (pFilter == NULL || pConfig == NULL) {
38788 return MA_INVALID_ARGS;
38789 }
38790
38791 bqConfig = ma_loshelf2__get_biquad_config(pConfig);
38792 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
38793 if (result != MA_SUCCESS) {
38794 return result;
38795 }
38796
38797 return MA_SUCCESS;
38798}
38799
38800static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
38801{
38802 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
38803}
38804
38805static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn)
38806{
38807 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
38808}
38809
38810MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
38811{
38812 if (pFilter == NULL) {
38813 return MA_INVALID_ARGS;
38814 }
38815
38816 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
38817}
38818
38819MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter)
38820{
38821 if (pFilter == NULL) {
38822 return 0;
38823 }
38824
38825 return ma_biquad_get_latency(&pFilter->bq);
38826}
38827
38828
38829/**************************************************************************************************************************************************************
38830
38831High Shelf Filter
38832
38833**************************************************************************************************************************************************************/
38834MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
38835{
38836 ma_hishelf2_config config;
38837
38838 MA_ZERO_OBJECT(&config);
38839 config.format = format;
38840 config.channels = channels;
38841 config.sampleRate = sampleRate;
38842 config.gainDB = gainDB;
38843 config.shelfSlope = shelfSlope;
38844 config.frequency = frequency;
38845
38846 return config;
38847}
38848
38849
38850static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig)
38851{
38852 ma_biquad_config bqConfig;
38853 double w;
38854 double s;
38855 double c;
38856 double A;
38857 double S;
38858 double a;
38859 double sqrtA;
38860
38861 MA_ASSERT(pConfig != NULL);
38862
38863 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
38864 s = ma_sind(w);
38865 c = ma_cosd(w);
38866 A = ma_powd(10, (pConfig->gainDB / 40));
38867 S = pConfig->shelfSlope;
38868 a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2);
38869 sqrtA = 2*ma_sqrtd(A)*a;
38870
38871 bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA);
38872 bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c);
38873 bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA);
38874 bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA;
38875 bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c);
38876 bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA;
38877
38878 bqConfig.format = pConfig->format;
38879 bqConfig.channels = pConfig->channels;
38880
38881 return bqConfig;
38882}
38883
38884MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter)
38885{
38886 ma_result result;
38887 ma_biquad_config bqConfig;
38888
38889 if (pFilter == NULL) {
38890 return MA_INVALID_ARGS;
38891 }
38892
38893 MA_ZERO_OBJECT(pFilter);
38894
38895 if (pConfig == NULL) {
38896 return MA_INVALID_ARGS;
38897 }
38898
38899 bqConfig = ma_hishelf2__get_biquad_config(pConfig);
38900 result = ma_biquad_init(&bqConfig, &pFilter->bq);
38901 if (result != MA_SUCCESS) {
38902 return result;
38903 }
38904
38905 return MA_SUCCESS;
38906}
38907
38908MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter)
38909{
38910 ma_result result;
38911 ma_biquad_config bqConfig;
38912
38913 if (pFilter == NULL || pConfig == NULL) {
38914 return MA_INVALID_ARGS;
38915 }
38916
38917 bqConfig = ma_hishelf2__get_biquad_config(pConfig);
38918 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
38919 if (result != MA_SUCCESS) {
38920 return result;
38921 }
38922
38923 return MA_SUCCESS;
38924}
38925
38926static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
38927{
38928 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
38929}
38930
38931static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn)
38932{
38933 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
38934}
38935
38936MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
38937{
38938 if (pFilter == NULL) {
38939 return MA_INVALID_ARGS;
38940 }
38941
38942 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
38943}
38944
38945MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter)
38946{
38947 if (pFilter == NULL) {
38948 return 0;
38949 }
38950
38951 return ma_biquad_get_latency(&pFilter->bq);
38952}
38953
38954
38955
38956/**************************************************************************************************************************************************************
38957
38958Resampling
38959
38960**************************************************************************************************************************************************************/
38961MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
38962{
38963 ma_linear_resampler_config config;
38964 MA_ZERO_OBJECT(&config);
38965 config.format = format;
38966 config.channels = channels;
38967 config.sampleRateIn = sampleRateIn;
38968 config.sampleRateOut = sampleRateOut;
38969 config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
38970 config.lpfNyquistFactor = 1;
38971
38972 return config;
38973}
38974
38975static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut)
38976{
38977 /*
38978 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
38979 be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate.
38980 */
38981 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. */
38982 ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut;
38983
38984 pResampler->inTimeFrac =
38985 (oldRateTimeWhole * newSampleRateOut) +
38986 ((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut);
38987
38988 /* Make sure the fractional part is less than the output sample rate. */
38989 pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut;
38990 pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut;
38991}
38992
38993static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized)
38994{
38995 ma_result result;
38996 ma_uint32 gcf;
38997 ma_uint32 lpfSampleRate;
38998 double lpfCutoffFrequency;
38999 ma_lpf_config lpfConfig;
39000 ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */
39001
39002 if (pResampler == NULL) {
39003 return MA_INVALID_ARGS;
39004 }
39005
39006 if (sampleRateIn == 0 || sampleRateOut == 0) {
39007 return MA_INVALID_ARGS;
39008 }
39009
39010 oldSampleRateOut = pResampler->config.sampleRateOut;
39011
39012 pResampler->config.sampleRateIn = sampleRateIn;
39013 pResampler->config.sampleRateOut = sampleRateOut;
39014
39015 /* Simplify the sample rate. */
39016 gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut);
39017 pResampler->config.sampleRateIn /= gcf;
39018 pResampler->config.sampleRateOut /= gcf;
39019
39020 /* Always initialize the low-pass filter, even when the order is 0. */
39021 if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) {
39022 return MA_INVALID_ARGS;
39023 }
39024
39025 lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut));
39026 lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor);
39027
39028 lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder);
39029
39030 /*
39031 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
39032 getting cleared. Instead we re-initialize the filter which will maintain any cached frames.
39033 */
39034 if (isResamplerAlreadyInitialized) {
39035 result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf);
39036 } else {
39037 result = ma_lpf_init(&lpfConfig, &pResampler->lpf);
39038 }
39039
39040 if (result != MA_SUCCESS) {
39041 return result;
39042 }
39043
39044
39045 pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut;
39046 pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut;
39047
39048 /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */
39049 ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut);
39050
39051 return MA_SUCCESS;
39052}
39053
39054MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, ma_linear_resampler* pResampler)
39055{
39056 ma_result result;
39057
39058 if (pResampler == NULL) {
39059 return MA_INVALID_ARGS;
39060 }
39061
39062 MA_ZERO_OBJECT(pResampler);
39063
39064 if (pConfig == NULL) {
39065 return MA_INVALID_ARGS;
39066 }
39067
39068 if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) {
39069 return MA_INVALID_ARGS;
39070 }
39071
39072 pResampler->config = *pConfig;
39073
39074 /* Setting the rate will set up the filter and time advances for us. */
39075 result = ma_linear_resampler_set_rate_internal(pResampler, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE);
39076 if (result != MA_SUCCESS) {
39077 return result;
39078 }
39079
39080 pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */
39081 pResampler->inTimeFrac = 0;
39082
39083 return MA_SUCCESS;
39084}
39085
39086MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler)
39087{
39088 if (pResampler == NULL) {
39089 return;
39090 }
39091}
39092
39093static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift)
39094{
39095 ma_int32 b;
39096 ma_int32 c;
39097 ma_int32 r;
39098
39099 MA_ASSERT(a <= (1<<shift));
39100
39101 b = x * ((1<<shift) - a);
39102 c = y * a;
39103 r = b + c;
39104
39105 return (ma_int16)(r >> shift);
39106}
39107
39108static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut)
39109{
39110 ma_uint32 c;
39111 ma_uint32 a;
39112 const ma_uint32 channels = pResampler->config.channels;
39113 const ma_uint32 shift = 12;
39114
39115 MA_ASSERT(pResampler != NULL);
39116 MA_ASSERT(pFrameOut != NULL);
39117
39118 a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut;
39119
39120 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
39121 for (c = 0; c < channels; c += 1) {
39122 ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift);
39123 pFrameOut[c] = s;
39124 }
39125}
39126
39127
39128static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut)
39129{
39130 ma_uint32 c;
39131 float a;
39132 const ma_uint32 channels = pResampler->config.channels;
39133
39134 MA_ASSERT(pResampler != NULL);
39135 MA_ASSERT(pFrameOut != NULL);
39136
39137 a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut;
39138
39139 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
39140 for (c = 0; c < channels; c += 1) {
39141 float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a);
39142 pFrameOut[c] = s;
39143 }
39144}
39145
39146static 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)
39147{
39148 const ma_int16* pFramesInS16;
39149 /* */ ma_int16* pFramesOutS16;
39150 ma_uint64 frameCountIn;
39151 ma_uint64 frameCountOut;
39152 ma_uint64 framesProcessedIn;
39153 ma_uint64 framesProcessedOut;
39154
39155 MA_ASSERT(pResampler != NULL);
39156 MA_ASSERT(pFrameCountIn != NULL);
39157 MA_ASSERT(pFrameCountOut != NULL);
39158
39159 pFramesInS16 = (const ma_int16*)pFramesIn;
39160 pFramesOutS16 = ( ma_int16*)pFramesOut;
39161 frameCountIn = *pFrameCountIn;
39162 frameCountOut = *pFrameCountOut;
39163 framesProcessedIn = 0;
39164 framesProcessedOut = 0;
39165
39166 while (framesProcessedOut < frameCountOut) {
39167 /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
39168 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
39169 ma_uint32 iChannel;
39170
39171 if (pFramesInS16 != NULL) {
39172 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
39173 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
39174 pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
39175 }
39176 pFramesInS16 += pResampler->config.channels;
39177 } else {
39178 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
39179 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
39180 pResampler->x1.s16[iChannel] = 0;
39181 }
39182 }
39183
39184 /* Filter. */
39185 ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16);
39186
39187 framesProcessedIn += 1;
39188 pResampler->inTimeInt -= 1;
39189 }
39190
39191 if (pResampler->inTimeInt > 0) {
39192 break; /* Ran out of input data. */
39193 }
39194
39195 /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
39196 if (pFramesOutS16 != NULL) {
39197 MA_ASSERT(pResampler->inTimeInt == 0);
39198 ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
39199
39200 pFramesOutS16 += pResampler->config.channels;
39201 }
39202
39203 framesProcessedOut += 1;
39204
39205 /* Advance time forward. */
39206 pResampler->inTimeInt += pResampler->inAdvanceInt;
39207 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
39208 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
39209 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
39210 pResampler->inTimeInt += 1;
39211 }
39212 }
39213
39214 *pFrameCountIn = framesProcessedIn;
39215 *pFrameCountOut = framesProcessedOut;
39216
39217 return MA_SUCCESS;
39218}
39219
39220static 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)
39221{
39222 const ma_int16* pFramesInS16;
39223 /* */ ma_int16* pFramesOutS16;
39224 ma_uint64 frameCountIn;
39225 ma_uint64 frameCountOut;
39226 ma_uint64 framesProcessedIn;
39227 ma_uint64 framesProcessedOut;
39228
39229 MA_ASSERT(pResampler != NULL);
39230 MA_ASSERT(pFrameCountIn != NULL);
39231 MA_ASSERT(pFrameCountOut != NULL);
39232
39233 pFramesInS16 = (const ma_int16*)pFramesIn;
39234 pFramesOutS16 = ( ma_int16*)pFramesOut;
39235 frameCountIn = *pFrameCountIn;
39236 frameCountOut = *pFrameCountOut;
39237 framesProcessedIn = 0;
39238 framesProcessedOut = 0;
39239
39240 while (framesProcessedOut < frameCountOut) {
39241 /* Before interpolating we need to load the buffers. */
39242 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
39243 ma_uint32 iChannel;
39244
39245 if (pFramesInS16 != NULL) {
39246 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
39247 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
39248 pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
39249 }
39250 pFramesInS16 += pResampler->config.channels;
39251 } else {
39252 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
39253 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
39254 pResampler->x1.s16[iChannel] = 0;
39255 }
39256 }
39257
39258 framesProcessedIn += 1;
39259 pResampler->inTimeInt -= 1;
39260 }
39261
39262 if (pResampler->inTimeInt > 0) {
39263 break; /* Ran out of input data. */
39264 }
39265
39266 /* Getting here means the frames have been loaded and we can generate the next output frame. */
39267 if (pFramesOutS16 != NULL) {
39268 MA_ASSERT(pResampler->inTimeInt == 0);
39269 ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
39270
39271 /* Filter. */
39272 ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16);
39273
39274 pFramesOutS16 += pResampler->config.channels;
39275 }
39276
39277 framesProcessedOut += 1;
39278
39279 /* Advance time forward. */
39280 pResampler->inTimeInt += pResampler->inAdvanceInt;
39281 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
39282 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
39283 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
39284 pResampler->inTimeInt += 1;
39285 }
39286 }
39287
39288 *pFrameCountIn = framesProcessedIn;
39289 *pFrameCountOut = framesProcessedOut;
39290
39291 return MA_SUCCESS;
39292}
39293
39294static ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
39295{
39296 MA_ASSERT(pResampler != NULL);
39297
39298 if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
39299 return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
39300 } else {
39301 return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
39302 }
39303}
39304
39305
39306static 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)
39307{
39308 const float* pFramesInF32;
39309 /* */ float* pFramesOutF32;
39310 ma_uint64 frameCountIn;
39311 ma_uint64 frameCountOut;
39312 ma_uint64 framesProcessedIn;
39313 ma_uint64 framesProcessedOut;
39314
39315 MA_ASSERT(pResampler != NULL);
39316 MA_ASSERT(pFrameCountIn != NULL);
39317 MA_ASSERT(pFrameCountOut != NULL);
39318
39319 pFramesInF32 = (const float*)pFramesIn;
39320 pFramesOutF32 = ( float*)pFramesOut;
39321 frameCountIn = *pFrameCountIn;
39322 frameCountOut = *pFrameCountOut;
39323 framesProcessedIn = 0;
39324 framesProcessedOut = 0;
39325
39326 while (framesProcessedOut < frameCountOut) {
39327 /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
39328 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
39329 ma_uint32 iChannel;
39330
39331 if (pFramesInF32 != NULL) {
39332 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
39333 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
39334 pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
39335 }
39336 pFramesInF32 += pResampler->config.channels;
39337 } else {
39338 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
39339 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
39340 pResampler->x1.f32[iChannel] = 0;
39341 }
39342 }
39343
39344 /* Filter. */
39345 ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32);
39346
39347 framesProcessedIn += 1;
39348 pResampler->inTimeInt -= 1;
39349 }
39350
39351 if (pResampler->inTimeInt > 0) {
39352 break; /* Ran out of input data. */
39353 }
39354
39355 /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
39356 if (pFramesOutF32 != NULL) {
39357 MA_ASSERT(pResampler->inTimeInt == 0);
39358 ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
39359
39360 pFramesOutF32 += pResampler->config.channels;
39361 }
39362
39363 framesProcessedOut += 1;
39364
39365 /* Advance time forward. */
39366 pResampler->inTimeInt += pResampler->inAdvanceInt;
39367 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
39368 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
39369 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
39370 pResampler->inTimeInt += 1;
39371 }
39372 }
39373
39374 *pFrameCountIn = framesProcessedIn;
39375 *pFrameCountOut = framesProcessedOut;
39376
39377 return MA_SUCCESS;
39378}
39379
39380static 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)
39381{
39382 const float* pFramesInF32;
39383 /* */ float* pFramesOutF32;
39384 ma_uint64 frameCountIn;
39385 ma_uint64 frameCountOut;
39386 ma_uint64 framesProcessedIn;
39387 ma_uint64 framesProcessedOut;
39388
39389 MA_ASSERT(pResampler != NULL);
39390 MA_ASSERT(pFrameCountIn != NULL);
39391 MA_ASSERT(pFrameCountOut != NULL);
39392
39393 pFramesInF32 = (const float*)pFramesIn;
39394 pFramesOutF32 = ( float*)pFramesOut;
39395 frameCountIn = *pFrameCountIn;
39396 frameCountOut = *pFrameCountOut;
39397 framesProcessedIn = 0;
39398 framesProcessedOut = 0;
39399
39400 while (framesProcessedOut < frameCountOut) {
39401 /* Before interpolating we need to load the buffers. */
39402 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
39403 ma_uint32 iChannel;
39404
39405 if (pFramesInF32 != NULL) {
39406 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
39407 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
39408 pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
39409 }
39410 pFramesInF32 += pResampler->config.channels;
39411 } else {
39412 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
39413 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
39414 pResampler->x1.f32[iChannel] = 0;
39415 }
39416 }
39417
39418 framesProcessedIn += 1;
39419 pResampler->inTimeInt -= 1;
39420 }
39421
39422 if (pResampler->inTimeInt > 0) {
39423 break; /* Ran out of input data. */
39424 }
39425
39426 /* Getting here means the frames have been loaded and we can generate the next output frame. */
39427 if (pFramesOutF32 != NULL) {
39428 MA_ASSERT(pResampler->inTimeInt == 0);
39429 ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
39430
39431 /* Filter. */
39432 ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32);
39433
39434 pFramesOutF32 += pResampler->config.channels;
39435 }
39436
39437 framesProcessedOut += 1;
39438
39439 /* Advance time forward. */
39440 pResampler->inTimeInt += pResampler->inAdvanceInt;
39441 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
39442 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
39443 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
39444 pResampler->inTimeInt += 1;
39445 }
39446 }
39447
39448 *pFrameCountIn = framesProcessedIn;
39449 *pFrameCountOut = framesProcessedOut;
39450
39451 return MA_SUCCESS;
39452}
39453
39454static ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
39455{
39456 MA_ASSERT(pResampler != NULL);
39457
39458 if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
39459 return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
39460 } else {
39461 return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
39462 }
39463}
39464
39465
39466MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
39467{
39468 if (pResampler == NULL) {
39469 return MA_INVALID_ARGS;
39470 }
39471
39472 /* */ if (pResampler->config.format == ma_format_s16) {
39473 return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
39474 } else if (pResampler->config.format == ma_format_f32) {
39475 return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
39476 } else {
39477 /* 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(). */
39478 MA_ASSERT(MA_FALSE);
39479 return MA_INVALID_ARGS;
39480 }
39481}
39482
39483
39484MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
39485{
39486 return ma_linear_resampler_set_rate_internal(pResampler, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE);
39487}
39488
39489MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut)
39490{
39491 ma_uint32 n;
39492 ma_uint32 d;
39493
39494 d = 1000;
39495 n = (ma_uint32)(ratioInOut * d);
39496
39497 if (n == 0) {
39498 return MA_INVALID_ARGS; /* Ratio too small. */
39499 }
39500
39501 MA_ASSERT(n != 0);
39502
39503 return ma_linear_resampler_set_rate(pResampler, n, d);
39504}
39505
39506
39507MA_API ma_uint64 ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount)
39508{
39509 ma_uint64 inputFrameCount;
39510
39511 if (pResampler == NULL) {
39512 return 0;
39513 }
39514
39515 if (outputFrameCount == 0) {
39516 return 0;
39517 }
39518
39519 /* Any whole input frames are consumed before the first output frame is generated. */
39520 inputFrameCount = pResampler->inTimeInt;
39521 outputFrameCount -= 1;
39522
39523 /* The rest of the output frames can be calculated in constant time. */
39524 inputFrameCount += outputFrameCount * pResampler->inAdvanceInt;
39525 inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut;
39526
39527 return inputFrameCount;
39528}
39529
39530MA_API ma_uint64 ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount)
39531{
39532 ma_uint64 outputFrameCount;
39533 ma_uint64 preliminaryInputFrameCountFromFrac;
39534 ma_uint64 preliminaryInputFrameCount;
39535
39536 if (pResampler == NULL) {
39537 return 0;
39538 }
39539
39540 /*
39541 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
39542 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
39543 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
39544 of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames.
39545 */
39546 outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn;
39547
39548 /*
39549 We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is
39550 used in the logic below to determine whether or not we need to add an extra output frame.
39551 */
39552 preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut;
39553 preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac;
39554
39555 /*
39556 If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greather than
39557 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
39558 to actually process. Otherwise we need to add the extra output frame.
39559 */
39560 if (preliminaryInputFrameCount <= inputFrameCount) {
39561 outputFrameCount += 1;
39562 }
39563
39564 return outputFrameCount;
39565}
39566
39567MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler)
39568{
39569 if (pResampler == NULL) {
39570 return 0;
39571 }
39572
39573 return 1 + ma_lpf_get_latency(&pResampler->lpf);
39574}
39575
39576MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler)
39577{
39578 if (pResampler == NULL) {
39579 return 0;
39580 }
39581
39582 return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn;
39583}
39584
39585
39586#if defined(ma_speex_resampler_h)
39587#define MA_HAS_SPEEX_RESAMPLER
39588
39589static ma_result ma_result_from_speex_err(int err)
39590{
39591 switch (err)
39592 {
39593 case RESAMPLER_ERR_SUCCESS: return MA_SUCCESS;
39594 case RESAMPLER_ERR_ALLOC_FAILED: return MA_OUT_OF_MEMORY;
39595 case RESAMPLER_ERR_BAD_STATE: return MA_ERROR;
39596 case RESAMPLER_ERR_INVALID_ARG: return MA_INVALID_ARGS;
39597 case RESAMPLER_ERR_PTR_OVERLAP: return MA_INVALID_ARGS;
39598 case RESAMPLER_ERR_OVERFLOW: return MA_ERROR;
39599 default: return MA_ERROR;
39600 }
39601}
39602#endif /* ma_speex_resampler_h */
39603
39604MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm)
39605{
39606 ma_resampler_config config;
39607
39608 MA_ZERO_OBJECT(&config);
39609 config.format = format;
39610 config.channels = channels;
39611 config.sampleRateIn = sampleRateIn;
39612 config.sampleRateOut = sampleRateOut;
39613 config.algorithm = algorithm;
39614
39615 /* Linear. */
39616 config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
39617 config.linear.lpfNyquistFactor = 1;
39618
39619 /* Speex. */
39620 config.speex.quality = 3; /* Cannot leave this as 0 as that is actually a valid value for Speex resampling quality. */
39621
39622 return config;
39623}
39624
39625MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pResampler)
39626{
39627 ma_result result;
39628
39629 if (pResampler == NULL) {
39630 return MA_INVALID_ARGS;
39631 }
39632
39633 MA_ZERO_OBJECT(pResampler);
39634
39635 if (pConfig == NULL) {
39636 return MA_INVALID_ARGS;
39637 }
39638
39639 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
39640 return MA_INVALID_ARGS;
39641 }
39642
39643 pResampler->config = *pConfig;
39644
39645 switch (pConfig->algorithm)
39646 {
39647 case ma_resample_algorithm_linear:
39648 {
39649 ma_linear_resampler_config linearConfig;
39650 linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut);
39651 linearConfig.lpfOrder = pConfig->linear.lpfOrder;
39652 linearConfig.lpfNyquistFactor = pConfig->linear.lpfNyquistFactor;
39653
39654 result = ma_linear_resampler_init(&linearConfig, &pResampler->state.linear);
39655 if (result != MA_SUCCESS) {
39656 return result;
39657 }
39658 } break;
39659
39660 case ma_resample_algorithm_speex:
39661 {
39662 #if defined(MA_HAS_SPEEX_RESAMPLER)
39663 int speexErr;
39664 pResampler->state.speex.pSpeexResamplerState = speex_resampler_init(pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->speex.quality, &speexErr);
39665 if (pResampler->state.speex.pSpeexResamplerState == NULL) {
39666 return ma_result_from_speex_err(speexErr);
39667 }
39668 #else
39669 /* Speex resampler not available. */
39670 return MA_NO_BACKEND;
39671 #endif
39672 } break;
39673
39674 default: return MA_INVALID_ARGS;
39675 }
39676
39677 return MA_SUCCESS;
39678}
39679
39680MA_API void ma_resampler_uninit(ma_resampler* pResampler)
39681{
39682 if (pResampler == NULL) {
39683 return;
39684 }
39685
39686 if (pResampler->config.algorithm == ma_resample_algorithm_linear) {
39687 ma_linear_resampler_uninit(&pResampler->state.linear);
39688 }
39689
39690#if defined(MA_HAS_SPEEX_RESAMPLER)
39691 if (pResampler->config.algorithm == ma_resample_algorithm_speex) {
39692 speex_resampler_destroy((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState);
39693 }
39694#endif
39695}
39696
39697static ma_result ma_resampler_process_pcm_frames__read__linear(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
39698{
39699 return ma_linear_resampler_process_pcm_frames(&pResampler->state.linear, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
39700}
39701
39702#if defined(MA_HAS_SPEEX_RESAMPLER)
39703static ma_result ma_resampler_process_pcm_frames__read__speex(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
39704{
39705 int speexErr;
39706 ma_uint64 frameCountOut;
39707 ma_uint64 frameCountIn;
39708 ma_uint64 framesProcessedOut;
39709 ma_uint64 framesProcessedIn;
39710 unsigned int framesPerIteration = UINT_MAX;
39711
39712 MA_ASSERT(pResampler != NULL);
39713 MA_ASSERT(pFramesOut != NULL);
39714 MA_ASSERT(pFrameCountOut != NULL);
39715 MA_ASSERT(pFrameCountIn != NULL);
39716
39717 /*
39718 Reading from the Speex resampler requires a bit of dancing around for a few reasons. The first thing is that it's frame counts
39719 are in unsigned int's whereas ours is in ma_uint64. We therefore need to run the conversion in a loop. The other, more complicated
39720 problem, is that we need to keep track of the input time, similar to what we do with the linear resampler. The reason we need to
39721 do this is for ma_resampler_get_required_input_frame_count() and ma_resampler_get_expected_output_frame_count().
39722 */
39723 frameCountOut = *pFrameCountOut;
39724 frameCountIn = *pFrameCountIn;
39725 framesProcessedOut = 0;
39726 framesProcessedIn = 0;
39727
39728 while (framesProcessedOut < frameCountOut && framesProcessedIn < frameCountIn) {
39729 unsigned int frameCountInThisIteration;
39730 unsigned int frameCountOutThisIteration;
39731 const void* pFramesInThisIteration;
39732 void* pFramesOutThisIteration;
39733
39734 frameCountInThisIteration = framesPerIteration;
39735 if ((ma_uint64)frameCountInThisIteration > (frameCountIn - framesProcessedIn)) {
39736 frameCountInThisIteration = (unsigned int)(frameCountIn - framesProcessedIn);
39737 }
39738
39739 frameCountOutThisIteration = framesPerIteration;
39740 if ((ma_uint64)frameCountOutThisIteration > (frameCountOut - framesProcessedOut)) {
39741 frameCountOutThisIteration = (unsigned int)(frameCountOut - framesProcessedOut);
39742 }
39743
39744 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels));
39745 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels));
39746
39747 if (pResampler->config.format == ma_format_f32) {
39748 speexErr = speex_resampler_process_interleaved_float((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, (const float*)pFramesInThisIteration, &frameCountInThisIteration, (float*)pFramesOutThisIteration, &frameCountOutThisIteration);
39749 } else if (pResampler->config.format == ma_format_s16) {
39750 speexErr = speex_resampler_process_interleaved_int((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, (const spx_int16_t*)pFramesInThisIteration, &frameCountInThisIteration, (spx_int16_t*)pFramesOutThisIteration, &frameCountOutThisIteration);
39751 } else {
39752 /* Format not supported. Should never get here. */
39753 MA_ASSERT(MA_FALSE);
39754 return MA_INVALID_OPERATION;
39755 }
39756
39757 if (speexErr != RESAMPLER_ERR_SUCCESS) {
39758 return ma_result_from_speex_err(speexErr);
39759 }
39760
39761 framesProcessedIn += frameCountInThisIteration;
39762 framesProcessedOut += frameCountOutThisIteration;
39763 }
39764
39765 *pFrameCountOut = framesProcessedOut;
39766 *pFrameCountIn = framesProcessedIn;
39767
39768 return MA_SUCCESS;
39769}
39770#endif
39771
39772static ma_result ma_resampler_process_pcm_frames__read(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
39773{
39774 MA_ASSERT(pResampler != NULL);
39775 MA_ASSERT(pFramesOut != NULL);
39776
39777 /* pFramesOut is not NULL, which means we must have a capacity. */
39778 if (pFrameCountOut == NULL) {
39779 return MA_INVALID_ARGS;
39780 }
39781
39782 /* It doesn't make sense to not have any input frames to process. */
39783 if (pFrameCountIn == NULL || pFramesIn == NULL) {
39784 return MA_INVALID_ARGS;
39785 }
39786
39787 switch (pResampler->config.algorithm)
39788 {
39789 case ma_resample_algorithm_linear:
39790 {
39791 return ma_resampler_process_pcm_frames__read__linear(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
39792 }
39793
39794 case ma_resample_algorithm_speex:
39795 {
39796 #if defined(MA_HAS_SPEEX_RESAMPLER)
39797 return ma_resampler_process_pcm_frames__read__speex(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
39798 #else
39799 break;
39800 #endif
39801 }
39802
39803 default: break;
39804 }
39805
39806 /* Should never get here. */
39807 MA_ASSERT(MA_FALSE);
39808 return MA_INVALID_ARGS;
39809}
39810
39811
39812static ma_result ma_resampler_process_pcm_frames__seek__linear(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut)
39813{
39814 MA_ASSERT(pResampler != NULL);
39815
39816 /* Seeking is supported natively by the linear resampler. */
39817 return ma_linear_resampler_process_pcm_frames(&pResampler->state.linear, pFramesIn, pFrameCountIn, NULL, pFrameCountOut);
39818}
39819
39820#if defined(MA_HAS_SPEEX_RESAMPLER)
39821static ma_result ma_resampler_process_pcm_frames__seek__speex(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut)
39822{
39823 /* The generic seek method is implemented in on top of ma_resampler_process_pcm_frames__read() by just processing into a dummy buffer. */
39824 float devnull[4096];
39825 ma_uint64 totalOutputFramesToProcess;
39826 ma_uint64 totalOutputFramesProcessed;
39827 ma_uint64 totalInputFramesProcessed;
39828 ma_uint32 bpf;
39829 ma_result result;
39830
39831 MA_ASSERT(pResampler != NULL);
39832
39833 totalOutputFramesProcessed = 0;
39834 totalInputFramesProcessed = 0;
39835 bpf = ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels);
39836
39837 if (pFrameCountOut != NULL) {
39838 /* Seek by output frames. */
39839 totalOutputFramesToProcess = *pFrameCountOut;
39840 } else {
39841 /* Seek by input frames. */
39842 MA_ASSERT(pFrameCountIn != NULL);
39843 totalOutputFramesToProcess = ma_resampler_get_expected_output_frame_count(pResampler, *pFrameCountIn);
39844 }
39845
39846 if (pFramesIn != NULL) {
39847 /* Process input data. */
39848 MA_ASSERT(pFrameCountIn != NULL);
39849 while (totalOutputFramesProcessed < totalOutputFramesToProcess && totalInputFramesProcessed < *pFrameCountIn) {
39850 ma_uint64 inputFramesToProcessThisIteration = (*pFrameCountIn - totalInputFramesProcessed);
39851 ma_uint64 outputFramesToProcessThisIteration = (totalOutputFramesToProcess - totalOutputFramesProcessed);
39852 if (outputFramesToProcessThisIteration > sizeof(devnull) / bpf) {
39853 outputFramesToProcessThisIteration = sizeof(devnull) / bpf;
39854 }
39855
39856 result = ma_resampler_process_pcm_frames__read(pResampler, ma_offset_ptr(pFramesIn, totalInputFramesProcessed*bpf), &inputFramesToProcessThisIteration, ma_offset_ptr(devnull, totalOutputFramesProcessed*bpf), &outputFramesToProcessThisIteration);
39857 if (result != MA_SUCCESS) {
39858 return result;
39859 }
39860
39861 totalOutputFramesProcessed += outputFramesToProcessThisIteration;
39862 totalInputFramesProcessed += inputFramesToProcessThisIteration;
39863 }
39864 } else {
39865 /* Don't process input data - just update timing and filter state as if zeroes were passed in. */
39866 while (totalOutputFramesProcessed < totalOutputFramesToProcess) {
39867 ma_uint64 inputFramesToProcessThisIteration = 16384;
39868 ma_uint64 outputFramesToProcessThisIteration = (totalOutputFramesToProcess - totalOutputFramesProcessed);
39869 if (outputFramesToProcessThisIteration > sizeof(devnull) / bpf) {
39870 outputFramesToProcessThisIteration = sizeof(devnull) / bpf;
39871 }
39872
39873 result = ma_resampler_process_pcm_frames__read(pResampler, NULL, &inputFramesToProcessThisIteration, ma_offset_ptr(devnull, totalOutputFramesProcessed*bpf), &outputFramesToProcessThisIteration);
39874 if (result != MA_SUCCESS) {
39875 return result;
39876 }
39877
39878 totalOutputFramesProcessed += outputFramesToProcessThisIteration;
39879 totalInputFramesProcessed += inputFramesToProcessThisIteration;
39880 }
39881 }
39882
39883
39884 if (pFrameCountIn != NULL) {
39885 *pFrameCountIn = totalInputFramesProcessed;
39886 }
39887 if (pFrameCountOut != NULL) {
39888 *pFrameCountOut = totalOutputFramesProcessed;
39889 }
39890
39891 return MA_SUCCESS;
39892}
39893#endif
39894
39895static ma_result ma_resampler_process_pcm_frames__seek(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut)
39896{
39897 MA_ASSERT(pResampler != NULL);
39898
39899 switch (pResampler->config.algorithm)
39900 {
39901 case ma_resample_algorithm_linear:
39902 {
39903 return ma_resampler_process_pcm_frames__seek__linear(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut);
39904 } break;
39905
39906 case ma_resample_algorithm_speex:
39907 {
39908 #if defined(MA_HAS_SPEEX_RESAMPLER)
39909 return ma_resampler_process_pcm_frames__seek__speex(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut);
39910 #else
39911 break;
39912 #endif
39913 };
39914
39915 default: break;
39916 }
39917
39918 /* Should never hit this. */
39919 MA_ASSERT(MA_FALSE);
39920 return MA_INVALID_ARGS;
39921}
39922
39923
39924MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
39925{
39926 if (pResampler == NULL) {
39927 return MA_INVALID_ARGS;
39928 }
39929
39930 if (pFrameCountOut == NULL && pFrameCountIn == NULL) {
39931 return MA_INVALID_ARGS;
39932 }
39933
39934 if (pFramesOut != NULL) {
39935 /* Reading. */
39936 return ma_resampler_process_pcm_frames__read(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
39937 } else {
39938 /* Seeking. */
39939 return ma_resampler_process_pcm_frames__seek(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut);
39940 }
39941}
39942
39943MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
39944{
39945 if (pResampler == NULL) {
39946 return MA_INVALID_ARGS;
39947 }
39948
39949 if (sampleRateIn == 0 || sampleRateOut == 0) {
39950 return MA_INVALID_ARGS;
39951 }
39952
39953 pResampler->config.sampleRateIn = sampleRateIn;
39954 pResampler->config.sampleRateOut = sampleRateOut;
39955
39956 switch (pResampler->config.algorithm)
39957 {
39958 case ma_resample_algorithm_linear:
39959 {
39960 return ma_linear_resampler_set_rate(&pResampler->state.linear, sampleRateIn, sampleRateOut);
39961 } break;
39962
39963 case ma_resample_algorithm_speex:
39964 {
39965 #if defined(MA_HAS_SPEEX_RESAMPLER)
39966 return ma_result_from_speex_err(speex_resampler_set_rate((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, sampleRateIn, sampleRateOut));
39967 #else
39968 break;
39969 #endif
39970 };
39971
39972 default: break;
39973 }
39974
39975 /* Should never get here. */
39976 MA_ASSERT(MA_FALSE);
39977 return MA_INVALID_OPERATION;
39978}
39979
39980MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio)
39981{
39982 if (pResampler == NULL) {
39983 return MA_INVALID_ARGS;
39984 }
39985
39986 if (pResampler->config.algorithm == ma_resample_algorithm_linear) {
39987 return ma_linear_resampler_set_rate_ratio(&pResampler->state.linear, ratio);
39988 } else {
39989 /* Getting here means the backend does not have native support for setting the rate as a ratio so we just do it generically. */
39990 ma_uint32 n;
39991 ma_uint32 d;
39992
39993 d = 1000;
39994 n = (ma_uint32)(ratio * d);
39995
39996 if (n == 0) {
39997 return MA_INVALID_ARGS; /* Ratio too small. */
39998 }
39999
40000 MA_ASSERT(n != 0);
40001
40002 return ma_resampler_set_rate(pResampler, n, d);
40003 }
40004}
40005
40006MA_API ma_uint64 ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount)
40007{
40008 if (pResampler == NULL) {
40009 return 0;
40010 }
40011
40012 if (outputFrameCount == 0) {
40013 return 0;
40014 }
40015
40016 switch (pResampler->config.algorithm)
40017 {
40018 case ma_resample_algorithm_linear:
40019 {
40020 return ma_linear_resampler_get_required_input_frame_count(&pResampler->state.linear, outputFrameCount);
40021 }
40022
40023 case ma_resample_algorithm_speex:
40024 {
40025 #if defined(MA_HAS_SPEEX_RESAMPLER)
40026 spx_uint64_t count;
40027 int speexErr = ma_speex_resampler_get_required_input_frame_count((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, outputFrameCount, &count);
40028 if (speexErr != RESAMPLER_ERR_SUCCESS) {
40029 return 0;
40030 }
40031
40032 return (ma_uint64)count;
40033 #else
40034 break;
40035 #endif
40036 }
40037
40038 default: break;
40039 }
40040
40041 /* Should never get here. */
40042 MA_ASSERT(MA_FALSE);
40043 return 0;
40044}
40045
40046MA_API ma_uint64 ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount)
40047{
40048 if (pResampler == NULL) {
40049 return 0; /* Invalid args. */
40050 }
40051
40052 if (inputFrameCount == 0) {
40053 return 0;
40054 }
40055
40056 switch (pResampler->config.algorithm)
40057 {
40058 case ma_resample_algorithm_linear:
40059 {
40060 return ma_linear_resampler_get_expected_output_frame_count(&pResampler->state.linear, inputFrameCount);
40061 }
40062
40063 case ma_resample_algorithm_speex:
40064 {
40065 #if defined(MA_HAS_SPEEX_RESAMPLER)
40066 spx_uint64_t count;
40067 int speexErr = ma_speex_resampler_get_expected_output_frame_count((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, inputFrameCount, &count);
40068 if (speexErr != RESAMPLER_ERR_SUCCESS) {
40069 return 0;
40070 }
40071
40072 return (ma_uint64)count;
40073 #else
40074 break;
40075 #endif
40076 }
40077
40078 default: break;
40079 }
40080
40081 /* Should never get here. */
40082 MA_ASSERT(MA_FALSE);
40083 return 0;
40084}
40085
40086MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler)
40087{
40088 if (pResampler == NULL) {
40089 return 0;
40090 }
40091
40092 switch (pResampler->config.algorithm)
40093 {
40094 case ma_resample_algorithm_linear:
40095 {
40096 return ma_linear_resampler_get_input_latency(&pResampler->state.linear);
40097 }
40098
40099 case ma_resample_algorithm_speex:
40100 {
40101 #if defined(MA_HAS_SPEEX_RESAMPLER)
40102 return (ma_uint64)ma_speex_resampler_get_input_latency((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState);
40103 #else
40104 break;
40105 #endif
40106 }
40107
40108 default: break;
40109 }
40110
40111 /* Should never get here. */
40112 MA_ASSERT(MA_FALSE);
40113 return 0;
40114}
40115
40116MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler)
40117{
40118 if (pResampler == NULL) {
40119 return 0;
40120 }
40121
40122 switch (pResampler->config.algorithm)
40123 {
40124 case ma_resample_algorithm_linear:
40125 {
40126 return ma_linear_resampler_get_output_latency(&pResampler->state.linear);
40127 }
40128
40129 case ma_resample_algorithm_speex:
40130 {
40131 #if defined(MA_HAS_SPEEX_RESAMPLER)
40132 return (ma_uint64)ma_speex_resampler_get_output_latency((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState);
40133 #else
40134 break;
40135 #endif
40136 }
40137
40138 default: break;
40139 }
40140
40141 /* Should never get here. */
40142 MA_ASSERT(MA_FALSE);
40143 return 0;
40144}
40145
40146/**************************************************************************************************************************************************************
40147
40148Channel Conversion
40149
40150**************************************************************************************************************************************************************/
40151#ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT
40152#define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12
40153#endif
40154
40155#define MA_PLANE_LEFT 0
40156#define MA_PLANE_RIGHT 1
40157#define MA_PLANE_FRONT 2
40158#define MA_PLANE_BACK 3
40159#define MA_PLANE_BOTTOM 4
40160#define MA_PLANE_TOP 5
40161
40162static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = {
40163 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */
40164 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */
40165 { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */
40166 { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */
40167 { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */
40168 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */
40169 { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */
40170 { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */
40171 { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */
40172 { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */
40173 { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */
40174 { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */
40175 { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */
40176 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */
40177 { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */
40178 { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */
40179 { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */
40180 { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */
40181 { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */
40182 { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */
40183 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */
40184 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */
40185 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */
40186 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */
40187 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */
40188 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */
40189 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */
40190 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */
40191 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */
40192 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */
40193 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */
40194 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */
40195 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */
40196 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */
40197 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */
40198 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */
40199 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */
40200 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */
40201 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */
40202 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */
40203 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */
40204 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */
40205 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */
40206 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */
40207 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */
40208 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */
40209 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */
40210 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */
40211 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */
40212 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */
40213 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */
40214 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */
40215};
40216
40217static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB)
40218{
40219 /*
40220 Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to
40221 the following output configuration:
40222
40223 - front/left
40224 - side/left
40225 - back/left
40226
40227 The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount
40228 of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated.
40229
40230 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
40231 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
40232 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
40233 receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between
40234 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
40235 across 3 spatial dimensions.
40236
40237 The first thing to do is figure out how each speaker's volume is spread over each of plane:
40238 - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane
40239 - side/left: 1 plane (left only) = 1/1 = entire volume from left plane
40240 - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane
40241 - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane
40242
40243 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
40244 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
40245 taken by the other to produce the final contribution.
40246 */
40247
40248 /* Contribution = Sum(Volume to Give * Volume to Take) */
40249 float contribution =
40250 g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] +
40251 g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] +
40252 g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] +
40253 g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] +
40254 g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] +
40255 g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5];
40256
40257 return contribution;
40258}
40259
40260MA_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)
40261{
40262 ma_channel_converter_config config;
40263
40264 /* Channel counts need to be clamped. */
40265 channelsIn = ma_min(channelsIn, ma_countof(config.channelMapIn));
40266 channelsOut = ma_min(channelsOut, ma_countof(config.channelMapOut));
40267
40268 MA_ZERO_OBJECT(&config);
40269 config.format = format;
40270 config.channelsIn = channelsIn;
40271 config.channelsOut = channelsOut;
40272 ma_channel_map_copy_or_default(config.channelMapIn, pChannelMapIn, channelsIn);
40273 ma_channel_map_copy_or_default(config.channelMapOut, pChannelMapOut, channelsOut);
40274 config.mixingMode = mixingMode;
40275
40276 return config;
40277}
40278
40279static ma_int32 ma_channel_converter_float_to_fixed(float x)
40280{
40281 return (ma_int32)(x * (1<<MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT));
40282}
40283
40284static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition)
40285{
40286 int i;
40287
40288 if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) {
40289 return MA_FALSE;
40290 }
40291
40292 if (channelPosition >= MA_CHANNEL_AUX_0 && channelPosition <= MA_CHANNEL_AUX_31) {
40293 return MA_FALSE;
40294 }
40295
40296 for (i = 0; i < 6; ++i) { /* Each side of a cube. */
40297 if (g_maChannelPlaneRatios[channelPosition][i] != 0) {
40298 return MA_TRUE;
40299 }
40300 }
40301
40302 return MA_FALSE;
40303}
40304
40305MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, ma_channel_converter* pConverter)
40306{
40307 ma_uint32 iChannelIn;
40308 ma_uint32 iChannelOut;
40309
40310 if (pConverter == NULL) {
40311 return MA_INVALID_ARGS;
40312 }
40313
40314 MA_ZERO_OBJECT(pConverter);
40315
40316 if (pConfig == NULL) {
40317 return MA_INVALID_ARGS;
40318 }
40319
40320 /* Basic validation for channel counts. */
40321 if (pConfig->channelsIn < MA_MIN_CHANNELS || pConfig->channelsIn > MA_MAX_CHANNELS ||
40322 pConfig->channelsOut < MA_MIN_CHANNELS || pConfig->channelsOut > MA_MAX_CHANNELS) {
40323 return MA_INVALID_ARGS;
40324 }
40325
40326 if (!ma_channel_map_valid(pConfig->channelsIn, pConfig->channelMapIn)) {
40327 return MA_INVALID_ARGS; /* Invalid input channel map. */
40328 }
40329 if (!ma_channel_map_valid(pConfig->channelsOut, pConfig->channelMapOut)) {
40330 return MA_INVALID_ARGS; /* Invalid output channel map. */
40331 }
40332
40333 pConverter->format = pConfig->format;
40334 pConverter->channelsIn = pConfig->channelsIn;
40335 pConverter->channelsOut = pConfig->channelsOut;
40336 ma_channel_map_copy_or_default(pConverter->channelMapIn, pConfig->channelMapIn, pConfig->channelsIn);
40337 ma_channel_map_copy_or_default(pConverter->channelMapOut, pConfig->channelMapOut, pConfig->channelsOut);
40338 pConverter->mixingMode = pConfig->mixingMode;
40339
40340 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
40341 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40342 if (pConverter->format == ma_format_f32) {
40343 pConverter->weights.f32[iChannelIn][iChannelOut] = pConfig->weights[iChannelIn][iChannelOut];
40344 } else {
40345 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(pConfig->weights[iChannelIn][iChannelOut]);
40346 }
40347 }
40348 }
40349
40350
40351
40352 /* If the input and output channels and channel maps are the same we should use a passthrough. */
40353 if (pConverter->channelsIn == pConverter->channelsOut) {
40354 if (ma_channel_map_equal(pConverter->channelsIn, pConverter->channelMapIn, pConverter->channelMapOut)) {
40355 pConverter->isPassthrough = MA_TRUE;
40356 }
40357 if (ma_channel_map_blank(pConverter->channelsIn, pConverter->channelMapIn) || ma_channel_map_blank(pConverter->channelsOut, pConverter->channelMapOut)) {
40358 pConverter->isPassthrough = MA_TRUE;
40359 }
40360 }
40361
40362
40363 /*
40364 We can use a simple case for expanding the mono channel. This will used when expanding a mono input into any output so long
40365 as no LFE is present in the output.
40366 */
40367 if (!pConverter->isPassthrough) {
40368 if (pConverter->channelsIn == 1 && pConverter->channelMapIn[0] == MA_CHANNEL_MONO) {
40369 /* Optimal case if no LFE is in the output channel map. */
40370 pConverter->isSimpleMonoExpansion = MA_TRUE;
40371 if (ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->channelMapOut, MA_CHANNEL_LFE)) {
40372 pConverter->isSimpleMonoExpansion = MA_FALSE;
40373 }
40374 }
40375 }
40376
40377 /* Another optimized case is stereo to mono. */
40378 if (!pConverter->isPassthrough) {
40379 if (pConverter->channelsOut == 1 && pConverter->channelMapOut[0] == MA_CHANNEL_MONO && pConverter->channelsIn == 2) {
40380 /* Optimal case if no LFE is in the input channel map. */
40381 pConverter->isStereoToMono = MA_TRUE;
40382 if (ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->channelMapIn, MA_CHANNEL_LFE)) {
40383 pConverter->isStereoToMono = MA_FALSE;
40384 }
40385 }
40386 }
40387
40388
40389 /*
40390 Here is where we do a bit of pre-processing to know how each channel should be combined to make up the output. Rules:
40391
40392 1) If it's a passthrough, do nothing - it's just a simple memcpy().
40393 2) If the channel counts are the same and every channel position in the input map is present in the output map, use a
40394 simple shuffle. An example might be different 5.1 channel layouts.
40395 3) Otherwise channels are blended based on spatial locality.
40396 */
40397 if (!pConverter->isPassthrough) {
40398 if (pConverter->channelsIn == pConverter->channelsOut) {
40399 ma_bool32 areAllChannelPositionsPresent = MA_TRUE;
40400 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40401 ma_bool32 isInputChannelPositionInOutput = MA_FALSE;
40402 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40403 if (pConverter->channelMapIn[iChannelIn] == pConverter->channelMapOut[iChannelOut]) {
40404 isInputChannelPositionInOutput = MA_TRUE;
40405 break;
40406 }
40407 }
40408
40409 if (!isInputChannelPositionInOutput) {
40410 areAllChannelPositionsPresent = MA_FALSE;
40411 break;
40412 }
40413 }
40414
40415 if (areAllChannelPositionsPresent) {
40416 pConverter->isSimpleShuffle = MA_TRUE;
40417
40418 /*
40419 All the router will be doing is rearranging channels which means all we need to do is use a shuffling table which is just
40420 a mapping between the index of the input channel to the index of the output channel.
40421 */
40422 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40423 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40424 if (pConverter->channelMapIn[iChannelIn] == pConverter->channelMapOut[iChannelOut]) {
40425 pConverter->shuffleTable[iChannelIn] = (ma_uint8)iChannelOut;
40426 break;
40427 }
40428 }
40429 }
40430 }
40431 }
40432 }
40433
40434
40435 /*
40436 Here is where weights are calculated. Note that we calculate the weights at all times, even when using a passthrough and simple
40437 shuffling. We use different algorithms for calculating weights depending on our mixing mode.
40438
40439 In simple mode we don't do any blending (except for converting between mono, which is done in a later step). Instead we just
40440 map 1:1 matching channels. In this mode, if no channels in the input channel map correspond to anything in the output channel
40441 map, nothing will be heard!
40442 */
40443
40444 /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */
40445 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40446 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
40447
40448 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40449 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
40450
40451 if (channelPosIn == channelPosOut) {
40452 if (pConverter->format == ma_format_f32) {
40453 pConverter->weights.f32[iChannelIn][iChannelOut] = 1;
40454 } else {
40455 pConverter->weights.s16[iChannelIn][iChannelOut] = (1 << MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT);
40456 }
40457 }
40458 }
40459 }
40460
40461 /*
40462 The mono channel is accumulated on all other channels, except LFE. Make sure in this loop we exclude output mono channels since
40463 they were handled in the pass above.
40464 */
40465 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40466 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
40467
40468 if (channelPosIn == MA_CHANNEL_MONO) {
40469 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40470 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
40471
40472 if (channelPosOut != MA_CHANNEL_NONE && channelPosOut != MA_CHANNEL_MONO && channelPosOut != MA_CHANNEL_LFE) {
40473 if (pConverter->format == ma_format_f32) {
40474 pConverter->weights.f32[iChannelIn][iChannelOut] = 1;
40475 } else {
40476 pConverter->weights.s16[iChannelIn][iChannelOut] = (1 << MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT);
40477 }
40478 }
40479 }
40480 }
40481 }
40482
40483 /* The output mono channel is the average of all non-none, non-mono and non-lfe input channels. */
40484 {
40485 ma_uint32 len = 0;
40486 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40487 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
40488
40489 if (channelPosIn != MA_CHANNEL_NONE && channelPosIn != MA_CHANNEL_MONO && channelPosIn != MA_CHANNEL_LFE) {
40490 len += 1;
40491 }
40492 }
40493
40494 if (len > 0) {
40495 float monoWeight = 1.0f / len;
40496
40497 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40498 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
40499
40500 if (channelPosOut == MA_CHANNEL_MONO) {
40501 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40502 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
40503
40504 if (channelPosIn != MA_CHANNEL_NONE && channelPosIn != MA_CHANNEL_MONO && channelPosIn != MA_CHANNEL_LFE) {
40505 if (pConverter->format == ma_format_f32) {
40506 pConverter->weights.f32[iChannelIn][iChannelOut] = monoWeight;
40507 } else {
40508 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(monoWeight);
40509 }
40510 }
40511 }
40512 }
40513 }
40514 }
40515 }
40516
40517
40518 /* Input and output channels that are not present on the other side need to be blended in based on spatial locality. */
40519 switch (pConverter->mixingMode)
40520 {
40521 case ma_channel_mix_mode_rectangular:
40522 {
40523 /* Unmapped input channels. */
40524 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40525 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
40526
40527 if (ma_is_spatial_channel_position(channelPosIn)) {
40528 if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->channelMapOut, channelPosIn)) {
40529 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40530 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
40531
40532 if (ma_is_spatial_channel_position(channelPosOut)) {
40533 float weight = 0;
40534 if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
40535 weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
40536 }
40537
40538 /* Only apply the weight if we haven't already got some contribution from the respective channels. */
40539 if (pConverter->format == ma_format_f32) {
40540 if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
40541 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
40542 }
40543 } else {
40544 if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
40545 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
40546 }
40547 }
40548 }
40549 }
40550 }
40551 }
40552 }
40553
40554 /* Unmapped output channels. */
40555 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40556 ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
40557
40558 if (ma_is_spatial_channel_position(channelPosOut)) {
40559 if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->channelMapIn, channelPosOut)) {
40560 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40561 ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
40562
40563 if (ma_is_spatial_channel_position(channelPosIn)) {
40564 float weight = 0;
40565 if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
40566 weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
40567 }
40568
40569 /* Only apply the weight if we haven't already got some contribution from the respective channels. */
40570 if (pConverter->format == ma_format_f32) {
40571 if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
40572 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
40573 }
40574 } else {
40575 if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
40576 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
40577 }
40578 }
40579 }
40580 }
40581 }
40582 }
40583 }
40584 } break;
40585
40586 case ma_channel_mix_mode_simple:
40587 {
40588 /* In simple mode, excess channels need to be silenced or dropped. */
40589 ma_uint32 iChannel;
40590 for (iChannel = 0; iChannel < ma_min(pConverter->channelsIn, pConverter->channelsOut); iChannel += 1) {
40591 if (pConverter->format == ma_format_f32) {
40592 if (pConverter->weights.f32[iChannel][iChannel] == 0) {
40593 pConverter->weights.f32[iChannel][iChannel] = 1;
40594 }
40595 } else {
40596 if (pConverter->weights.s16[iChannel][iChannel] == 0) {
40597 pConverter->weights.s16[iChannel][iChannel] = ma_channel_converter_float_to_fixed(1);
40598 }
40599 }
40600 }
40601 } break;
40602
40603 case ma_channel_mix_mode_custom_weights:
40604 default:
40605 {
40606 /* Fallthrough. */
40607 } break;
40608 }
40609
40610
40611 return MA_SUCCESS;
40612}
40613
40614MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter)
40615{
40616 if (pConverter == NULL) {
40617 return;
40618 }
40619}
40620
40621static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
40622{
40623 MA_ASSERT(pConverter != NULL);
40624 MA_ASSERT(pFramesOut != NULL);
40625 MA_ASSERT(pFramesIn != NULL);
40626
40627 ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
40628 return MA_SUCCESS;
40629}
40630
40631static ma_result ma_channel_converter_process_pcm_frames__simple_shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
40632{
40633 ma_uint32 iFrame;
40634 ma_uint32 iChannelIn;
40635
40636 MA_ASSERT(pConverter != NULL);
40637 MA_ASSERT(pFramesOut != NULL);
40638 MA_ASSERT(pFramesIn != NULL);
40639 MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut);
40640
40641 switch (pConverter->format)
40642 {
40643 case ma_format_u8:
40644 {
40645 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
40646 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
40647
40648 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
40649 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40650 pFramesOutU8[pConverter->shuffleTable[iChannelIn]] = pFramesInU8[iChannelIn];
40651 }
40652
40653 pFramesOutU8 += pConverter->channelsOut;
40654 pFramesInU8 += pConverter->channelsIn;
40655 }
40656 } break;
40657
40658 case ma_format_s16:
40659 {
40660 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
40661 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
40662
40663 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
40664 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40665 pFramesOutS16[pConverter->shuffleTable[iChannelIn]] = pFramesInS16[iChannelIn];
40666 }
40667
40668 pFramesOutS16 += pConverter->channelsOut;
40669 pFramesInS16 += pConverter->channelsIn;
40670 }
40671 } break;
40672
40673 case ma_format_s24:
40674 {
40675 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
40676 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
40677
40678 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
40679 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40680 ma_uint32 iChannelOut = pConverter->shuffleTable[iChannelIn];
40681 pFramesOutS24[iChannelOut*3 + 0] = pFramesInS24[iChannelIn*3 + 0];
40682 pFramesOutS24[iChannelOut*3 + 1] = pFramesInS24[iChannelIn*3 + 1];
40683 pFramesOutS24[iChannelOut*3 + 2] = pFramesInS24[iChannelIn*3 + 2];
40684 }
40685
40686 pFramesOutS24 += pConverter->channelsOut*3;
40687 pFramesInS24 += pConverter->channelsIn*3;
40688 }
40689 } break;
40690
40691 case ma_format_s32:
40692 {
40693 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
40694 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
40695
40696 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
40697 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40698 pFramesOutS32[pConverter->shuffleTable[iChannelIn]] = pFramesInS32[iChannelIn];
40699 }
40700
40701 pFramesOutS32 += pConverter->channelsOut;
40702 pFramesInS32 += pConverter->channelsIn;
40703 }
40704 } break;
40705
40706 case ma_format_f32:
40707 {
40708 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
40709 const float* pFramesInF32 = (const float*)pFramesIn;
40710
40711 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
40712 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40713 pFramesOutF32[pConverter->shuffleTable[iChannelIn]] = pFramesInF32[iChannelIn];
40714 }
40715
40716 pFramesOutF32 += pConverter->channelsOut;
40717 pFramesInF32 += pConverter->channelsIn;
40718 }
40719 } break;
40720
40721 default: return MA_INVALID_OPERATION; /* Unknown format. */
40722 }
40723
40724 return MA_SUCCESS;
40725}
40726
40727static ma_result ma_channel_converter_process_pcm_frames__simple_mono_expansion(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
40728{
40729 ma_uint64 iFrame;
40730
40731 MA_ASSERT(pConverter != NULL);
40732 MA_ASSERT(pFramesOut != NULL);
40733 MA_ASSERT(pFramesIn != NULL);
40734 MA_ASSERT(pConverter->channelsIn == 1);
40735
40736 switch (pConverter->format)
40737 {
40738 case ma_format_u8:
40739 {
40740 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
40741 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
40742
40743 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40744 ma_uint32 iChannel;
40745 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
40746 pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame];
40747 }
40748 }
40749 } break;
40750
40751 case ma_format_s16:
40752 {
40753 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
40754 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
40755
40756 if (pConverter->channelsOut == 2) {
40757 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40758 pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame];
40759 pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame];
40760 }
40761 } else {
40762 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40763 ma_uint32 iChannel;
40764 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
40765 pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame];
40766 }
40767 }
40768 }
40769 } break;
40770
40771 case ma_format_s24:
40772 {
40773 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
40774 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
40775
40776 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40777 ma_uint32 iChannel;
40778 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
40779 ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel;
40780 ma_uint64 iSampleIn = iFrame;
40781 pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0];
40782 pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1];
40783 pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2];
40784 }
40785 }
40786 } break;
40787
40788 case ma_format_s32:
40789 {
40790 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
40791 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
40792
40793 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40794 ma_uint32 iChannel;
40795 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
40796 pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame];
40797 }
40798 }
40799 } break;
40800
40801 case ma_format_f32:
40802 {
40803 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
40804 const float* pFramesInF32 = (const float*)pFramesIn;
40805
40806 if (pConverter->channelsOut == 2) {
40807 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40808 pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame];
40809 pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame];
40810 }
40811 } else {
40812 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40813 ma_uint32 iChannel;
40814 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
40815 pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame];
40816 }
40817 }
40818 }
40819 } break;
40820
40821 default: return MA_INVALID_OPERATION; /* Unknown format. */
40822 }
40823
40824 return MA_SUCCESS;
40825}
40826
40827static ma_result ma_channel_converter_process_pcm_frames__stereo_to_mono(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
40828{
40829 ma_uint64 iFrame;
40830
40831 MA_ASSERT(pConverter != NULL);
40832 MA_ASSERT(pFramesOut != NULL);
40833 MA_ASSERT(pFramesIn != NULL);
40834 MA_ASSERT(pConverter->channelsIn == 2);
40835 MA_ASSERT(pConverter->channelsOut == 1);
40836
40837 switch (pConverter->format)
40838 {
40839 case ma_format_u8:
40840 {
40841 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
40842 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
40843
40844 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40845 pFramesOutU8[iFrame] = ma_clip_u8((ma_int16)((ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*2+0]) + ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*2+1])) / 2));
40846 }
40847 } break;
40848
40849 case ma_format_s16:
40850 {
40851 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
40852 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
40853
40854 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40855 pFramesOutS16[iFrame] = (ma_int16)(((ma_int32)pFramesInS16[iFrame*2+0] + (ma_int32)pFramesInS16[iFrame*2+1]) / 2);
40856 }
40857 } break;
40858
40859 case ma_format_s24:
40860 {
40861 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
40862 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
40863
40864 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40865 ma_int64 s24_0 = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*2+0)*3]);
40866 ma_int64 s24_1 = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*2+1)*3]);
40867 ma_pcm_sample_s32_to_s24_no_scale((s24_0 + s24_1) / 2, &pFramesOutS24[iFrame*3]);
40868 }
40869 } break;
40870
40871 case ma_format_s32:
40872 {
40873 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
40874 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
40875
40876 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40877 pFramesOutS32[iFrame] = (ma_int16)(((ma_int32)pFramesInS32[iFrame*2+0] + (ma_int32)pFramesInS32[iFrame*2+1]) / 2);
40878 }
40879 } break;
40880
40881 case ma_format_f32:
40882 {
40883 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
40884 const float* pFramesInF32 = (const float*)pFramesIn;
40885
40886 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
40887 pFramesOutF32[iFrame] = (pFramesInF32[iFrame*2+0] + pFramesInF32[iFrame*2+1]) * 0.5f;
40888 }
40889 } break;
40890
40891 default: return MA_INVALID_OPERATION; /* Unknown format. */
40892 }
40893
40894 return MA_SUCCESS;
40895}
40896
40897static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
40898{
40899 ma_uint32 iFrame;
40900 ma_uint32 iChannelIn;
40901 ma_uint32 iChannelOut;
40902
40903 MA_ASSERT(pConverter != NULL);
40904 MA_ASSERT(pFramesOut != NULL);
40905 MA_ASSERT(pFramesIn != NULL);
40906
40907 /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */
40908
40909 /* Clear. */
40910 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
40911
40912 /* Accumulate. */
40913 switch (pConverter->format)
40914 {
40915 case ma_format_u8:
40916 {
40917 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
40918 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
40919
40920 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
40921 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40922 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40923 ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]);
40924 ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn + iChannelIn ]);
40925 ma_int32 s = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127);
40926 pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s);
40927 }
40928 }
40929 }
40930 } break;
40931
40932 case ma_format_s16:
40933 {
40934 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
40935 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
40936
40937 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
40938 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40939 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40940 ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut];
40941 s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
40942
40943 pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767);
40944 }
40945 }
40946 }
40947 } break;
40948
40949 case ma_format_s24:
40950 {
40951 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
40952 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
40953
40954 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
40955 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40956 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40957 ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]);
40958 ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn + iChannelIn )*3]);
40959 ma_int64 s24 = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607);
40960 ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]);
40961 }
40962 }
40963 }
40964 } break;
40965
40966 case ma_format_s32:
40967 {
40968 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
40969 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
40970
40971 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
40972 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40973 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40974 ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut];
40975 s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
40976
40977 pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s);
40978 }
40979 }
40980 }
40981 } break;
40982
40983 case ma_format_f32:
40984 {
40985 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
40986 const float* pFramesInF32 = (const float*)pFramesIn;
40987
40988 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
40989 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
40990 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
40991 pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut];
40992 }
40993 }
40994 }
40995 } break;
40996
40997 default: return MA_INVALID_OPERATION; /* Unknown format. */
40998 }
40999
41000 return MA_SUCCESS;
41001}
41002
41003MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
41004{
41005 if (pConverter == NULL) {
41006 return MA_INVALID_ARGS;
41007 }
41008
41009 if (pFramesOut == NULL) {
41010 return MA_INVALID_ARGS;
41011 }
41012
41013 if (pFramesIn == NULL) {
41014 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
41015 return MA_SUCCESS;
41016 }
41017
41018 if (pConverter->isPassthrough) {
41019 return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount);
41020 } else if (pConverter->isSimpleShuffle) {
41021 return ma_channel_converter_process_pcm_frames__simple_shuffle(pConverter, pFramesOut, pFramesIn, frameCount);
41022 } else if (pConverter->isSimpleMonoExpansion) {
41023 return ma_channel_converter_process_pcm_frames__simple_mono_expansion(pConverter, pFramesOut, pFramesIn, frameCount);
41024 } else if (pConverter->isStereoToMono) {
41025 return ma_channel_converter_process_pcm_frames__stereo_to_mono(pConverter, pFramesOut, pFramesIn, frameCount);
41026 } else {
41027 return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount);
41028 }
41029}
41030
41031
41032/**************************************************************************************************************************************************************
41033
41034Data Conversion
41035
41036**************************************************************************************************************************************************************/
41037MA_API ma_data_converter_config ma_data_converter_config_init_default()
41038{
41039 ma_data_converter_config config;
41040 MA_ZERO_OBJECT(&config);
41041
41042 config.ditherMode = ma_dither_mode_none;
41043 config.resampling.algorithm = ma_resample_algorithm_linear;
41044 config.resampling.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. */
41045
41046 /* Linear resampling defaults. */
41047 config.resampling.linear.lpfOrder = 1;
41048 config.resampling.linear.lpfNyquistFactor = 1;
41049
41050 /* Speex resampling defaults. */
41051 config.resampling.speex.quality = 3;
41052
41053 return config;
41054}
41055
41056MA_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)
41057{
41058 ma_data_converter_config config = ma_data_converter_config_init_default();
41059 config.formatIn = formatIn;
41060 config.formatOut = formatOut;
41061 config.channelsIn = ma_min(channelsIn, MA_MAX_CHANNELS);
41062 config.channelsOut = ma_min(channelsOut, MA_MAX_CHANNELS);
41063 config.sampleRateIn = sampleRateIn;
41064 config.sampleRateOut = sampleRateOut;
41065
41066 return config;
41067}
41068
41069MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, ma_data_converter* pConverter)
41070{
41071 ma_result result;
41072 ma_format midFormat;
41073
41074 if (pConverter == NULL) {
41075 return MA_INVALID_ARGS;
41076 }
41077
41078 MA_ZERO_OBJECT(pConverter);
41079
41080 if (pConfig == NULL) {
41081 return MA_INVALID_ARGS;
41082 }
41083
41084 pConverter->config = *pConfig;
41085
41086 /* Basic validation. */
41087 if (pConfig->channelsIn < MA_MIN_CHANNELS || pConfig->channelsOut < MA_MIN_CHANNELS ||
41088 pConfig->channelsIn > MA_MAX_CHANNELS || pConfig->channelsOut > MA_MAX_CHANNELS) {
41089 return MA_INVALID_ARGS;
41090 }
41091
41092 /*
41093 We want to avoid as much data conversion as possible. The channel converter and resampler both support s16 and f32 natively. We need to decide
41094 on the format to use for this stage. We call this the mid format because it's used in the middle stage of the conversion pipeline. If the output
41095 format is either s16 or f32 we use that one. If that is not the case it will do the same thing for the input format. If it's neither we just
41096 use f32.
41097 */
41098 /* */ if (pConverter->config.formatOut == ma_format_s16 || pConverter->config.formatOut == ma_format_f32) {
41099 midFormat = pConverter->config.formatOut;
41100 } else if (pConverter->config.formatIn == ma_format_s16 || pConverter->config.formatIn == ma_format_f32) {
41101 midFormat = pConverter->config.formatIn;
41102 } else {
41103 midFormat = ma_format_f32;
41104 }
41105
41106 /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */
41107 {
41108 ma_uint32 iChannelIn;
41109 ma_uint32 iChannelOut;
41110 ma_channel_converter_config channelConverterConfig;
41111
41112 channelConverterConfig = ma_channel_converter_config_init(midFormat, pConverter->config.channelsIn, pConverter->config.channelMapIn, pConverter->config.channelsOut, pConverter->config.channelMapOut, pConverter->config.channelMixMode);
41113
41114 /* Channel weights. */
41115 for (iChannelIn = 0; iChannelIn < pConverter->config.channelsIn; iChannelIn += 1) {
41116 for (iChannelOut = 0; iChannelOut < pConverter->config.channelsOut; iChannelOut += 1) {
41117 channelConverterConfig.weights[iChannelIn][iChannelOut] = pConverter->config.channelWeights[iChannelIn][iChannelOut];
41118 }
41119 }
41120
41121 result = ma_channel_converter_init(&channelConverterConfig, &pConverter->channelConverter);
41122 if (result != MA_SUCCESS) {
41123 return result;
41124 }
41125
41126 /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */
41127 if (pConverter->channelConverter.isPassthrough == MA_FALSE) {
41128 pConverter->hasChannelConverter = MA_TRUE;
41129 }
41130 }
41131
41132
41133 /* Always enable dynamic sample rates if the input sample rate is different because we're always going to need a resampler in this case anyway. */
41134 if (pConverter->config.resampling.allowDynamicSampleRate == MA_FALSE) {
41135 pConverter->config.resampling.allowDynamicSampleRate = pConverter->config.sampleRateIn != pConverter->config.sampleRateOut;
41136 }
41137
41138 /* Resampler. */
41139 if (pConverter->config.resampling.allowDynamicSampleRate) {
41140 ma_resampler_config resamplerConfig;
41141 ma_uint32 resamplerChannels;
41142
41143 /* 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. */
41144 if (pConverter->config.channelsIn < pConverter->config.channelsOut) {
41145 resamplerChannels = pConverter->config.channelsIn;
41146 } else {
41147 resamplerChannels = pConverter->config.channelsOut;
41148 }
41149
41150 resamplerConfig = ma_resampler_config_init(midFormat, resamplerChannels, pConverter->config.sampleRateIn, pConverter->config.sampleRateOut, pConverter->config.resampling.algorithm);
41151 resamplerConfig.linear.lpfOrder = pConverter->config.resampling.linear.lpfOrder;
41152 resamplerConfig.linear.lpfNyquistFactor = pConverter->config.resampling.linear.lpfNyquistFactor;
41153 resamplerConfig.speex.quality = pConverter->config.resampling.speex.quality;
41154
41155 result = ma_resampler_init(&resamplerConfig, &pConverter->resampler);
41156 if (result != MA_SUCCESS) {
41157 return result;
41158 }
41159
41160 pConverter->hasResampler = MA_TRUE;
41161 }
41162
41163
41164 /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */
41165 if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) {
41166 /* 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. */
41167 if (pConverter->config.formatIn == pConverter->config.formatOut) {
41168 /* The formats are the same so we can just pass through. */
41169 pConverter->hasPreFormatConversion = MA_FALSE;
41170 pConverter->hasPostFormatConversion = MA_FALSE;
41171 } else {
41172 /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */
41173 pConverter->hasPreFormatConversion = MA_FALSE;
41174 pConverter->hasPostFormatConversion = MA_TRUE;
41175 }
41176 } else {
41177 /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */
41178 if (pConverter->config.formatIn != midFormat) {
41179 pConverter->hasPreFormatConversion = MA_TRUE;
41180 }
41181 if (pConverter->config.formatOut != midFormat) {
41182 pConverter->hasPostFormatConversion = MA_TRUE;
41183 }
41184 }
41185
41186 /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */
41187 if (pConverter->hasPreFormatConversion == MA_FALSE &&
41188 pConverter->hasPostFormatConversion == MA_FALSE &&
41189 pConverter->hasChannelConverter == MA_FALSE &&
41190 pConverter->hasResampler == MA_FALSE) {
41191 pConverter->isPassthrough = MA_TRUE;
41192 }
41193
41194 return MA_SUCCESS;
41195}
41196
41197MA_API void ma_data_converter_uninit(ma_data_converter* pConverter)
41198{
41199 if (pConverter == NULL) {
41200 return;
41201 }
41202
41203 if (pConverter->hasResampler) {
41204 ma_resampler_uninit(&pConverter->resampler);
41205 }
41206}
41207
41208static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
41209{
41210 ma_uint64 frameCountIn;
41211 ma_uint64 frameCountOut;
41212 ma_uint64 frameCount;
41213
41214 MA_ASSERT(pConverter != NULL);
41215
41216 frameCountIn = 0;
41217 if (pFrameCountIn != NULL) {
41218 frameCountIn = *pFrameCountIn;
41219 }
41220
41221 frameCountOut = 0;
41222 if (pFrameCountOut != NULL) {
41223 frameCountOut = *pFrameCountOut;
41224 }
41225
41226 frameCount = ma_min(frameCountIn, frameCountOut);
41227
41228 if (pFramesOut != NULL) {
41229 if (pFramesIn != NULL) {
41230 ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
41231 } else {
41232 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
41233 }
41234 }
41235
41236 if (pFrameCountIn != NULL) {
41237 *pFrameCountIn = frameCount;
41238 }
41239 if (pFrameCountOut != NULL) {
41240 *pFrameCountOut = frameCount;
41241 }
41242
41243 return MA_SUCCESS;
41244}
41245
41246static 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)
41247{
41248 ma_uint64 frameCountIn;
41249 ma_uint64 frameCountOut;
41250 ma_uint64 frameCount;
41251
41252 MA_ASSERT(pConverter != NULL);
41253
41254 frameCountIn = 0;
41255 if (pFrameCountIn != NULL) {
41256 frameCountIn = *pFrameCountIn;
41257 }
41258
41259 frameCountOut = 0;
41260 if (pFrameCountOut != NULL) {
41261 frameCountOut = *pFrameCountOut;
41262 }
41263
41264 frameCount = ma_min(frameCountIn, frameCountOut);
41265
41266 if (pFramesOut != NULL) {
41267 if (pFramesIn != NULL) {
41268 ma_convert_pcm_frames_format(pFramesOut, pConverter->config.formatOut, pFramesIn, pConverter->config.formatIn, frameCount, pConverter->config.channelsIn, pConverter->config.ditherMode);
41269 } else {
41270 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
41271 }
41272 }
41273
41274 if (pFrameCountIn != NULL) {
41275 *pFrameCountIn = frameCount;
41276 }
41277 if (pFrameCountOut != NULL) {
41278 *pFrameCountOut = frameCount;
41279 }
41280
41281 return MA_SUCCESS;
41282}
41283
41284
41285static 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)
41286{
41287 ma_result result = MA_SUCCESS;
41288 ma_uint64 frameCountIn;
41289 ma_uint64 frameCountOut;
41290 ma_uint64 framesProcessedIn;
41291 ma_uint64 framesProcessedOut;
41292
41293 MA_ASSERT(pConverter != NULL);
41294
41295 frameCountIn = 0;
41296 if (pFrameCountIn != NULL) {
41297 frameCountIn = *pFrameCountIn;
41298 }
41299
41300 frameCountOut = 0;
41301 if (pFrameCountOut != NULL) {
41302 frameCountOut = *pFrameCountOut;
41303 }
41304
41305 framesProcessedIn = 0;
41306 framesProcessedOut = 0;
41307
41308 while (framesProcessedOut < frameCountOut) {
41309 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
41310 const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
41311 const void* pFramesInThisIteration;
41312 /* */ void* pFramesOutThisIteration;
41313 ma_uint64 frameCountInThisIteration;
41314 ma_uint64 frameCountOutThisIteration;
41315
41316 if (pFramesIn != NULL) {
41317 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn));
41318 } else {
41319 pFramesInThisIteration = NULL;
41320 }
41321
41322 if (pFramesOut != NULL) {
41323 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
41324 } else {
41325 pFramesOutThisIteration = NULL;
41326 }
41327
41328 /* Do a pre format conversion if necessary. */
41329 if (pConverter->hasPreFormatConversion) {
41330 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
41331 const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
41332
41333 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
41334 if (frameCountInThisIteration > tempBufferInCap) {
41335 frameCountInThisIteration = tempBufferInCap;
41336 }
41337
41338 if (pConverter->hasPostFormatConversion) {
41339 if (frameCountInThisIteration > tempBufferOutCap) {
41340 frameCountInThisIteration = tempBufferOutCap;
41341 }
41342 }
41343
41344 if (pFramesInThisIteration != NULL) {
41345 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.config.format, pFramesInThisIteration, pConverter->config.formatIn, frameCountInThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode);
41346 } else {
41347 MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
41348 }
41349
41350 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
41351
41352 if (pConverter->hasPostFormatConversion) {
41353 /* Both input and output conversion required. Output to the temp buffer. */
41354 if (frameCountOutThisIteration > tempBufferOutCap) {
41355 frameCountOutThisIteration = tempBufferOutCap;
41356 }
41357
41358 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
41359 } else {
41360 /* Only pre-format required. Output straight to the output buffer. */
41361 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration);
41362 }
41363
41364 if (result != MA_SUCCESS) {
41365 break;
41366 }
41367 } else {
41368 /* No pre-format required. Just read straight from the input buffer. */
41369 MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
41370
41371 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
41372 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
41373 if (frameCountOutThisIteration > tempBufferOutCap) {
41374 frameCountOutThisIteration = tempBufferOutCap;
41375 }
41376
41377 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
41378 if (result != MA_SUCCESS) {
41379 break;
41380 }
41381 }
41382
41383 /* If we are doing a post format conversion we need to do that now. */
41384 if (pConverter->hasPostFormatConversion) {
41385 if (pFramesOutThisIteration != NULL) {
41386 ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->config.formatOut, pTempBufferOut, pConverter->resampler.config.format, frameCountOutThisIteration, pConverter->resampler.config.channels, pConverter->config.ditherMode);
41387 }
41388 }
41389
41390 framesProcessedIn += frameCountInThisIteration;
41391 framesProcessedOut += frameCountOutThisIteration;
41392
41393 MA_ASSERT(framesProcessedIn <= frameCountIn);
41394 MA_ASSERT(framesProcessedOut <= frameCountOut);
41395
41396 if (frameCountOutThisIteration == 0) {
41397 break; /* Consumed all of our input data. */
41398 }
41399 }
41400
41401 if (pFrameCountIn != NULL) {
41402 *pFrameCountIn = framesProcessedIn;
41403 }
41404 if (pFrameCountOut != NULL) {
41405 *pFrameCountOut = framesProcessedOut;
41406 }
41407
41408 return result;
41409}
41410
41411static 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)
41412{
41413 MA_ASSERT(pConverter != NULL);
41414
41415 if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
41416 /* Neither pre- nor post-format required. This is simple case where only resampling is required. */
41417 return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
41418 } else {
41419 /* Format conversion required. */
41420 return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
41421 }
41422}
41423
41424static 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)
41425{
41426 ma_result result;
41427 ma_uint64 frameCountIn;
41428 ma_uint64 frameCountOut;
41429 ma_uint64 frameCount;
41430
41431 MA_ASSERT(pConverter != NULL);
41432
41433 frameCountIn = 0;
41434 if (pFrameCountIn != NULL) {
41435 frameCountIn = *pFrameCountIn;
41436 }
41437
41438 frameCountOut = 0;
41439 if (pFrameCountOut != NULL) {
41440 frameCountOut = *pFrameCountOut;
41441 }
41442
41443 frameCount = ma_min(frameCountIn, frameCountOut);
41444
41445 if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
41446 /* No format conversion required. */
41447 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount);
41448 if (result != MA_SUCCESS) {
41449 return result;
41450 }
41451 } else {
41452 /* Format conversion required. */
41453 ma_uint64 framesProcessed = 0;
41454
41455 while (framesProcessed < frameCount) {
41456 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
41457 const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
41458 const void* pFramesInThisIteration;
41459 /* */ void* pFramesOutThisIteration;
41460 ma_uint64 frameCountThisIteration;
41461
41462 if (pFramesIn != NULL) {
41463 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn));
41464 } else {
41465 pFramesInThisIteration = NULL;
41466 }
41467
41468 if (pFramesOut != NULL) {
41469 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
41470 } else {
41471 pFramesOutThisIteration = NULL;
41472 }
41473
41474 /* Do a pre format conversion if necessary. */
41475 if (pConverter->hasPreFormatConversion) {
41476 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
41477 const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
41478
41479 frameCountThisIteration = (frameCount - framesProcessed);
41480 if (frameCountThisIteration > tempBufferInCap) {
41481 frameCountThisIteration = tempBufferInCap;
41482 }
41483
41484 if (pConverter->hasPostFormatConversion) {
41485 if (frameCountThisIteration > tempBufferOutCap) {
41486 frameCountThisIteration = tempBufferOutCap;
41487 }
41488 }
41489
41490 if (pFramesInThisIteration != NULL) {
41491 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->config.formatIn, frameCountThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode);
41492 } else {
41493 MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
41494 }
41495
41496 if (pConverter->hasPostFormatConversion) {
41497 /* Both input and output conversion required. Output to the temp buffer. */
41498 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration);
41499 } else {
41500 /* Only pre-format required. Output straight to the output buffer. */
41501 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration);
41502 }
41503
41504 if (result != MA_SUCCESS) {
41505 break;
41506 }
41507 } else {
41508 /* No pre-format required. Just read straight from the input buffer. */
41509 MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
41510
41511 frameCountThisIteration = (frameCount - framesProcessed);
41512 if (frameCountThisIteration > tempBufferOutCap) {
41513 frameCountThisIteration = tempBufferOutCap;
41514 }
41515
41516 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration);
41517 if (result != MA_SUCCESS) {
41518 break;
41519 }
41520 }
41521
41522 /* If we are doing a post format conversion we need to do that now. */
41523 if (pConverter->hasPostFormatConversion) {
41524 if (pFramesOutThisIteration != NULL) {
41525 ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->config.formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->config.ditherMode);
41526 }
41527 }
41528
41529 framesProcessed += frameCountThisIteration;
41530 }
41531 }
41532
41533 if (pFrameCountIn != NULL) {
41534 *pFrameCountIn = frameCount;
41535 }
41536 if (pFrameCountOut != NULL) {
41537 *pFrameCountOut = frameCount;
41538 }
41539
41540 return MA_SUCCESS;
41541}
41542
41543static ma_result ma_data_converter_process_pcm_frames__resampling_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
41544{
41545 ma_result result;
41546 ma_uint64 frameCountIn;
41547 ma_uint64 frameCountOut;
41548 ma_uint64 framesProcessedIn;
41549 ma_uint64 framesProcessedOut;
41550 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
41551 ma_uint64 tempBufferInCap;
41552 ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
41553 ma_uint64 tempBufferMidCap;
41554 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
41555 ma_uint64 tempBufferOutCap;
41556
41557 MA_ASSERT(pConverter != NULL);
41558 MA_ASSERT(pConverter->resampler.config.format == pConverter->channelConverter.format);
41559 MA_ASSERT(pConverter->resampler.config.channels == pConverter->channelConverter.channelsIn);
41560 MA_ASSERT(pConverter->resampler.config.channels < pConverter->channelConverter.channelsOut);
41561
41562 frameCountIn = 0;
41563 if (pFrameCountIn != NULL) {
41564 frameCountIn = *pFrameCountIn;
41565 }
41566
41567 frameCountOut = 0;
41568 if (pFrameCountOut != NULL) {
41569 frameCountOut = *pFrameCountOut;
41570 }
41571
41572 framesProcessedIn = 0;
41573 framesProcessedOut = 0;
41574
41575 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
41576 tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
41577 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
41578
41579 while (framesProcessedOut < frameCountOut) {
41580 ma_uint64 frameCountInThisIteration;
41581 ma_uint64 frameCountOutThisIteration;
41582 const void* pRunningFramesIn = NULL;
41583 void* pRunningFramesOut = NULL;
41584 const void* pResampleBufferIn;
41585 void* pChannelsBufferOut;
41586
41587 if (pFramesIn != NULL) {
41588 pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn));
41589 }
41590 if (pFramesOut != NULL) {
41591 pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
41592 }
41593
41594 /* Run input data through the resampler and output it to the temporary buffer. */
41595 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
41596
41597 if (pConverter->hasPreFormatConversion) {
41598 if (frameCountInThisIteration > tempBufferInCap) {
41599 frameCountInThisIteration = tempBufferInCap;
41600 }
41601 }
41602
41603 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
41604 if (frameCountOutThisIteration > tempBufferMidCap) {
41605 frameCountOutThisIteration = tempBufferMidCap;
41606 }
41607
41608 /* We can't read more frames than can fit in the output buffer. */
41609 if (pConverter->hasPostFormatConversion) {
41610 if (frameCountOutThisIteration > tempBufferOutCap) {
41611 frameCountOutThisIteration = tempBufferOutCap;
41612 }
41613 }
41614
41615 /* 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. */
41616 {
41617 ma_uint64 requiredInputFrameCount = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration);
41618 if (frameCountInThisIteration > requiredInputFrameCount) {
41619 frameCountInThisIteration = requiredInputFrameCount;
41620 }
41621 }
41622
41623 if (pConverter->hasPreFormatConversion) {
41624 if (pFramesIn != NULL) {
41625 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.config.format, pRunningFramesIn, pConverter->config.formatIn, frameCountInThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode);
41626 pResampleBufferIn = pTempBufferIn;
41627 } else {
41628 pResampleBufferIn = NULL;
41629 }
41630 } else {
41631 pResampleBufferIn = pRunningFramesIn;
41632 }
41633
41634 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration);
41635 if (result != MA_SUCCESS) {
41636 return result;
41637 }
41638
41639
41640 /*
41641 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
41642 this part if we have an output buffer.
41643 */
41644 if (pFramesOut != NULL) {
41645 if (pConverter->hasPostFormatConversion) {
41646 pChannelsBufferOut = pTempBufferOut;
41647 } else {
41648 pChannelsBufferOut = pRunningFramesOut;
41649 }
41650
41651 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration);
41652 if (result != MA_SUCCESS) {
41653 return result;
41654 }
41655
41656 /* Finally we do post format conversion. */
41657 if (pConverter->hasPostFormatConversion) {
41658 ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->config.formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->config.ditherMode);
41659 }
41660 }
41661
41662
41663 framesProcessedIn += frameCountInThisIteration;
41664 framesProcessedOut += frameCountOutThisIteration;
41665
41666 MA_ASSERT(framesProcessedIn <= frameCountIn);
41667 MA_ASSERT(framesProcessedOut <= frameCountOut);
41668
41669 if (frameCountOutThisIteration == 0) {
41670 break; /* Consumed all of our input data. */
41671 }
41672 }
41673
41674 if (pFrameCountIn != NULL) {
41675 *pFrameCountIn = framesProcessedIn;
41676 }
41677 if (pFrameCountOut != NULL) {
41678 *pFrameCountOut = framesProcessedOut;
41679 }
41680
41681 return MA_SUCCESS;
41682}
41683
41684static 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)
41685{
41686 ma_result result;
41687 ma_uint64 frameCountIn;
41688 ma_uint64 frameCountOut;
41689 ma_uint64 framesProcessedIn;
41690 ma_uint64 framesProcessedOut;
41691 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
41692 ma_uint64 tempBufferInCap;
41693 ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
41694 ma_uint64 tempBufferMidCap;
41695 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
41696 ma_uint64 tempBufferOutCap;
41697
41698 MA_ASSERT(pConverter != NULL);
41699 MA_ASSERT(pConverter->resampler.config.format == pConverter->channelConverter.format);
41700 MA_ASSERT(pConverter->resampler.config.channels == pConverter->channelConverter.channelsOut);
41701 MA_ASSERT(pConverter->resampler.config.channels < pConverter->channelConverter.channelsIn);
41702
41703 frameCountIn = 0;
41704 if (pFrameCountIn != NULL) {
41705 frameCountIn = *pFrameCountIn;
41706 }
41707
41708 frameCountOut = 0;
41709 if (pFrameCountOut != NULL) {
41710 frameCountOut = *pFrameCountOut;
41711 }
41712
41713 framesProcessedIn = 0;
41714 framesProcessedOut = 0;
41715
41716 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
41717 tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
41718 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.config.format, pConverter->resampler.config.channels);
41719
41720 while (framesProcessedOut < frameCountOut) {
41721 ma_uint64 frameCountInThisIteration;
41722 ma_uint64 frameCountOutThisIteration;
41723 const void* pRunningFramesIn = NULL;
41724 void* pRunningFramesOut = NULL;
41725 const void* pChannelsBufferIn;
41726 void* pResampleBufferOut;
41727
41728 if (pFramesIn != NULL) {
41729 pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->config.formatIn, pConverter->config.channelsIn));
41730 }
41731 if (pFramesOut != NULL) {
41732 pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->config.formatOut, pConverter->config.channelsOut));
41733 }
41734
41735 /* Run input data through the channel converter and output it to the temporary buffer. */
41736 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
41737
41738 if (pConverter->hasPreFormatConversion) {
41739 if (frameCountInThisIteration > tempBufferInCap) {
41740 frameCountInThisIteration = tempBufferInCap;
41741 }
41742
41743 if (pRunningFramesIn != NULL) {
41744 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->config.formatIn, frameCountInThisIteration, pConverter->config.channelsIn, pConverter->config.ditherMode);
41745 pChannelsBufferIn = pTempBufferIn;
41746 } else {
41747 pChannelsBufferIn = NULL;
41748 }
41749 } else {
41750 pChannelsBufferIn = pRunningFramesIn;
41751 }
41752
41753 /*
41754 We can't convert more frames than will fit in the output buffer. We shouldn't actually need to do this check because the channel count is always reduced
41755 in this case which means we should always have capacity, but I'm leaving it here just for safety for future maintenance.
41756 */
41757 if (frameCountInThisIteration > tempBufferMidCap) {
41758 frameCountInThisIteration = tempBufferMidCap;
41759 }
41760
41761 /*
41762 Make sure we don't read any more input frames than we need to fill the output frame count. If we do this we will end up in a situation where we lose some
41763 input samples and will end up glitching.
41764 */
41765 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
41766 if (frameCountOutThisIteration > tempBufferMidCap) {
41767 frameCountOutThisIteration = tempBufferMidCap;
41768 }
41769
41770 if (pConverter->hasPostFormatConversion) {
41771 ma_uint64 requiredInputFrameCount;
41772
41773 if (frameCountOutThisIteration > tempBufferOutCap) {
41774 frameCountOutThisIteration = tempBufferOutCap;
41775 }
41776
41777 requiredInputFrameCount = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration);
41778 if (frameCountInThisIteration > requiredInputFrameCount) {
41779 frameCountInThisIteration = requiredInputFrameCount;
41780 }
41781 }
41782
41783 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration);
41784 if (result != MA_SUCCESS) {
41785 return result;
41786 }
41787
41788
41789 /* At this point we have converted the channels to the output channel count which we now need to resample. */
41790 if (pConverter->hasPostFormatConversion) {
41791 pResampleBufferOut = pTempBufferOut;
41792 } else {
41793 pResampleBufferOut = pRunningFramesOut;
41794 }
41795
41796 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration);
41797 if (result != MA_SUCCESS) {
41798 return result;
41799 }
41800
41801 /* Finally we can do the post format conversion. */
41802 if (pConverter->hasPostFormatConversion) {
41803 if (pRunningFramesOut != NULL) {
41804 ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->config.formatOut, pResampleBufferOut, pConverter->resampler.config.format, frameCountOutThisIteration, pConverter->config.channelsOut, pConverter->config.ditherMode);
41805 }
41806 }
41807
41808 framesProcessedIn += frameCountInThisIteration;
41809 framesProcessedOut += frameCountOutThisIteration;
41810
41811 MA_ASSERT(framesProcessedIn <= frameCountIn);
41812 MA_ASSERT(framesProcessedOut <= frameCountOut);
41813
41814 if (frameCountOutThisIteration == 0) {
41815 break; /* Consumed all of our input data. */
41816 }
41817 }
41818
41819 if (pFrameCountIn != NULL) {
41820 *pFrameCountIn = framesProcessedIn;
41821 }
41822 if (pFrameCountOut != NULL) {
41823 *pFrameCountOut = framesProcessedOut;
41824 }
41825
41826 return MA_SUCCESS;
41827}
41828
41829MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
41830{
41831 if (pConverter == NULL) {
41832 return MA_INVALID_ARGS;
41833 }
41834
41835 if (pConverter->isPassthrough) {
41836 return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
41837 }
41838
41839 /*
41840 Here is where the real work is done. Getting here means we're not using a passthrough and we need to move the data through each of the relevant stages. The order
41841 of our stages depends on the input and output channel count. If the input channels is less than the output channels we want to do sample rate conversion first so
41842 that it has less work (resampling is the most expensive part of format conversion).
41843 */
41844 if (pConverter->config.channelsIn < pConverter->config.channelsOut) {
41845 /* Do resampling first, if necessary. */
41846 MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE);
41847
41848 if (pConverter->hasResampler) {
41849 /* Resampling first. */
41850 return ma_data_converter_process_pcm_frames__resampling_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
41851 } else {
41852 /* Resampling not required. */
41853 return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
41854 }
41855 } else {
41856 /* Do channel conversion first, if necessary. */
41857 if (pConverter->hasChannelConverter) {
41858 if (pConverter->hasResampler) {
41859 /* Channel routing first. */
41860 return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
41861 } else {
41862 /* Resampling not required. */
41863 return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
41864 }
41865 } else {
41866 /* Channel routing not required. */
41867 if (pConverter->hasResampler) {
41868 /* Resampling only. */
41869 return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
41870 } else {
41871 /* No channel routing nor resampling required. Just format conversion. */
41872 return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
41873 }
41874 }
41875 }
41876}
41877
41878MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
41879{
41880 if (pConverter == NULL) {
41881 return MA_INVALID_ARGS;
41882 }
41883
41884 if (pConverter->hasResampler == MA_FALSE) {
41885 return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */
41886 }
41887
41888 return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut);
41889}
41890
41891MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut)
41892{
41893 if (pConverter == NULL) {
41894 return MA_INVALID_ARGS;
41895 }
41896
41897 if (pConverter->hasResampler == MA_FALSE) {
41898 return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */
41899 }
41900
41901 return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut);
41902}
41903
41904MA_API ma_uint64 ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount)
41905{
41906 if (pConverter == NULL) {
41907 return 0;
41908 }
41909
41910 if (pConverter->hasResampler) {
41911 return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount);
41912 } else {
41913 return outputFrameCount; /* 1:1 */
41914 }
41915}
41916
41917MA_API ma_uint64 ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount)
41918{
41919 if (pConverter == NULL) {
41920 return 0;
41921 }
41922
41923 if (pConverter->hasResampler) {
41924 return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount);
41925 } else {
41926 return inputFrameCount; /* 1:1 */
41927 }
41928}
41929
41930MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter)
41931{
41932 if (pConverter == NULL) {
41933 return 0;
41934 }
41935
41936 if (pConverter->hasResampler) {
41937 return ma_resampler_get_input_latency(&pConverter->resampler);
41938 }
41939
41940 return 0; /* No latency without a resampler. */
41941}
41942
41943MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter)
41944{
41945 if (pConverter == NULL) {
41946 return 0;
41947 }
41948
41949 if (pConverter->hasResampler) {
41950 return ma_resampler_get_output_latency(&pConverter->resampler);
41951 }
41952
41953 return 0; /* No latency without a resampler. */
41954}
41955
41956
41957
41958/**************************************************************************************************************************************************************
41959
41960Channel Maps
41961
41962**************************************************************************************************************************************************************/
41963MA_API ma_channel ma_channel_map_get_default_channel(ma_uint32 channelCount, ma_uint32 channelIndex)
41964{
41965 if (channelCount == 0 || channelIndex >= channelCount) {
41966 return MA_CHANNEL_NONE;
41967 }
41968
41969 /* 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 */
41970 switch (channelCount)
41971 {
41972 case 0: return MA_CHANNEL_NONE;
41973
41974 case 1:
41975 {
41976 return MA_CHANNEL_MONO;
41977 } break;
41978
41979 case 2:
41980 {
41981 switch (channelIndex) {
41982 case 0: return MA_CHANNEL_FRONT_LEFT;
41983 case 1: return MA_CHANNEL_FRONT_RIGHT;
41984 }
41985 } break;
41986
41987 case 3: /* No defined, but best guess. */
41988 {
41989 switch (channelIndex) {
41990 case 0: return MA_CHANNEL_FRONT_LEFT;
41991 case 1: return MA_CHANNEL_FRONT_RIGHT;
41992 case 2: return MA_CHANNEL_FRONT_CENTER;
41993 }
41994 } break;
41995
41996 case 4:
41997 {
41998 switch (channelIndex) {
41999 #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP
42000 /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */
42001 case 0: return MA_CHANNEL_FRONT_LEFT;
42002 case 1: return MA_CHANNEL_FRONT_RIGHT;
42003 case 2: return MA_CHANNEL_FRONT_CENTER;
42004 case 3: return MA_CHANNEL_BACK_CENTER;
42005 #else
42006 /* Quad. */
42007 case 0: return MA_CHANNEL_FRONT_LEFT;
42008 case 1: return MA_CHANNEL_FRONT_RIGHT;
42009 case 2: return MA_CHANNEL_BACK_LEFT;
42010 case 3: return MA_CHANNEL_BACK_RIGHT;
42011 #endif
42012 }
42013 } break;
42014
42015 case 5: /* Not defined, but best guess. */
42016 {
42017 switch (channelIndex) {
42018 case 0: return MA_CHANNEL_FRONT_LEFT;
42019 case 1: return MA_CHANNEL_FRONT_RIGHT;
42020 case 2: return MA_CHANNEL_FRONT_CENTER;
42021 case 3: return MA_CHANNEL_BACK_LEFT;
42022 case 4: return MA_CHANNEL_BACK_RIGHT;
42023 }
42024 } break;
42025
42026 case 6:
42027 {
42028 switch (channelIndex) {
42029 case 0: return MA_CHANNEL_FRONT_LEFT;
42030 case 1: return MA_CHANNEL_FRONT_RIGHT;
42031 case 2: return MA_CHANNEL_FRONT_CENTER;
42032 case 3: return MA_CHANNEL_LFE;
42033 case 4: return MA_CHANNEL_SIDE_LEFT;
42034 case 5: return MA_CHANNEL_SIDE_RIGHT;
42035 }
42036 } break;
42037
42038 case 7: /* Not defined, but best guess. */
42039 {
42040 switch (channelIndex) {
42041 case 0: return MA_CHANNEL_FRONT_LEFT;
42042 case 1: return MA_CHANNEL_FRONT_RIGHT;
42043 case 2: return MA_CHANNEL_FRONT_CENTER;
42044 case 3: return MA_CHANNEL_LFE;
42045 case 4: return MA_CHANNEL_BACK_CENTER;
42046 case 5: return MA_CHANNEL_SIDE_LEFT;
42047 case 6: return MA_CHANNEL_SIDE_RIGHT;
42048 }
42049 } break;
42050
42051 case 8:
42052 default:
42053 {
42054 switch (channelIndex) {
42055 case 0: return MA_CHANNEL_FRONT_LEFT;
42056 case 1: return MA_CHANNEL_FRONT_RIGHT;
42057 case 2: return MA_CHANNEL_FRONT_CENTER;
42058 case 3: return MA_CHANNEL_LFE;
42059 case 4: return MA_CHANNEL_BACK_LEFT;
42060 case 5: return MA_CHANNEL_BACK_RIGHT;
42061 case 6: return MA_CHANNEL_SIDE_LEFT;
42062 case 7: return MA_CHANNEL_SIDE_RIGHT;
42063 }
42064 } break;
42065 }
42066
42067 if (channelCount > 8) {
42068 if (channelIndex < 32) { /* We have 32 AUX channels. */
42069 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
42070 }
42071 }
42072
42073 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
42074 return MA_CHANNEL_NONE;
42075}
42076
42077MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)
42078{
42079 if (pChannelMap == NULL) {
42080 return ma_channel_map_get_default_channel(channelCount, channelIndex);
42081 } else {
42082 if (channelIndex >= channelCount) {
42083 return MA_CHANNEL_NONE;
42084 }
42085
42086 return pChannelMap[channelIndex];
42087 }
42088}
42089
42090
42091MA_API void ma_channel_map_init_blank(ma_uint32 channels, ma_channel* pChannelMap)
42092{
42093 if (pChannelMap == NULL) {
42094 return;
42095 }
42096
42097 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels);
42098}
42099
42100static void ma_get_standard_channel_map_microsoft(ma_uint32 channels, ma_channel* pChannelMap)
42101{
42102 /* Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */
42103 switch (channels)
42104 {
42105 case 1:
42106 {
42107 pChannelMap[0] = MA_CHANNEL_MONO;
42108 } break;
42109
42110 case 2:
42111 {
42112 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42113 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42114 } break;
42115
42116 case 3: /* Not defined, but best guess. */
42117 {
42118 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42119 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42120 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
42121 } break;
42122
42123 case 4:
42124 {
42125#ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP
42126 /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */
42127 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42128 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42129 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
42130 pChannelMap[3] = MA_CHANNEL_BACK_CENTER;
42131#else
42132 /* Quad. */
42133 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42134 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42135 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
42136 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
42137#endif
42138 } break;
42139
42140 case 5: /* Not defined, but best guess. */
42141 {
42142 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42143 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42144 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
42145 pChannelMap[3] = MA_CHANNEL_BACK_LEFT;
42146 pChannelMap[4] = MA_CHANNEL_BACK_RIGHT;
42147 } break;
42148
42149 case 6:
42150 {
42151 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42152 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42153 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
42154 pChannelMap[3] = MA_CHANNEL_LFE;
42155 pChannelMap[4] = MA_CHANNEL_SIDE_LEFT;
42156 pChannelMap[5] = MA_CHANNEL_SIDE_RIGHT;
42157 } break;
42158
42159 case 7: /* Not defined, but best guess. */
42160 {
42161 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42162 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42163 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
42164 pChannelMap[3] = MA_CHANNEL_LFE;
42165 pChannelMap[4] = MA_CHANNEL_BACK_CENTER;
42166 pChannelMap[5] = MA_CHANNEL_SIDE_LEFT;
42167 pChannelMap[6] = MA_CHANNEL_SIDE_RIGHT;
42168 } break;
42169
42170 case 8:
42171 default:
42172 {
42173 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42174 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42175 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
42176 pChannelMap[3] = MA_CHANNEL_LFE;
42177 pChannelMap[4] = MA_CHANNEL_BACK_LEFT;
42178 pChannelMap[5] = MA_CHANNEL_BACK_RIGHT;
42179 pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
42180 pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
42181 } break;
42182 }
42183
42184 /* Remainder. */
42185 if (channels > 8) {
42186 ma_uint32 iChannel;
42187 for (iChannel = 8; iChannel < channels; ++iChannel) {
42188 if (iChannel < MA_MAX_CHANNELS) {
42189 pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
42190 } else {
42191 pChannelMap[iChannel] = MA_CHANNEL_NONE;
42192 }
42193 }
42194 }
42195}
42196
42197static void ma_get_standard_channel_map_alsa(ma_uint32 channels, ma_channel* pChannelMap)
42198{
42199 switch (channels)
42200 {
42201 case 1:
42202 {
42203 pChannelMap[0] = MA_CHANNEL_MONO;
42204 } break;
42205
42206 case 2:
42207 {
42208 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42209 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42210 } break;
42211
42212 case 3:
42213 {
42214 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42215 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42216 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
42217 } break;
42218
42219 case 4:
42220 {
42221 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42222 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42223 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
42224 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
42225 } break;
42226
42227 case 5:
42228 {
42229 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42230 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42231 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
42232 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
42233 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
42234 } break;
42235
42236 case 6:
42237 {
42238 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42239 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42240 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
42241 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
42242 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
42243 pChannelMap[5] = MA_CHANNEL_LFE;
42244 } break;
42245
42246 case 7:
42247 {
42248 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42249 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42250 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
42251 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
42252 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
42253 pChannelMap[5] = MA_CHANNEL_LFE;
42254 pChannelMap[6] = MA_CHANNEL_BACK_CENTER;
42255 } break;
42256
42257 case 8:
42258 default:
42259 {
42260 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42261 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42262 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
42263 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
42264 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
42265 pChannelMap[5] = MA_CHANNEL_LFE;
42266 pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
42267 pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
42268 } break;
42269 }
42270
42271 /* Remainder. */
42272 if (channels > 8) {
42273 ma_uint32 iChannel;
42274 for (iChannel = 8; iChannel < channels; ++iChannel) {
42275 if (iChannel < MA_MAX_CHANNELS) {
42276 pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
42277 } else {
42278 pChannelMap[iChannel] = MA_CHANNEL_NONE;
42279 }
42280 }
42281 }
42282}
42283
42284static void ma_get_standard_channel_map_rfc3551(ma_uint32 channels, ma_channel* pChannelMap)
42285{
42286 switch (channels)
42287 {
42288 case 1:
42289 {
42290 pChannelMap[0] = MA_CHANNEL_MONO;
42291 } break;
42292
42293 case 2:
42294 {
42295 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42296 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42297 } break;
42298
42299 case 3:
42300 {
42301 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42302 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42303 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
42304 } break;
42305
42306 case 4:
42307 {
42308 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42309 pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
42310 pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
42311 pChannelMap[3] = MA_CHANNEL_BACK_CENTER;
42312 } break;
42313
42314 case 5:
42315 {
42316 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42317 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42318 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
42319 pChannelMap[3] = MA_CHANNEL_BACK_LEFT;
42320 pChannelMap[4] = MA_CHANNEL_BACK_RIGHT;
42321 } break;
42322
42323 case 6:
42324 {
42325 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42326 pChannelMap[1] = MA_CHANNEL_SIDE_LEFT;
42327 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
42328 pChannelMap[3] = MA_CHANNEL_FRONT_RIGHT;
42329 pChannelMap[4] = MA_CHANNEL_SIDE_RIGHT;
42330 pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
42331 } break;
42332 }
42333
42334 /* Remainder. */
42335 if (channels > 8) {
42336 ma_uint32 iChannel;
42337 for (iChannel = 6; iChannel < channels; ++iChannel) {
42338 if (iChannel < MA_MAX_CHANNELS) {
42339 pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6));
42340 } else {
42341 pChannelMap[iChannel] = MA_CHANNEL_NONE;
42342 }
42343 }
42344 }
42345}
42346
42347static void ma_get_standard_channel_map_flac(ma_uint32 channels, ma_channel* pChannelMap)
42348{
42349 switch (channels)
42350 {
42351 case 1:
42352 {
42353 pChannelMap[0] = MA_CHANNEL_MONO;
42354 } break;
42355
42356 case 2:
42357 {
42358 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42359 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42360 } break;
42361
42362 case 3:
42363 {
42364 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42365 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42366 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
42367 } break;
42368
42369 case 4:
42370 {
42371 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42372 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42373 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
42374 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
42375 } break;
42376
42377 case 5:
42378 {
42379 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42380 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42381 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
42382 pChannelMap[3] = MA_CHANNEL_BACK_LEFT;
42383 pChannelMap[4] = MA_CHANNEL_BACK_RIGHT;
42384 } break;
42385
42386 case 6:
42387 {
42388 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42389 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42390 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
42391 pChannelMap[3] = MA_CHANNEL_LFE;
42392 pChannelMap[4] = MA_CHANNEL_BACK_LEFT;
42393 pChannelMap[5] = MA_CHANNEL_BACK_RIGHT;
42394 } break;
42395
42396 case 7:
42397 {
42398 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42399 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42400 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
42401 pChannelMap[3] = MA_CHANNEL_LFE;
42402 pChannelMap[4] = MA_CHANNEL_BACK_CENTER;
42403 pChannelMap[5] = MA_CHANNEL_SIDE_LEFT;
42404 pChannelMap[6] = MA_CHANNEL_SIDE_RIGHT;
42405 } break;
42406
42407 case 8:
42408 default:
42409 {
42410 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42411 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42412 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
42413 pChannelMap[3] = MA_CHANNEL_LFE;
42414 pChannelMap[4] = MA_CHANNEL_BACK_LEFT;
42415 pChannelMap[5] = MA_CHANNEL_BACK_RIGHT;
42416 pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
42417 pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
42418 } break;
42419 }
42420
42421 /* Remainder. */
42422 if (channels > 8) {
42423 ma_uint32 iChannel;
42424 for (iChannel = 8; iChannel < channels; ++iChannel) {
42425 if (iChannel < MA_MAX_CHANNELS) {
42426 pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
42427 } else {
42428 pChannelMap[iChannel] = MA_CHANNEL_NONE;
42429 }
42430 }
42431 }
42432}
42433
42434static void ma_get_standard_channel_map_vorbis(ma_uint32 channels, ma_channel* pChannelMap)
42435{
42436 /* In Vorbis' type 0 channel mapping, the first two channels are not always the standard left/right - it will have the center speaker where the right usually goes. Why?! */
42437 switch (channels)
42438 {
42439 case 1:
42440 {
42441 pChannelMap[0] = MA_CHANNEL_MONO;
42442 } break;
42443
42444 case 2:
42445 {
42446 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42447 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42448 } break;
42449
42450 case 3:
42451 {
42452 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42453 pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
42454 pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
42455 } break;
42456
42457 case 4:
42458 {
42459 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42460 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42461 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
42462 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
42463 } break;
42464
42465 case 5:
42466 {
42467 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42468 pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
42469 pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
42470 pChannelMap[3] = MA_CHANNEL_BACK_LEFT;
42471 pChannelMap[4] = MA_CHANNEL_BACK_RIGHT;
42472 } break;
42473
42474 case 6:
42475 {
42476 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42477 pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
42478 pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
42479 pChannelMap[3] = MA_CHANNEL_BACK_LEFT;
42480 pChannelMap[4] = MA_CHANNEL_BACK_RIGHT;
42481 pChannelMap[5] = MA_CHANNEL_LFE;
42482 } break;
42483
42484 case 7:
42485 {
42486 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42487 pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
42488 pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
42489 pChannelMap[3] = MA_CHANNEL_SIDE_LEFT;
42490 pChannelMap[4] = MA_CHANNEL_SIDE_RIGHT;
42491 pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
42492 pChannelMap[6] = MA_CHANNEL_LFE;
42493 } break;
42494
42495 case 8:
42496 default:
42497 {
42498 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42499 pChannelMap[1] = MA_CHANNEL_FRONT_CENTER;
42500 pChannelMap[2] = MA_CHANNEL_FRONT_RIGHT;
42501 pChannelMap[3] = MA_CHANNEL_SIDE_LEFT;
42502 pChannelMap[4] = MA_CHANNEL_SIDE_RIGHT;
42503 pChannelMap[5] = MA_CHANNEL_BACK_LEFT;
42504 pChannelMap[6] = MA_CHANNEL_BACK_RIGHT;
42505 pChannelMap[7] = MA_CHANNEL_LFE;
42506 } break;
42507 }
42508
42509 /* Remainder. */
42510 if (channels > 8) {
42511 ma_uint32 iChannel;
42512 for (iChannel = 8; iChannel < channels; ++iChannel) {
42513 if (iChannel < MA_MAX_CHANNELS) {
42514 pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
42515 } else {
42516 pChannelMap[iChannel] = MA_CHANNEL_NONE;
42517 }
42518 }
42519 }
42520}
42521
42522static void ma_get_standard_channel_map_sound4(ma_uint32 channels, ma_channel* pChannelMap)
42523{
42524 switch (channels)
42525 {
42526 case 1:
42527 {
42528 pChannelMap[0] = MA_CHANNEL_MONO;
42529 } break;
42530
42531 case 2:
42532 {
42533 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42534 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42535 } break;
42536
42537 case 3:
42538 {
42539 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42540 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42541 pChannelMap[2] = MA_CHANNEL_BACK_CENTER;
42542 } break;
42543
42544 case 4:
42545 {
42546 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42547 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42548 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
42549 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
42550 } break;
42551
42552 case 5:
42553 {
42554 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42555 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42556 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
42557 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
42558 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
42559 } break;
42560
42561 case 6:
42562 {
42563 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42564 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42565 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
42566 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
42567 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
42568 pChannelMap[5] = MA_CHANNEL_LFE;
42569 } break;
42570
42571 case 7:
42572 {
42573 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42574 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42575 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
42576 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
42577 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
42578 pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
42579 pChannelMap[6] = MA_CHANNEL_LFE;
42580 } break;
42581
42582 case 8:
42583 default:
42584 {
42585 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42586 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42587 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
42588 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
42589 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
42590 pChannelMap[5] = MA_CHANNEL_LFE;
42591 pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
42592 pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
42593 } break;
42594 }
42595
42596 /* Remainder. */
42597 if (channels > 8) {
42598 ma_uint32 iChannel;
42599 for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
42600 if (iChannel < MA_MAX_CHANNELS) {
42601 pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
42602 } else {
42603 pChannelMap[iChannel] = MA_CHANNEL_NONE;
42604 }
42605 }
42606 }
42607}
42608
42609static void ma_get_standard_channel_map_sndio(ma_uint32 channels, ma_channel* pChannelMap)
42610{
42611 switch (channels)
42612 {
42613 case 1:
42614 {
42615 pChannelMap[0] = MA_CHANNEL_MONO;
42616 } break;
42617
42618 case 2:
42619 {
42620 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42621 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42622 } break;
42623
42624 case 3:
42625 {
42626 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42627 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42628 pChannelMap[2] = MA_CHANNEL_FRONT_CENTER;
42629 } break;
42630
42631 case 4:
42632 {
42633 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42634 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42635 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
42636 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
42637 } break;
42638
42639 case 5:
42640 {
42641 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42642 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42643 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
42644 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
42645 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
42646 } break;
42647
42648 case 6:
42649 default:
42650 {
42651 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
42652 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
42653 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
42654 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
42655 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
42656 pChannelMap[5] = MA_CHANNEL_LFE;
42657 } break;
42658 }
42659
42660 /* Remainder. */
42661 if (channels > 6) {
42662 ma_uint32 iChannel;
42663 for (iChannel = 6; iChannel < channels && iChannel < MA_MAX_CHANNELS; ++iChannel) {
42664 if (iChannel < MA_MAX_CHANNELS) {
42665 pChannelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6));
42666 } else {
42667 pChannelMap[iChannel] = MA_CHANNEL_NONE;
42668 }
42669 }
42670 }
42671}
42672
42673MA_API void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel* pChannelMap)
42674{
42675 switch (standardChannelMap)
42676 {
42677 case ma_standard_channel_map_alsa:
42678 {
42679 ma_get_standard_channel_map_alsa(channels, pChannelMap);
42680 } break;
42681
42682 case ma_standard_channel_map_rfc3551:
42683 {
42684 ma_get_standard_channel_map_rfc3551(channels, pChannelMap);
42685 } break;
42686
42687 case ma_standard_channel_map_flac:
42688 {
42689 ma_get_standard_channel_map_flac(channels, pChannelMap);
42690 } break;
42691
42692 case ma_standard_channel_map_vorbis:
42693 {
42694 ma_get_standard_channel_map_vorbis(channels, pChannelMap);
42695 } break;
42696
42697 case ma_standard_channel_map_sound4:
42698 {
42699 ma_get_standard_channel_map_sound4(channels, pChannelMap);
42700 } break;
42701
42702 case ma_standard_channel_map_sndio:
42703 {
42704 ma_get_standard_channel_map_sndio(channels, pChannelMap);
42705 } break;
42706
42707 case ma_standard_channel_map_microsoft: /* Also default. */
42708 /*case ma_standard_channel_map_default;*/
42709 default:
42710 {
42711 ma_get_standard_channel_map_microsoft(channels, pChannelMap);
42712 } break;
42713 }
42714}
42715
42716MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels)
42717{
42718 if (pOut != NULL && pIn != NULL && channels > 0) {
42719 MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels);
42720 }
42721}
42722
42723MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels)
42724{
42725 if (pOut == NULL || channels == 0) {
42726 return;
42727 }
42728
42729 if (pIn != NULL) {
42730 ma_channel_map_copy(pOut, pIn, channels);
42731 } else {
42732 ma_get_standard_channel_map(ma_standard_channel_map_default, channels, pOut);
42733 }
42734}
42735
42736MA_API ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel* pChannelMap)
42737{
42738 if (pChannelMap == NULL) {
42739 return MA_FALSE;
42740 }
42741
42742 /* A channel count of 0 is invalid. */
42743 if (channels == 0) {
42744 return MA_FALSE;
42745 }
42746
42747 /* It does not make sense to have a mono channel when there is more than 1 channel. */
42748 if (channels > 1) {
42749 ma_uint32 iChannel;
42750 for (iChannel = 0; iChannel < channels; ++iChannel) {
42751 if (pChannelMap[iChannel] == MA_CHANNEL_MONO) {
42752 return MA_FALSE;
42753 }
42754 }
42755 }
42756
42757 return MA_TRUE;
42758}
42759
42760MA_API ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel* pChannelMapA, const ma_channel* pChannelMapB)
42761{
42762 ma_uint32 iChannel;
42763
42764 if (pChannelMapA == pChannelMapB) {
42765 return MA_TRUE;
42766 }
42767
42768 for (iChannel = 0; iChannel < channels; ++iChannel) {
42769 if (ma_channel_map_get_channel(pChannelMapA, channels, iChannel) != ma_channel_map_get_channel(pChannelMapB, channels, iChannel)) {
42770 return MA_FALSE;
42771 }
42772 }
42773
42774 return MA_TRUE;
42775}
42776
42777MA_API ma_bool32 ma_channel_map_blank(ma_uint32 channels, const ma_channel* pChannelMap)
42778{
42779 ma_uint32 iChannel;
42780
42781 /* A null channel map is equivalent to the default channel map. */
42782 if (pChannelMap == NULL) {
42783 return MA_FALSE;
42784 }
42785
42786 for (iChannel = 0; iChannel < channels; ++iChannel) {
42787 if (pChannelMap[iChannel] != MA_CHANNEL_NONE) {
42788 return MA_FALSE;
42789 }
42790 }
42791
42792 return MA_TRUE;
42793}
42794
42795MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition)
42796{
42797 ma_uint32 iChannel;
42798
42799 for (iChannel = 0; iChannel < channels; ++iChannel) {
42800 if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) {
42801 return MA_TRUE;
42802 }
42803 }
42804
42805 return MA_FALSE;
42806}
42807
42808
42809
42810/**************************************************************************************************************************************************************
42811
42812Conversion Helpers
42813
42814**************************************************************************************************************************************************************/
42815MA_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)
42816{
42817 ma_data_converter_config config;
42818
42819 config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut);
42820 ma_get_standard_channel_map(ma_standard_channel_map_default, channelsOut, config.channelMapOut);
42821 ma_get_standard_channel_map(ma_standard_channel_map_default, channelsIn, config.channelMapIn);
42822 config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
42823
42824 return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config);
42825}
42826
42827MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig)
42828{
42829 ma_result result;
42830 ma_data_converter converter;
42831
42832 if (frameCountIn == 0 || pConfig == NULL) {
42833 return 0;
42834 }
42835
42836 result = ma_data_converter_init(pConfig, &converter);
42837 if (result != MA_SUCCESS) {
42838 return 0; /* Failed to initialize the data converter. */
42839 }
42840
42841 if (pOut == NULL) {
42842 frameCountOut = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn);
42843 } else {
42844 result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut);
42845 if (result != MA_SUCCESS) {
42846 frameCountOut = 0;
42847 }
42848 }
42849
42850 ma_data_converter_uninit(&converter);
42851 return frameCountOut;
42852}
42853
42854
42855/**************************************************************************************************************************************************************
42856
42857Ring Buffer
42858
42859**************************************************************************************************************************************************************/
42860static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset)
42861{
42862 return encodedOffset & 0x7FFFFFFF;
42863}
42864
42865static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset)
42866{
42867 return encodedOffset & 0x80000000;
42868}
42869
42870static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB)
42871{
42872 MA_ASSERT(pRB != NULL);
42873 return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedReadOffset)));
42874}
42875
42876static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB)
42877{
42878 MA_ASSERT(pRB != NULL);
42879 return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedWriteOffset)));
42880}
42881
42882static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag)
42883{
42884 return offsetLoopFlag | offsetInBytes;
42885}
42886
42887static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag)
42888{
42889 MA_ASSERT(pOffsetInBytes != NULL);
42890 MA_ASSERT(pOffsetLoopFlag != NULL);
42891
42892 *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset);
42893 *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset);
42894}
42895
42896
42897MA_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)
42898{
42899 ma_result result;
42900 const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1);
42901
42902 if (pRB == NULL) {
42903 return MA_INVALID_ARGS;
42904 }
42905
42906 if (subbufferSizeInBytes == 0 || subbufferCount == 0) {
42907 return MA_INVALID_ARGS;
42908 }
42909
42910 if (subbufferSizeInBytes > maxSubBufferSize) {
42911 return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */
42912 }
42913
42914
42915 MA_ZERO_OBJECT(pRB);
42916
42917 result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks);
42918 if (result != MA_SUCCESS) {
42919 return result;
42920 }
42921
42922 pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes;
42923 pRB->subbufferCount = (ma_uint32)subbufferCount;
42924
42925 if (pOptionalPreallocatedBuffer != NULL) {
42926 pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes;
42927 pRB->pBuffer = pOptionalPreallocatedBuffer;
42928 } else {
42929 size_t bufferSizeInBytes;
42930
42931 /*
42932 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
42933 we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT.
42934 */
42935 pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT;
42936
42937 bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes;
42938 pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks);
42939 if (pRB->pBuffer == NULL) {
42940 return MA_OUT_OF_MEMORY;
42941 }
42942
42943 MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes);
42944 pRB->ownsBuffer = MA_TRUE;
42945 }
42946
42947 return MA_SUCCESS;
42948}
42949
42950MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB)
42951{
42952 return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
42953}
42954
42955MA_API void ma_rb_uninit(ma_rb* pRB)
42956{
42957 if (pRB == NULL) {
42958 return;
42959 }
42960
42961 if (pRB->ownsBuffer) {
42962 ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks);
42963 }
42964}
42965
42966MA_API void ma_rb_reset(ma_rb* pRB)
42967{
42968 if (pRB == NULL) {
42969 return;
42970 }
42971
42972 c89atomic_exchange_32(&pRB->encodedReadOffset, 0);
42973 c89atomic_exchange_32(&pRB->encodedWriteOffset, 0);
42974}
42975
42976MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
42977{
42978 ma_uint32 writeOffset;
42979 ma_uint32 writeOffsetInBytes;
42980 ma_uint32 writeOffsetLoopFlag;
42981 ma_uint32 readOffset;
42982 ma_uint32 readOffsetInBytes;
42983 ma_uint32 readOffsetLoopFlag;
42984 size_t bytesAvailable;
42985 size_t bytesRequested;
42986
42987 if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
42988 return MA_INVALID_ARGS;
42989 }
42990
42991 /* The returned buffer should never move ahead of the write pointer. */
42992 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
42993 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
42994
42995 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
42996 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
42997
42998 /*
42999 The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we
43000 can only read up to the write pointer. If not, we can only read up to the end of the buffer.
43001 */
43002 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
43003 bytesAvailable = writeOffsetInBytes - readOffsetInBytes;
43004 } else {
43005 bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes;
43006 }
43007
43008 bytesRequested = *pSizeInBytes;
43009 if (bytesRequested > bytesAvailable) {
43010 bytesRequested = bytesAvailable;
43011 }
43012
43013 *pSizeInBytes = bytesRequested;
43014 (*ppBufferOut) = ma_rb__get_read_ptr(pRB);
43015
43016 return MA_SUCCESS;
43017}
43018
43019MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut)
43020{
43021 ma_uint32 readOffset;
43022 ma_uint32 readOffsetInBytes;
43023 ma_uint32 readOffsetLoopFlag;
43024 ma_uint32 newReadOffsetInBytes;
43025 ma_uint32 newReadOffsetLoopFlag;
43026
43027 if (pRB == NULL) {
43028 return MA_INVALID_ARGS;
43029 }
43030
43031 /* Validate the buffer. */
43032 if (pBufferOut != ma_rb__get_read_ptr(pRB)) {
43033 return MA_INVALID_ARGS;
43034 }
43035
43036 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
43037 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
43038
43039 /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
43040 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes);
43041 if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) {
43042 return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
43043 }
43044
43045 /* Move the read pointer back to the start if necessary. */
43046 newReadOffsetLoopFlag = readOffsetLoopFlag;
43047 if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) {
43048 newReadOffsetInBytes = 0;
43049 newReadOffsetLoopFlag ^= 0x80000000;
43050 }
43051
43052 c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes));
43053
43054 if (ma_rb_pointer_distance(pRB) == 0) {
43055 return MA_AT_END;
43056 } else {
43057 return MA_SUCCESS;
43058 }
43059}
43060
43061MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
43062{
43063 ma_uint32 readOffset;
43064 ma_uint32 readOffsetInBytes;
43065 ma_uint32 readOffsetLoopFlag;
43066 ma_uint32 writeOffset;
43067 ma_uint32 writeOffsetInBytes;
43068 ma_uint32 writeOffsetLoopFlag;
43069 size_t bytesAvailable;
43070 size_t bytesRequested;
43071
43072 if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
43073 return MA_INVALID_ARGS;
43074 }
43075
43076 /* The returned buffer should never overtake the read buffer. */
43077 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
43078 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
43079
43080 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
43081 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
43082
43083 /*
43084 In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only
43085 write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should
43086 never overtake the read pointer.
43087 */
43088 if (writeOffsetLoopFlag == readOffsetLoopFlag) {
43089 bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes;
43090 } else {
43091 bytesAvailable = readOffsetInBytes - writeOffsetInBytes;
43092 }
43093
43094 bytesRequested = *pSizeInBytes;
43095 if (bytesRequested > bytesAvailable) {
43096 bytesRequested = bytesAvailable;
43097 }
43098
43099 *pSizeInBytes = bytesRequested;
43100 *ppBufferOut = ma_rb__get_write_ptr(pRB);
43101
43102 /* Clear the buffer if desired. */
43103 if (pRB->clearOnWriteAcquire) {
43104 MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes);
43105 }
43106
43107 return MA_SUCCESS;
43108}
43109
43110MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut)
43111{
43112 ma_uint32 writeOffset;
43113 ma_uint32 writeOffsetInBytes;
43114 ma_uint32 writeOffsetLoopFlag;
43115 ma_uint32 newWriteOffsetInBytes;
43116 ma_uint32 newWriteOffsetLoopFlag;
43117
43118 if (pRB == NULL) {
43119 return MA_INVALID_ARGS;
43120 }
43121
43122 /* Validate the buffer. */
43123 if (pBufferOut != ma_rb__get_write_ptr(pRB)) {
43124 return MA_INVALID_ARGS;
43125 }
43126
43127 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
43128 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
43129
43130 /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
43131 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes);
43132 if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) {
43133 return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
43134 }
43135
43136 /* Move the read pointer back to the start if necessary. */
43137 newWriteOffsetLoopFlag = writeOffsetLoopFlag;
43138 if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) {
43139 newWriteOffsetInBytes = 0;
43140 newWriteOffsetLoopFlag ^= 0x80000000;
43141 }
43142
43143 c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes));
43144
43145 if (ma_rb_pointer_distance(pRB) == 0) {
43146 return MA_AT_END;
43147 } else {
43148 return MA_SUCCESS;
43149 }
43150}
43151
43152MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes)
43153{
43154 ma_uint32 readOffset;
43155 ma_uint32 readOffsetInBytes;
43156 ma_uint32 readOffsetLoopFlag;
43157 ma_uint32 writeOffset;
43158 ma_uint32 writeOffsetInBytes;
43159 ma_uint32 writeOffsetLoopFlag;
43160 ma_uint32 newReadOffsetInBytes;
43161 ma_uint32 newReadOffsetLoopFlag;
43162
43163 if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) {
43164 return MA_INVALID_ARGS;
43165 }
43166
43167 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
43168 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
43169
43170 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
43171 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
43172
43173 newReadOffsetLoopFlag = readOffsetLoopFlag;
43174
43175 /* We cannot go past the write buffer. */
43176 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
43177 if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) {
43178 newReadOffsetInBytes = writeOffsetInBytes;
43179 } else {
43180 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
43181 }
43182 } else {
43183 /* May end up looping. */
43184 if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
43185 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
43186 newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
43187 } else {
43188 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
43189 }
43190 }
43191
43192 c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag));
43193 return MA_SUCCESS;
43194}
43195
43196MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes)
43197{
43198 ma_uint32 readOffset;
43199 ma_uint32 readOffsetInBytes;
43200 ma_uint32 readOffsetLoopFlag;
43201 ma_uint32 writeOffset;
43202 ma_uint32 writeOffsetInBytes;
43203 ma_uint32 writeOffsetLoopFlag;
43204 ma_uint32 newWriteOffsetInBytes;
43205 ma_uint32 newWriteOffsetLoopFlag;
43206
43207 if (pRB == NULL) {
43208 return MA_INVALID_ARGS;
43209 }
43210
43211 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
43212 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
43213
43214 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
43215 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
43216
43217 newWriteOffsetLoopFlag = writeOffsetLoopFlag;
43218
43219 /* We cannot go past the write buffer. */
43220 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
43221 /* May end up looping. */
43222 if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
43223 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
43224 newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
43225 } else {
43226 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
43227 }
43228 } else {
43229 if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) {
43230 newWriteOffsetInBytes = readOffsetInBytes;
43231 } else {
43232 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
43233 }
43234 }
43235
43236 c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag));
43237 return MA_SUCCESS;
43238}
43239
43240MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB)
43241{
43242 ma_uint32 readOffset;
43243 ma_uint32 readOffsetInBytes;
43244 ma_uint32 readOffsetLoopFlag;
43245 ma_uint32 writeOffset;
43246 ma_uint32 writeOffsetInBytes;
43247 ma_uint32 writeOffsetLoopFlag;
43248
43249 if (pRB == NULL) {
43250 return 0;
43251 }
43252
43253 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
43254 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
43255
43256 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
43257 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
43258
43259 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
43260 return writeOffsetInBytes - readOffsetInBytes;
43261 } else {
43262 return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes);
43263 }
43264}
43265
43266MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB)
43267{
43268 ma_int32 dist;
43269
43270 if (pRB == NULL) {
43271 return 0;
43272 }
43273
43274 dist = ma_rb_pointer_distance(pRB);
43275 if (dist < 0) {
43276 return 0;
43277 }
43278
43279 return dist;
43280}
43281
43282MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB)
43283{
43284 if (pRB == NULL) {
43285 return 0;
43286 }
43287
43288 return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB));
43289}
43290
43291MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB)
43292{
43293 if (pRB == NULL) {
43294 return 0;
43295 }
43296
43297 return pRB->subbufferSizeInBytes;
43298}
43299
43300MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB)
43301{
43302 if (pRB == NULL) {
43303 return 0;
43304 }
43305
43306 if (pRB->subbufferStrideInBytes == 0) {
43307 return (size_t)pRB->subbufferSizeInBytes;
43308 }
43309
43310 return (size_t)pRB->subbufferStrideInBytes;
43311}
43312
43313MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex)
43314{
43315 if (pRB == NULL) {
43316 return 0;
43317 }
43318
43319 return subbufferIndex * ma_rb_get_subbuffer_stride(pRB);
43320}
43321
43322MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer)
43323{
43324 if (pRB == NULL) {
43325 return NULL;
43326 }
43327
43328 return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex));
43329}
43330
43331
43332
43333static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB)
43334{
43335 MA_ASSERT(pRB != NULL);
43336
43337 return ma_get_bytes_per_frame(pRB->format, pRB->channels);
43338}
43339
43340MA_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)
43341{
43342 ma_uint32 bpf;
43343 ma_result result;
43344
43345 if (pRB == NULL) {
43346 return MA_INVALID_ARGS;
43347 }
43348
43349 MA_ZERO_OBJECT(pRB);
43350
43351 bpf = ma_get_bytes_per_frame(format, channels);
43352 if (bpf == 0) {
43353 return MA_INVALID_ARGS;
43354 }
43355
43356 result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb);
43357 if (result != MA_SUCCESS) {
43358 return result;
43359 }
43360
43361 pRB->format = format;
43362 pRB->channels = channels;
43363
43364 return MA_SUCCESS;
43365}
43366
43367MA_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)
43368{
43369 return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
43370}
43371
43372MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB)
43373{
43374 if (pRB == NULL) {
43375 return;
43376 }
43377
43378 ma_rb_uninit(&pRB->rb);
43379}
43380
43381MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB)
43382{
43383 if (pRB == NULL) {
43384 return;
43385 }
43386
43387 ma_rb_reset(&pRB->rb);
43388}
43389
43390MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
43391{
43392 size_t sizeInBytes;
43393 ma_result result;
43394
43395 if (pRB == NULL || pSizeInFrames == NULL) {
43396 return MA_INVALID_ARGS;
43397 }
43398
43399 sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
43400
43401 result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut);
43402 if (result != MA_SUCCESS) {
43403 return result;
43404 }
43405
43406 *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB));
43407 return MA_SUCCESS;
43408}
43409
43410MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut)
43411{
43412 if (pRB == NULL) {
43413 return MA_INVALID_ARGS;
43414 }
43415
43416 return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB), pBufferOut);
43417}
43418
43419MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
43420{
43421 size_t sizeInBytes;
43422 ma_result result;
43423
43424 if (pRB == NULL) {
43425 return MA_INVALID_ARGS;
43426 }
43427
43428 sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
43429
43430 result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut);
43431 if (result != MA_SUCCESS) {
43432 return result;
43433 }
43434
43435 *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB));
43436 return MA_SUCCESS;
43437}
43438
43439MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut)
43440{
43441 if (pRB == NULL) {
43442 return MA_INVALID_ARGS;
43443 }
43444
43445 return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB), pBufferOut);
43446}
43447
43448MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
43449{
43450 if (pRB == NULL) {
43451 return MA_INVALID_ARGS;
43452 }
43453
43454 return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
43455}
43456
43457MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
43458{
43459 if (pRB == NULL) {
43460 return MA_INVALID_ARGS;
43461 }
43462
43463 return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
43464}
43465
43466MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB)
43467{
43468 if (pRB == NULL) {
43469 return 0;
43470 }
43471
43472 return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
43473}
43474
43475MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB)
43476{
43477 if (pRB == NULL) {
43478 return 0;
43479 }
43480
43481 return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
43482}
43483
43484MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB)
43485{
43486 if (pRB == NULL) {
43487 return 0;
43488 }
43489
43490 return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
43491}
43492
43493MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB)
43494{
43495 if (pRB == NULL) {
43496 return 0;
43497 }
43498
43499 return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
43500}
43501
43502MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB)
43503{
43504 if (pRB == NULL) {
43505 return 0;
43506 }
43507
43508 return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
43509}
43510
43511MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex)
43512{
43513 if (pRB == NULL) {
43514 return 0;
43515 }
43516
43517 return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB));
43518}
43519
43520MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer)
43521{
43522 if (pRB == NULL) {
43523 return NULL;
43524 }
43525
43526 return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer);
43527}
43528
43529
43530
43531MA_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)
43532{
43533 ma_result result;
43534 ma_uint32 sizeInFrames;
43535
43536 sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5);
43537 if (sizeInFrames == 0) {
43538 return MA_INVALID_ARGS;
43539 }
43540
43541 result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb);
43542 if (result != MA_SUCCESS) {
43543 return result;
43544 }
43545
43546 /* Seek forward a bit so we have a bit of a buffer in case of desyncs. */
43547 ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2);
43548
43549 return MA_SUCCESS;
43550}
43551
43552MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB)
43553{
43554 ma_pcm_rb_uninit((ma_pcm_rb*)pRB);
43555 return MA_SUCCESS;
43556}
43557
43558
43559
43560/**************************************************************************************************************************************************************
43561
43562Miscellaneous Helpers
43563
43564**************************************************************************************************************************************************************/
43565MA_API const char* ma_result_description(ma_result result)
43566{
43567 switch (result)
43568 {
43569 case MA_SUCCESS: return "No error";
43570 case MA_ERROR: return "Unknown error";
43571 case MA_INVALID_ARGS: return "Invalid argument";
43572 case MA_INVALID_OPERATION: return "Invalid operation";
43573 case MA_OUT_OF_MEMORY: return "Out of memory";
43574 case MA_OUT_OF_RANGE: return "Out of range";
43575 case MA_ACCESS_DENIED: return "Permission denied";
43576 case MA_DOES_NOT_EXIST: return "Resource does not exist";
43577 case MA_ALREADY_EXISTS: return "Resource already exists";
43578 case MA_TOO_MANY_OPEN_FILES: return "Too many open files";
43579 case MA_INVALID_FILE: return "Invalid file";
43580 case MA_TOO_BIG: return "Too large";
43581 case MA_PATH_TOO_LONG: return "Path too long";
43582 case MA_NAME_TOO_LONG: return "Name too long";
43583 case MA_NOT_DIRECTORY: return "Not a directory";
43584 case MA_IS_DIRECTORY: return "Is a directory";
43585 case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty";
43586 case MA_AT_END: return "At end";
43587 case MA_NO_SPACE: return "No space available";
43588 case MA_BUSY: return "Device or resource busy";
43589 case MA_IO_ERROR: return "Input/output error";
43590 case MA_INTERRUPT: return "Interrupted";
43591 case MA_UNAVAILABLE: return "Resource unavailable";
43592 case MA_ALREADY_IN_USE: return "Resource already in use";
43593 case MA_BAD_ADDRESS: return "Bad address";
43594 case MA_BAD_SEEK: return "Illegal seek";
43595 case MA_BAD_PIPE: return "Broken pipe";
43596 case MA_DEADLOCK: return "Deadlock";
43597 case MA_TOO_MANY_LINKS: return "Too many links";
43598 case MA_NOT_IMPLEMENTED: return "Not implemented";
43599 case MA_NO_MESSAGE: return "No message of desired type";
43600 case MA_BAD_MESSAGE: return "Invalid message";
43601 case MA_NO_DATA_AVAILABLE: return "No data available";
43602 case MA_INVALID_DATA: return "Invalid data";
43603 case MA_TIMEOUT: return "Timeout";
43604 case MA_NO_NETWORK: return "Network unavailable";
43605 case MA_NOT_UNIQUE: return "Not unique";
43606 case MA_NOT_SOCKET: return "Socket operation on non-socket";
43607 case MA_NO_ADDRESS: return "Destination address required";
43608 case MA_BAD_PROTOCOL: return "Protocol wrong type for socket";
43609 case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available";
43610 case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported";
43611 case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported";
43612 case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported";
43613 case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported";
43614 case MA_CONNECTION_RESET: return "Connection reset";
43615 case MA_ALREADY_CONNECTED: return "Already connected";
43616 case MA_NOT_CONNECTED: return "Not connected";
43617 case MA_CONNECTION_REFUSED: return "Connection refused";
43618 case MA_NO_HOST: return "No host";
43619 case MA_IN_PROGRESS: return "Operation in progress";
43620 case MA_CANCELLED: return "Operation cancelled";
43621 case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped";
43622
43623 case MA_FORMAT_NOT_SUPPORTED: return "Format not supported";
43624 case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported";
43625 case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported";
43626 case MA_NO_BACKEND: return "No backend";
43627 case MA_NO_DEVICE: return "No device";
43628 case MA_API_NOT_FOUND: return "API not found";
43629 case MA_INVALID_DEVICE_CONFIG: return "Invalid device config";
43630
43631 case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized";
43632 case MA_DEVICE_NOT_STARTED: return "Device not started";
43633
43634 case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend";
43635 case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device";
43636 case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device";
43637 case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device";
43638
43639 default: return "Unknown error";
43640 }
43641}
43642
43643MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
43644{
43645 if (pAllocationCallbacks != NULL) {
43646 return ma__malloc_from_callbacks(sz, pAllocationCallbacks);
43647 } else {
43648 return ma__malloc_default(sz, NULL);
43649 }
43650}
43651
43652MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
43653{
43654 if (pAllocationCallbacks != NULL) {
43655 if (pAllocationCallbacks->onRealloc != NULL) {
43656 return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData);
43657 } else {
43658 return NULL; /* This requires a native implementation of realloc(). */
43659 }
43660 } else {
43661 return ma__realloc_default(p, sz, NULL);
43662 }
43663}
43664
43665MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
43666{
43667 if (pAllocationCallbacks != NULL) {
43668 ma__free_from_callbacks(p, pAllocationCallbacks);
43669 } else {
43670 ma__free_default(p, NULL);
43671 }
43672}
43673
43674MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks)
43675{
43676 size_t extraBytes;
43677 void* pUnaligned;
43678 void* pAligned;
43679
43680 if (alignment == 0) {
43681 return 0;
43682 }
43683
43684 extraBytes = alignment-1 + sizeof(void*);
43685
43686 pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks);
43687 if (pUnaligned == NULL) {
43688 return NULL;
43689 }
43690
43691 pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1)));
43692 ((void**)pAligned)[-1] = pUnaligned;
43693
43694 return pAligned;
43695}
43696
43697MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
43698{
43699 ma_free(((void**)p)[-1], pAllocationCallbacks);
43700}
43701
43702MA_API const char* ma_get_format_name(ma_format format)
43703{
43704 switch (format)
43705 {
43706 case ma_format_unknown: return "Unknown";
43707 case ma_format_u8: return "8-bit Unsigned Integer";
43708 case ma_format_s16: return "16-bit Signed Integer";
43709 case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)";
43710 case ma_format_s32: return "32-bit Signed Integer";
43711 case ma_format_f32: return "32-bit IEEE Floating Point";
43712 default: return "Invalid";
43713 }
43714}
43715
43716MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels)
43717{
43718 ma_uint32 i;
43719 for (i = 0; i < channels; ++i) {
43720 pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor);
43721 }
43722}
43723
43724
43725MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format)
43726{
43727 ma_uint32 sizes[] = {
43728 0, /* unknown */
43729 1, /* u8 */
43730 2, /* s16 */
43731 3, /* s24 */
43732 4, /* s32 */
43733 4, /* f32 */
43734 };
43735 return sizes[format];
43736}
43737
43738
43739
43740MA_API ma_data_source_config ma_data_source_config_init(void)
43741{
43742 ma_data_source_config config;
43743
43744 MA_ZERO_OBJECT(&config);
43745
43746 return config;
43747}
43748
43749
43750MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource)
43751{
43752 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
43753
43754 if (pDataSource == NULL) {
43755 return MA_INVALID_ARGS;
43756 }
43757
43758 MA_ZERO_OBJECT(pDataSourceBase);
43759
43760 if (pConfig == NULL) {
43761 return MA_INVALID_ARGS;
43762 }
43763
43764 pDataSourceBase->vtable = pConfig->vtable;
43765 pDataSourceBase->rangeBegInFrames = 0;
43766 pDataSourceBase->rangeEndInFrames = ~((ma_uint64)0);
43767 pDataSourceBase->loopBegInFrames = 0;
43768 pDataSourceBase->loopEndInFrames = ~((ma_uint64)0);
43769 pDataSourceBase->pCurrent = pDataSource; /* Always read from ourself by default. */
43770 pDataSourceBase->pNext = NULL;
43771 pDataSourceBase->onGetNext = NULL;
43772
43773 /* Compatibility: Need to make a copy of the callbacks. This will be removed in version 0.11. */
43774 if (pConfig->vtable != NULL) {
43775 pDataSourceBase->cb = *pConfig->vtable;
43776 }
43777
43778 return MA_SUCCESS;
43779}
43780
43781MA_API void ma_data_source_uninit(ma_data_source* pDataSource)
43782{
43783 if (pDataSource == NULL) {
43784 return;
43785 }
43786
43787 /*
43788 This is placeholder in case we need this later. Data sources need to call this in their
43789 uninitialization routine to ensure things work later on if something is added here.
43790 */
43791}
43792
43793#if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING)
43794static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource)
43795{
43796 ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource;
43797
43798 MA_ASSERT(pDataSource != NULL);
43799 MA_ASSERT(ppCurrentDataSource != NULL);
43800
43801 if (pCurrentDataSource->pCurrent == NULL) {
43802 /*
43803 The current data source is NULL. If we're using this in the context of a chain we need to return NULL
43804 here so that we don't end up looping. Otherwise we just return the data source itself.
43805 */
43806 if (pCurrentDataSource->pNext != NULL || pCurrentDataSource->onGetNext != NULL) {
43807 pCurrentDataSource = NULL;
43808 } else {
43809 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. */
43810 }
43811 } else {
43812 pCurrentDataSource = (ma_data_source_base*)pCurrentDataSource->pCurrent;
43813 }
43814
43815 *ppCurrentDataSource = pCurrentDataSource;
43816
43817 return MA_SUCCESS;
43818}
43819
43820static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead, ma_bool32 loop)
43821{
43822 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
43823
43824 if (pDataSourceBase == NULL) {
43825 return MA_AT_END;
43826 }
43827
43828 if (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE)) {
43829 /* No range is set - just read like normal. The data source itself will tell us when the end is reached. */
43830 return pDataSourceBase->cb.onRead(pDataSourceBase, pFramesOut, frameCount, pFramesRead);
43831 } else {
43832 /* Need to clamp to within the range. */
43833 ma_result result;
43834 ma_uint64 cursor;
43835 ma_uint64 framesRead = 0;
43836 ma_uint64 rangeEnd;
43837
43838 result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &cursor);
43839 if (result != MA_SUCCESS) {
43840 /* 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. */
43841 return pDataSourceBase->cb.onRead(pDataSourceBase, pFramesOut, frameCount, pFramesRead);
43842 }
43843
43844 /* We have the cursor. We need to make sure we don't read beyond our range. */
43845 rangeEnd = pDataSourceBase->rangeEndInFrames;
43846
43847 /* If looping, make sure we're within range. */
43848 if (loop) {
43849 if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) {
43850 rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames);
43851 }
43852 }
43853
43854 if (frameCount > (rangeEnd - cursor) && rangeEnd != ~((ma_uint64)0)) {
43855 frameCount = (rangeEnd - cursor);
43856 }
43857
43858 result = pDataSourceBase->cb.onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);
43859
43860 if (pFramesRead != NULL) {
43861 *pFramesRead = framesRead;
43862 }
43863
43864 /* We need to make sure MA_AT_END is returned if we hit the end of the range. */
43865 if (result != MA_AT_END && framesRead == 0) {
43866 result = MA_AT_END;
43867 }
43868
43869 return result;
43870 }
43871}
43872#endif
43873
43874MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead, ma_bool32 loop)
43875{
43876#if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING)
43877 ma_result result = MA_SUCCESS;
43878 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
43879 ma_data_source_base* pCurrentDataSource;
43880 void* pRunningFramesOut = pFramesOut;
43881 ma_uint64 totalFramesProcessed = 0;
43882 ma_format format;
43883 ma_uint32 channels;
43884 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. */
43885
43886 if (pFramesRead != NULL) {
43887 *pFramesRead = 0;
43888 }
43889
43890 if (pDataSourceBase == NULL) {
43891 return MA_INVALID_ARGS;
43892 }
43893
43894 /*
43895 We need to know the data format so we can advance the output buffer as we read frames. If this
43896 fails, chaining will not work and we'll just read as much as we can from the current source.
43897 */
43898 if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL) != MA_SUCCESS) {
43899 result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource);
43900 if (result != MA_SUCCESS) {
43901 return result;
43902 }
43903
43904 return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead, loop);
43905 }
43906
43907 /*
43908 Looping is a bit of a special case. When the `loop` argument is true, chaining will not work and
43909 only the current data source will be read from.
43910 */
43911
43912 /* Keep reading until we've read as many frames as possible. */
43913 while (totalFramesProcessed < frameCount) {
43914 ma_uint64 framesProcessed;
43915 ma_uint64 framesRemaining = frameCount - totalFramesProcessed;
43916
43917 /* We need to resolve the data source that we'll actually be reading from. */
43918 result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource);
43919 if (result != MA_SUCCESS) {
43920 break;
43921 }
43922
43923 if (pCurrentDataSource == NULL) {
43924 break;
43925 }
43926
43927 result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed, loop);
43928 totalFramesProcessed += framesProcessed;
43929
43930 /*
43931 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
43932 not necessarily considered an error.
43933 */
43934 if (result != MA_SUCCESS && result != MA_AT_END) {
43935 break;
43936 }
43937
43938 /*
43939 We can determine if we've reached the end by checking the return value of the onRead()
43940 callback. To loop back to the start, all we need to do is seek back to the first frame.
43941 */
43942 if (result == MA_AT_END) {
43943 /*
43944 We reached the end. If we're looping, we just loop back to the start of the current
43945 data source. If we're not looping we need to check if we have another in the chain, and
43946 if so, switch to it.
43947 */
43948 if (loop) {
43949 if (framesProcessed == 0) {
43950 emptyLoopCounter += 1;
43951 if (emptyLoopCounter > 1) {
43952 break; /* Infinite loop detected. Get out. */
43953 }
43954 } else {
43955 emptyLoopCounter = 0;
43956 }
43957
43958 if (ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames) != MA_SUCCESS) {
43959 break; /* Failed to loop. Abort. */
43960 }
43961
43962 /* Don't return MA_AT_END for looping sounds. */
43963 result = MA_SUCCESS;
43964 } else {
43965 if (pCurrentDataSource->pNext != NULL) {
43966 pDataSourceBase->pCurrent = pCurrentDataSource->pNext;
43967 } else if (pCurrentDataSource->onGetNext != NULL) {
43968 pDataSourceBase->pCurrent = pCurrentDataSource->onGetNext(pCurrentDataSource);
43969 if (pDataSourceBase->pCurrent == NULL) {
43970 break; /* Our callback did not return a next data source. We're done. */
43971 }
43972 } else {
43973 /* Reached the end of the chain. We're done. */
43974 break;
43975 }
43976
43977 /* The next data source needs to be rewound to ensure data is read in looping scenarios. */
43978 ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0);
43979
43980 /*
43981 We need to make sure we clear the MA_AT_END result so we don't accidentally return
43982 it in the event that we coincidentally ended reading at the exact transition point
43983 of two data sources in a chain.
43984 */
43985 result = MA_SUCCESS;
43986 }
43987 }
43988
43989 if (pRunningFramesOut != NULL) {
43990 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels));
43991 }
43992 }
43993
43994 if (pFramesRead != NULL) {
43995 *pFramesRead = totalFramesProcessed;
43996 }
43997
43998 return result;
43999#else
44000 ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
44001
44002 /* Safety. */
44003 if (pFramesRead != NULL) {
44004 *pFramesRead = 0;
44005 }
44006
44007 if (pCallbacks == NULL) {
44008 return MA_INVALID_ARGS;
44009 }
44010
44011 if (pCallbacks->onRead == NULL) {
44012 return MA_NOT_IMPLEMENTED;
44013 }
44014
44015 /* A very small optimization for the non looping case. */
44016 if (loop == MA_FALSE) {
44017 return pCallbacks->onRead(pDataSource, pFramesOut, frameCount, pFramesRead);
44018 } else {
44019 ma_format format;
44020 ma_uint32 channels;
44021 ma_uint32 sampleRate;
44022 if (ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate) != MA_SUCCESS) {
44023 return pCallbacks->onRead(pDataSource, pFramesOut, frameCount, pFramesRead); /* We don't have a way to retrieve the data format which means we don't know how to offset the output buffer. Just read as much as we can. */
44024 } else {
44025 ma_result result = MA_SUCCESS;
44026 ma_uint64 totalFramesProcessed;
44027 void* pRunningFramesOut = pFramesOut;
44028
44029 totalFramesProcessed = 0;
44030 while (totalFramesProcessed < frameCount) {
44031 ma_uint64 framesProcessed;
44032 ma_uint64 framesRemaining = frameCount - totalFramesProcessed;
44033
44034 result = pCallbacks->onRead(pDataSource, pRunningFramesOut, framesRemaining, &framesProcessed);
44035 totalFramesProcessed += framesProcessed;
44036
44037 /*
44038 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
44039 not necessarily considered an error.
44040 */
44041 if (result != MA_SUCCESS && result != MA_AT_END) {
44042 break;
44043 }
44044
44045 /*
44046 We can determine if we've reached the end by checking the return value of the onRead() callback. If it's less than what we requested it means
44047 we've reached the end. To loop back to the start, all we need to do is seek back to the first frame.
44048 */
44049 if (framesProcessed < framesRemaining || result == MA_AT_END) {
44050 if (ma_data_source_seek_to_pcm_frame(pDataSource, 0) != MA_SUCCESS) {
44051 break;
44052 }
44053 }
44054
44055 if (pRunningFramesOut != NULL) {
44056 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels));
44057 }
44058 }
44059
44060 if (pFramesRead != NULL) {
44061 *pFramesRead = totalFramesProcessed;
44062 }
44063
44064 return result;
44065 }
44066 }
44067#endif
44068}
44069
44070MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked, ma_bool32 loop)
44071{
44072 return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked, loop);
44073}
44074
44075MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
44076{
44077#if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING)
44078 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
44079
44080 if (pDataSourceBase == NULL) {
44081 return MA_SUCCESS;
44082 }
44083
44084 if (pDataSourceBase->cb.onSeek == NULL) {
44085 return MA_NOT_IMPLEMENTED;
44086 }
44087
44088 if (frameIndex > pDataSourceBase->rangeEndInFrames) {
44089 return MA_INVALID_OPERATION; /* Trying to seek to far forward. */
44090 }
44091
44092 return pDataSourceBase->cb.onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex);
44093#else
44094 ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
44095 if (pCallbacks == NULL) {
44096 return MA_INVALID_ARGS;
44097 }
44098
44099 if (pCallbacks->onSeek == NULL) {
44100 return MA_NOT_IMPLEMENTED;
44101 }
44102
44103 return pCallbacks->onSeek(pDataSource, frameIndex);
44104#endif
44105}
44106
44107MA_API ma_result ma_data_source_map(ma_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount)
44108{
44109 ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
44110 if (pCallbacks == NULL) {
44111 return MA_INVALID_ARGS;
44112 }
44113
44114 if (pCallbacks->onMap == NULL) {
44115 return MA_NOT_IMPLEMENTED;
44116 }
44117
44118 return pCallbacks->onMap(pDataSource, ppFramesOut, pFrameCount);
44119}
44120
44121MA_API ma_result ma_data_source_unmap(ma_data_source* pDataSource, ma_uint64 frameCount)
44122{
44123 ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
44124 if (pCallbacks == NULL) {
44125 return MA_INVALID_ARGS;
44126 }
44127
44128 if (pCallbacks->onUnmap == NULL) {
44129 return MA_NOT_IMPLEMENTED;
44130 }
44131
44132 return pCallbacks->onUnmap(pDataSource, frameCount);
44133}
44134
44135MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
44136{
44137 ma_result result;
44138 ma_format format;
44139 ma_uint32 channels;
44140 ma_uint32 sampleRate;
44141 ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
44142
44143 if (pFormat != NULL) {
44144 *pFormat = ma_format_unknown;
44145 }
44146
44147 if (pChannels != NULL) {
44148 *pChannels = 0;
44149 }
44150
44151 if (pSampleRate != NULL) {
44152 *pSampleRate = 0;
44153 }
44154
44155 if (pCallbacks == NULL) {
44156 return MA_INVALID_ARGS;
44157 }
44158
44159 if (pCallbacks->onGetDataFormat == NULL) {
44160 return MA_NOT_IMPLEMENTED;
44161 }
44162
44163 result = pCallbacks->onGetDataFormat(pDataSource, &format, &channels, &sampleRate);
44164 if (result != MA_SUCCESS) {
44165 return result;
44166 }
44167
44168 if (pFormat != NULL) {
44169 *pFormat = format;
44170 }
44171 if (pChannels != NULL) {
44172 *pChannels = channels;
44173 }
44174 if (pSampleRate != NULL) {
44175 *pSampleRate = sampleRate;
44176 }
44177
44178 return MA_SUCCESS;
44179}
44180
44181MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
44182{
44183#if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING)
44184 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
44185 ma_result result;
44186 ma_uint64 cursor;
44187
44188 if (pCursor == NULL) {
44189 return MA_INVALID_ARGS;
44190 }
44191
44192 *pCursor = 0;
44193
44194 if (pDataSourceBase == NULL) {
44195 return MA_SUCCESS;
44196 }
44197
44198 if (pDataSourceBase->cb.onGetCursor == NULL) {
44199 return MA_NOT_IMPLEMENTED;
44200 }
44201
44202 result = pDataSourceBase->cb.onGetCursor(pDataSourceBase, &cursor);
44203 if (result != MA_SUCCESS) {
44204 return result;
44205 }
44206
44207 /* The cursor needs to be made relative to the start of the range. */
44208 if (cursor < pDataSourceBase->rangeBegInFrames) { /* Safety check so we don't return some huge number. */
44209 *pCursor = 0;
44210 } else {
44211 *pCursor = cursor - pDataSourceBase->rangeBegInFrames;
44212 }
44213
44214 return MA_SUCCESS;
44215#else
44216 ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
44217
44218 if (pCursor == NULL) {
44219 return MA_INVALID_ARGS;
44220 }
44221
44222 *pCursor = 0;
44223
44224 if (pCallbacks == NULL) {
44225 return MA_INVALID_ARGS;
44226 }
44227
44228 if (pCallbacks->onGetCursor == NULL) {
44229 return MA_NOT_IMPLEMENTED;
44230 }
44231
44232 return pCallbacks->onGetCursor(pDataSource, pCursor);
44233#endif
44234}
44235
44236MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
44237{
44238#if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING)
44239 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
44240
44241 if (pLength == NULL) {
44242 return MA_INVALID_ARGS;
44243 }
44244
44245 *pLength = 0;
44246
44247 if (pDataSourceBase == NULL) {
44248 return MA_INVALID_ARGS;
44249 }
44250
44251 /*
44252 If we have a range defined we'll use that to determine the length. This is one of rare times
44253 where we'll actually trust the caller. If they've set the range, I think it's mostly safe to
44254 assume they've set it based on some higher level knowledge of the structure of the sound bank.
44255 */
44256 if (pDataSourceBase->rangeEndInFrames != ~((ma_uint64)0)) {
44257 *pLength = pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames;
44258 return MA_SUCCESS;
44259 }
44260
44261 /*
44262 Getting here means a range is not defined so we'll need to get the data source itself to tell
44263 us the length.
44264 */
44265 if (pDataSourceBase->cb.onGetLength == NULL) {
44266 return MA_NOT_IMPLEMENTED;
44267 }
44268
44269 return pDataSourceBase->cb.onGetLength(pDataSource, pLength);
44270#else
44271 ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource;
44272
44273 if (pLength == NULL) {
44274 return MA_INVALID_ARGS;
44275 }
44276
44277 *pLength = 0;
44278
44279 if (pCallbacks == NULL) {
44280 return MA_INVALID_ARGS;
44281 }
44282
44283 if (pCallbacks->onGetLength == NULL) {
44284 return MA_NOT_IMPLEMENTED;
44285 }
44286
44287 return pCallbacks->onGetLength(pDataSource, pLength);
44288#endif
44289}
44290
44291
44292#if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING)
44293MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames)
44294{
44295 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
44296 ma_result result;
44297 ma_uint64 cursor;
44298 ma_uint64 loopBegAbsolute;
44299 ma_uint64 loopEndAbsolute;
44300
44301 if (pDataSource == NULL) {
44302 return MA_INVALID_ARGS;
44303 }
44304
44305 if (rangeEndInFrames < rangeBegInFrames) {
44306 return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */
44307 }
44308
44309 /*
44310 The loop points need to be updated. We'll be storing the loop points relative to the range. We'll update
44311 these so that they maintain their absolute positioning. The loop points will then be clamped to the range.
44312 */
44313 loopBegAbsolute = pDataSourceBase->loopBegInFrames + pDataSourceBase->rangeBegInFrames;
44314 loopEndAbsolute = pDataSourceBase->loopEndInFrames + ((pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) ? pDataSourceBase->rangeBegInFrames : 0);
44315
44316 pDataSourceBase->rangeBegInFrames = rangeBegInFrames;
44317 pDataSourceBase->rangeEndInFrames = rangeEndInFrames;
44318
44319 /* Make the loop points relative again, and make sure they're clamped to within the range. */
44320 if (loopBegAbsolute > pDataSourceBase->rangeBegInFrames) {
44321 pDataSourceBase->loopBegInFrames = loopBegAbsolute - pDataSourceBase->rangeBegInFrames;
44322 } else {
44323 pDataSourceBase->loopBegInFrames = 0;
44324 }
44325
44326 if (pDataSourceBase->loopBegInFrames > pDataSourceBase->rangeEndInFrames) {
44327 pDataSourceBase->loopBegInFrames = pDataSourceBase->rangeEndInFrames;
44328 }
44329
44330 /* Only need to update the loop end point if it's not -1. */
44331 if (loopEndAbsolute != ~((ma_uint64)0)) {
44332 if (loopEndAbsolute > pDataSourceBase->rangeBegInFrames) {
44333 pDataSourceBase->loopEndInFrames = loopEndAbsolute - pDataSourceBase->rangeBegInFrames;
44334 } else {
44335 pDataSourceBase->loopEndInFrames = 0;
44336 }
44337
44338 if (pDataSourceBase->loopEndInFrames > pDataSourceBase->rangeEndInFrames && pDataSourceBase->loopEndInFrames) {
44339 pDataSourceBase->loopEndInFrames = pDataSourceBase->rangeEndInFrames;
44340 }
44341 }
44342
44343
44344 /* If the new range is past the current cursor position we need to seek to it. */
44345 result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor);
44346 if (result == MA_SUCCESS) {
44347 /* Seek to within range. Note that our seek positions here are relative to the new range. */
44348 if (cursor < rangeBegInFrames) {
44349 ma_data_source_seek_to_pcm_frame(pDataSource, 0);
44350 } else if (cursor > rangeEndInFrames) {
44351 ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames);
44352 }
44353 } else {
44354 /* 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. */
44355 }
44356
44357 return MA_SUCCESS;
44358}
44359
44360MA_API void ma_data_source_get_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames)
44361{
44362 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
44363
44364 if (pDataSource == NULL) {
44365 return;
44366 }
44367
44368 if (pRangeBegInFrames != NULL) {
44369 *pRangeBegInFrames = pDataSourceBase->rangeBegInFrames;
44370 }
44371
44372 if (pRangeEndInFrames != NULL) {
44373 *pRangeEndInFrames = pDataSourceBase->rangeEndInFrames;
44374 }
44375}
44376
44377MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames)
44378{
44379 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
44380
44381 if (pDataSource == NULL) {
44382 return MA_INVALID_ARGS;
44383 }
44384
44385 if (loopEndInFrames < loopBegInFrames) {
44386 return MA_INVALID_ARGS; /* The end of the loop point must come after the beginning. */
44387 }
44388
44389 if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) {
44390 return MA_INVALID_ARGS; /* The end of the loop point must not go beyond the range. */
44391 }
44392
44393 pDataSourceBase->loopBegInFrames = loopBegInFrames;
44394 pDataSourceBase->loopEndInFrames = loopEndInFrames;
44395
44396 /* The end cannot exceed the range. */
44397 if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) {
44398 pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames);
44399 }
44400
44401 return MA_SUCCESS;
44402}
44403
44404MA_API void ma_data_source_get_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames)
44405{
44406 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
44407
44408 if (pDataSource == NULL) {
44409 return;
44410 }
44411
44412 if (pLoopBegInFrames != NULL) {
44413 *pLoopBegInFrames = pDataSourceBase->loopBegInFrames;
44414 }
44415
44416 if (pLoopEndInFrames != NULL) {
44417 *pLoopEndInFrames = pDataSourceBase->loopEndInFrames;
44418 }
44419}
44420
44421MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource)
44422{
44423 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
44424
44425 if (pDataSource == NULL) {
44426 return MA_INVALID_ARGS;
44427 }
44428
44429 pDataSourceBase->pCurrent = pCurrentDataSource;
44430
44431 return MA_SUCCESS;
44432}
44433
44434MA_API ma_data_source* ma_data_source_get_current(ma_data_source* pDataSource)
44435{
44436 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
44437
44438 if (pDataSource == NULL) {
44439 return NULL;
44440 }
44441
44442 return pDataSourceBase->pCurrent;
44443}
44444
44445MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource)
44446{
44447 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
44448
44449 if (pDataSource == NULL) {
44450 return MA_INVALID_ARGS;
44451 }
44452
44453 pDataSourceBase->pNext = pNextDataSource;
44454
44455 return MA_SUCCESS;
44456}
44457
44458MA_API ma_data_source* ma_data_source_get_next(ma_data_source* pDataSource)
44459{
44460 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
44461
44462 if (pDataSource == NULL) {
44463 return NULL;
44464 }
44465
44466 return pDataSourceBase->pNext;
44467}
44468
44469MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext)
44470{
44471 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
44472
44473 if (pDataSource == NULL) {
44474 return MA_INVALID_ARGS;
44475 }
44476
44477 pDataSourceBase->onGetNext = onGetNext;
44478
44479 return MA_SUCCESS;
44480}
44481
44482MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(ma_data_source* pDataSource)
44483{
44484 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
44485
44486 if (pDataSource == NULL) {
44487 return NULL;
44488 }
44489
44490 return pDataSourceBase->onGetNext;
44491}
44492#endif
44493
44494
44495static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
44496{
44497 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
44498 ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames(pAudioBufferRef, pFramesOut, frameCount, MA_FALSE);
44499
44500 if (pFramesRead != NULL) {
44501 *pFramesRead = framesRead;
44502 }
44503
44504 if (framesRead < frameCount || framesRead == 0) {
44505 return MA_AT_END;
44506 }
44507
44508 return MA_SUCCESS;
44509}
44510
44511static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
44512{
44513 return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex);
44514}
44515
44516static ma_result ma_audio_buffer_ref__data_source_on_map(ma_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount)
44517{
44518 return ma_audio_buffer_ref_map((ma_audio_buffer_ref*)pDataSource, ppFramesOut, pFrameCount);
44519}
44520
44521static ma_result ma_audio_buffer_ref__data_source_on_unmap(ma_data_source* pDataSource, ma_uint64 frameCount)
44522{
44523 return ma_audio_buffer_ref_unmap((ma_audio_buffer_ref*)pDataSource, frameCount);
44524}
44525
44526static 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)
44527{
44528 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
44529
44530 *pFormat = pAudioBufferRef->format;
44531 *pChannels = pAudioBufferRef->channels;
44532 *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */
44533
44534 return MA_SUCCESS;
44535}
44536
44537static ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
44538{
44539 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
44540
44541 *pCursor = pAudioBufferRef->cursor;
44542
44543 return MA_SUCCESS;
44544}
44545
44546static ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
44547{
44548 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
44549
44550 *pLength = pAudioBufferRef->sizeInFrames;
44551
44552 return MA_SUCCESS;
44553}
44554
44555static ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable =
44556{
44557 ma_audio_buffer_ref__data_source_on_read,
44558 ma_audio_buffer_ref__data_source_on_seek,
44559 ma_audio_buffer_ref__data_source_on_map,
44560 ma_audio_buffer_ref__data_source_on_unmap,
44561 ma_audio_buffer_ref__data_source_on_get_data_format,
44562 ma_audio_buffer_ref__data_source_on_get_cursor,
44563 ma_audio_buffer_ref__data_source_on_get_length
44564};
44565
44566MA_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)
44567{
44568 ma_result result;
44569 ma_data_source_config dataSourceConfig;
44570
44571 if (pAudioBufferRef == NULL) {
44572 return MA_INVALID_ARGS;
44573 }
44574
44575 MA_ZERO_OBJECT(pAudioBufferRef);
44576
44577 dataSourceConfig = ma_data_source_config_init();
44578 dataSourceConfig.vtable = &g_ma_audio_buffer_ref_data_source_vtable;
44579
44580 result = ma_data_source_init(&dataSourceConfig, &pAudioBufferRef->ds);
44581 if (result != MA_SUCCESS) {
44582 return result;
44583 }
44584
44585 pAudioBufferRef->format = format;
44586 pAudioBufferRef->channels = channels;
44587 pAudioBufferRef->cursor = 0;
44588 pAudioBufferRef->sizeInFrames = sizeInFrames;
44589 pAudioBufferRef->pData = pData;
44590
44591 return MA_SUCCESS;
44592}
44593
44594MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef)
44595{
44596 if (pAudioBufferRef == NULL) {
44597 return;
44598 }
44599
44600 ma_data_source_uninit(&pAudioBufferRef->ds);
44601}
44602
44603MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames)
44604{
44605 if (pAudioBufferRef == NULL) {
44606 return MA_INVALID_ARGS;
44607 }
44608
44609 pAudioBufferRef->cursor = 0;
44610 pAudioBufferRef->sizeInFrames = sizeInFrames;
44611 pAudioBufferRef->pData = pData;
44612
44613 return MA_SUCCESS;
44614}
44615
44616MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop)
44617{
44618 ma_uint64 totalFramesRead = 0;
44619
44620 if (pAudioBufferRef == NULL) {
44621 return 0;
44622 }
44623
44624 if (frameCount == 0) {
44625 return 0;
44626 }
44627
44628 while (totalFramesRead < frameCount) {
44629 ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
44630 ma_uint64 framesRemaining = frameCount - totalFramesRead;
44631 ma_uint64 framesToRead;
44632
44633 framesToRead = framesRemaining;
44634 if (framesToRead > framesAvailable) {
44635 framesToRead = framesAvailable;
44636 }
44637
44638 if (pFramesOut != NULL) {
44639 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);
44640 }
44641
44642 totalFramesRead += framesToRead;
44643
44644 pAudioBufferRef->cursor += framesToRead;
44645 if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) {
44646 if (loop) {
44647 pAudioBufferRef->cursor = 0;
44648 } else {
44649 break; /* We've reached the end and we're not looping. Done. */
44650 }
44651 }
44652
44653 MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames);
44654 }
44655
44656 return totalFramesRead;
44657}
44658
44659MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex)
44660{
44661 if (pAudioBufferRef == NULL) {
44662 return MA_INVALID_ARGS;
44663 }
44664
44665 if (frameIndex > pAudioBufferRef->sizeInFrames) {
44666 return MA_INVALID_ARGS;
44667 }
44668
44669 pAudioBufferRef->cursor = (size_t)frameIndex;
44670
44671 return MA_SUCCESS;
44672}
44673
44674MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount)
44675{
44676 ma_uint64 framesAvailable;
44677 ma_uint64 frameCount = 0;
44678
44679 if (ppFramesOut != NULL) {
44680 *ppFramesOut = NULL; /* Safety. */
44681 }
44682
44683 if (pFrameCount != NULL) {
44684 frameCount = *pFrameCount;
44685 *pFrameCount = 0; /* Safety. */
44686 }
44687
44688 if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) {
44689 return MA_INVALID_ARGS;
44690 }
44691
44692 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
44693 if (frameCount > framesAvailable) {
44694 frameCount = framesAvailable;
44695 }
44696
44697 *ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels));
44698 *pFrameCount = frameCount;
44699
44700 return MA_SUCCESS;
44701}
44702
44703MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount)
44704{
44705 ma_uint64 framesAvailable;
44706
44707 if (pAudioBufferRef == NULL) {
44708 return MA_INVALID_ARGS;
44709 }
44710
44711 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
44712 if (frameCount > framesAvailable) {
44713 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. */
44714 }
44715
44716 pAudioBufferRef->cursor += frameCount;
44717
44718 if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) {
44719 return MA_AT_END; /* Successful. Need to tell the caller that the end has been reached so that it can loop if desired. */
44720 } else {
44721 return MA_SUCCESS;
44722 }
44723}
44724
44725MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef)
44726{
44727 if (pAudioBufferRef == NULL) {
44728 return MA_FALSE;
44729 }
44730
44731 return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames;
44732}
44733
44734MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor)
44735{
44736 if (pCursor == NULL) {
44737 return MA_INVALID_ARGS;
44738 }
44739
44740 *pCursor = 0;
44741
44742 if (pAudioBufferRef == NULL) {
44743 return MA_INVALID_ARGS;
44744 }
44745
44746 *pCursor = pAudioBufferRef->cursor;
44747
44748 return MA_SUCCESS;
44749}
44750
44751MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength)
44752{
44753 if (pLength == NULL) {
44754 return MA_INVALID_ARGS;
44755 }
44756
44757 *pLength = 0;
44758
44759 if (pAudioBufferRef == NULL) {
44760 return MA_INVALID_ARGS;
44761 }
44762
44763 *pLength = pAudioBufferRef->sizeInFrames;
44764
44765 return MA_SUCCESS;
44766}
44767
44768MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames)
44769{
44770 if (pAvailableFrames == NULL) {
44771 return MA_INVALID_ARGS;
44772 }
44773
44774 *pAvailableFrames = 0;
44775
44776 if (pAudioBufferRef == NULL) {
44777 return MA_INVALID_ARGS;
44778 }
44779
44780 if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) {
44781 *pAvailableFrames = 0;
44782 } else {
44783 *pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
44784 }
44785
44786 return MA_SUCCESS;
44787}
44788
44789
44790
44791
44792MA_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)
44793{
44794 ma_audio_buffer_config config;
44795
44796 MA_ZERO_OBJECT(&config);
44797 config.format = format;
44798 config.channels = channels;
44799 config.sizeInFrames = sizeInFrames;
44800 config.pData = pData;
44801 ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks);
44802
44803 return config;
44804}
44805
44806static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer)
44807{
44808 ma_result result;
44809
44810 if (pAudioBuffer == NULL) {
44811 return MA_INVALID_ARGS;
44812 }
44813
44814 MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData)); /* Safety. Don't overwrite the extra data. */
44815
44816 if (pConfig == NULL) {
44817 return MA_INVALID_ARGS;
44818 }
44819
44820 if (pConfig->sizeInFrames == 0) {
44821 return MA_INVALID_ARGS; /* Not allowing buffer sizes of 0 frames. */
44822 }
44823
44824 result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref);
44825 if (result != MA_SUCCESS) {
44826 return result;
44827 }
44828
44829 ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks);
44830
44831 if (doCopy) {
44832 ma_uint64 allocationSizeInBytes;
44833 void* pData;
44834
44835 allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels);
44836 if (allocationSizeInBytes > MA_SIZE_MAX) {
44837 return MA_OUT_OF_MEMORY; /* Too big. */
44838 }
44839
44840 pData = ma__malloc_from_callbacks((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */
44841 if (pData == NULL) {
44842 return MA_OUT_OF_MEMORY;
44843 }
44844
44845 if (pConfig->pData != NULL) {
44846 ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
44847 } else {
44848 ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
44849 }
44850
44851 ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames);
44852 pAudioBuffer->ownsData = MA_TRUE;
44853 } else {
44854 ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames);
44855 pAudioBuffer->ownsData = MA_FALSE;
44856 }
44857
44858 return MA_SUCCESS;
44859}
44860
44861static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree)
44862{
44863 if (pAudioBuffer == NULL) {
44864 return;
44865 }
44866
44867 if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) {
44868 ma__free_from_callbacks((void*)pAudioBuffer->ref.pData, &pAudioBuffer->allocationCallbacks); /* Naugty const cast, but OK in this case since we've guarded it with the ownsData check. */
44869 }
44870
44871 if (doFree) {
44872 ma__free_from_callbacks(pAudioBuffer, &pAudioBuffer->allocationCallbacks);
44873 }
44874
44875 ma_audio_buffer_ref_uninit(&pAudioBuffer->ref);
44876}
44877
44878MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer)
44879{
44880 return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer);
44881}
44882
44883MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer)
44884{
44885 return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer);
44886}
44887
44888MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer)
44889{
44890 ma_result result;
44891 ma_audio_buffer* pAudioBuffer;
44892 ma_audio_buffer_config innerConfig; /* We'll be making some changes to the config, so need to make a copy. */
44893 ma_uint64 allocationSizeInBytes;
44894
44895 if (ppAudioBuffer == NULL) {
44896 return MA_INVALID_ARGS;
44897 }
44898
44899 *ppAudioBuffer = NULL; /* Safety. */
44900
44901 if (pConfig == NULL) {
44902 return MA_INVALID_ARGS;
44903 }
44904
44905 innerConfig = *pConfig;
44906 ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks);
44907
44908 allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels));
44909 if (allocationSizeInBytes > MA_SIZE_MAX) {
44910 return MA_OUT_OF_MEMORY; /* Too big. */
44911 }
44912
44913 pAudioBuffer = (ma_audio_buffer*)ma__malloc_from_callbacks((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */
44914 if (pAudioBuffer == NULL) {
44915 return MA_OUT_OF_MEMORY;
44916 }
44917
44918 if (pConfig->pData != NULL) {
44919 ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
44920 } else {
44921 ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels);
44922 }
44923
44924 innerConfig.pData = &pAudioBuffer->_pExtraData[0];
44925
44926 result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer);
44927 if (result != MA_SUCCESS) {
44928 ma__free_from_callbacks(pAudioBuffer, &innerConfig.allocationCallbacks);
44929 return result;
44930 }
44931
44932 *ppAudioBuffer = pAudioBuffer;
44933
44934 return MA_SUCCESS;
44935}
44936
44937MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer)
44938{
44939 ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE);
44940}
44941
44942MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer)
44943{
44944 ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE);
44945}
44946
44947MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop)
44948{
44949 if (pAudioBuffer == NULL) {
44950 return 0;
44951 }
44952
44953 return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop);
44954}
44955
44956MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex)
44957{
44958 if (pAudioBuffer == NULL) {
44959 return MA_INVALID_ARGS;
44960 }
44961
44962 return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex);
44963}
44964
44965MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount)
44966{
44967 if (ppFramesOut != NULL) {
44968 *ppFramesOut = NULL; /* Safety. */
44969 }
44970
44971 if (pAudioBuffer == NULL) {
44972 if (pFrameCount != NULL) {
44973 *pFrameCount = 0;
44974 }
44975
44976 return MA_INVALID_ARGS;
44977 }
44978
44979 return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount);
44980}
44981
44982MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount)
44983{
44984 if (pAudioBuffer == NULL) {
44985 return MA_INVALID_ARGS;
44986 }
44987
44988 return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount);
44989}
44990
44991MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer)
44992{
44993 if (pAudioBuffer == NULL) {
44994 return MA_FALSE;
44995 }
44996
44997 return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref);
44998}
44999
45000MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor)
45001{
45002 if (pAudioBuffer == NULL) {
45003 return MA_INVALID_ARGS;
45004 }
45005
45006 return ma_audio_buffer_ref_get_cursor_in_pcm_frames(&pAudioBuffer->ref, pCursor);
45007}
45008
45009MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength)
45010{
45011 if (pAudioBuffer == NULL) {
45012 return MA_INVALID_ARGS;
45013 }
45014
45015 return ma_audio_buffer_ref_get_length_in_pcm_frames(&pAudioBuffer->ref, pLength);
45016}
45017
45018MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames)
45019{
45020 if (pAvailableFrames == NULL) {
45021 return MA_INVALID_ARGS;
45022 }
45023
45024 *pAvailableFrames = 0;
45025
45026 if (pAudioBuffer == NULL) {
45027 return MA_INVALID_ARGS;
45028 }
45029
45030 return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames);
45031}
45032
45033
45034
45035/**************************************************************************************************************************************************************
45036
45037VFS
45038
45039**************************************************************************************************************************************************************/
45040MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
45041{
45042 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
45043
45044 if (pFile == NULL) {
45045 return MA_INVALID_ARGS;
45046 }
45047
45048 *pFile = NULL;
45049
45050 if (pVFS == NULL || pFilePath == NULL || openMode == 0) {
45051 return MA_INVALID_ARGS;
45052 }
45053
45054 if (pCallbacks->onOpen == NULL) {
45055 return MA_NOT_IMPLEMENTED;
45056 }
45057
45058 return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile);
45059}
45060
45061MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
45062{
45063 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
45064
45065 if (pFile == NULL) {
45066 return MA_INVALID_ARGS;
45067 }
45068
45069 *pFile = NULL;
45070
45071 if (pVFS == NULL || pFilePath == NULL || openMode == 0) {
45072 return MA_INVALID_ARGS;
45073 }
45074
45075 if (pCallbacks->onOpenW == NULL) {
45076 return MA_NOT_IMPLEMENTED;
45077 }
45078
45079 return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile);
45080}
45081
45082MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file)
45083{
45084 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
45085
45086 if (pVFS == NULL || file == NULL) {
45087 return MA_INVALID_ARGS;
45088 }
45089
45090 if (pCallbacks->onClose == NULL) {
45091 return MA_NOT_IMPLEMENTED;
45092 }
45093
45094 return pCallbacks->onClose(pVFS, file);
45095}
45096
45097MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
45098{
45099 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
45100
45101 if (pBytesRead != NULL) {
45102 *pBytesRead = 0;
45103 }
45104
45105 if (pVFS == NULL || file == NULL || pDst == NULL) {
45106 return MA_INVALID_ARGS;
45107 }
45108
45109 if (pCallbacks->onRead == NULL) {
45110 return MA_NOT_IMPLEMENTED;
45111 }
45112
45113 return pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, pBytesRead);
45114}
45115
45116MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
45117{
45118 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
45119
45120 if (pBytesWritten != NULL) {
45121 *pBytesWritten = 0;
45122 }
45123
45124 if (pVFS == NULL || file == NULL || pSrc == NULL) {
45125 return MA_INVALID_ARGS;
45126 }
45127
45128 if (pCallbacks->onWrite == NULL) {
45129 return MA_NOT_IMPLEMENTED;
45130 }
45131
45132 return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
45133}
45134
45135MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
45136{
45137 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
45138
45139 if (pVFS == NULL || file == NULL) {
45140 return MA_INVALID_ARGS;
45141 }
45142
45143 if (pCallbacks->onSeek == NULL) {
45144 return MA_NOT_IMPLEMENTED;
45145 }
45146
45147 return pCallbacks->onSeek(pVFS, file, offset, origin);
45148}
45149
45150MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
45151{
45152 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
45153
45154 if (pCursor == NULL) {
45155 return MA_INVALID_ARGS;
45156 }
45157
45158 *pCursor = 0;
45159
45160 if (pVFS == NULL || file == NULL) {
45161 return MA_INVALID_ARGS;
45162 }
45163
45164 if (pCallbacks->onTell == NULL) {
45165 return MA_NOT_IMPLEMENTED;
45166 }
45167
45168 return pCallbacks->onTell(pVFS, file, pCursor);
45169}
45170
45171MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
45172{
45173 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
45174
45175 if (pInfo == NULL) {
45176 return MA_INVALID_ARGS;
45177 }
45178
45179 MA_ZERO_OBJECT(pInfo);
45180
45181 if (pVFS == NULL || file == NULL) {
45182 return MA_INVALID_ARGS;
45183 }
45184
45185 if (pCallbacks->onInfo == NULL) {
45186 return MA_NOT_IMPLEMENTED;
45187 }
45188
45189 return pCallbacks->onInfo(pVFS, file, pInfo);
45190}
45191
45192
45193static 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, ma_uint32 allocationType)
45194{
45195 ma_result result;
45196 ma_vfs_file file;
45197 ma_file_info info;
45198 void* pData;
45199 size_t bytesRead;
45200
45201 (void)allocationType;
45202
45203 if (ppData != NULL) {
45204 *ppData = NULL;
45205 }
45206 if (pSize != NULL) {
45207 *pSize = 0;
45208 }
45209
45210 if (ppData == NULL) {
45211 return MA_INVALID_ARGS;
45212 }
45213
45214 if (pFilePath != NULL) {
45215 result = ma_vfs_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
45216 } else {
45217 result = ma_vfs_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file);
45218 }
45219 if (result != MA_SUCCESS) {
45220 return result;
45221 }
45222
45223 result = ma_vfs_info(pVFS, file, &info);
45224 if (result != MA_SUCCESS) {
45225 ma_vfs_close(pVFS, file);
45226 return result;
45227 }
45228
45229 if (info.sizeInBytes > MA_SIZE_MAX) {
45230 ma_vfs_close(pVFS, file);
45231 return MA_TOO_BIG;
45232 }
45233
45234 pData = ma__malloc_from_callbacks((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */
45235 if (pData == NULL) {
45236 ma_vfs_close(pVFS, file);
45237 return result;
45238 }
45239
45240 result = ma_vfs_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */
45241 ma_vfs_close(pVFS, file);
45242
45243 if (result != MA_SUCCESS) {
45244 ma__free_from_callbacks(pData, pAllocationCallbacks);
45245 return result;
45246 }
45247
45248 if (pSize != NULL) {
45249 *pSize = bytesRead;
45250 }
45251
45252 MA_ASSERT(ppData != NULL);
45253 *ppData = pData;
45254
45255 return MA_SUCCESS;
45256}
45257
45258MA_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)
45259{
45260 return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks, 0 /*MA_ALLOCATION_TYPE_GENERAL*/);
45261}
45262
45263MA_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)
45264{
45265 return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks, 0 /*MA_ALLOCATION_TYPE_GENERAL*/);
45266}
45267
45268
45269#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
45270static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition)
45271{
45272 *pDesiredAccess = 0;
45273 if ((openMode & MA_OPEN_MODE_READ) != 0) {
45274 *pDesiredAccess |= GENERIC_READ;
45275 }
45276 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
45277 *pDesiredAccess |= GENERIC_WRITE;
45278 }
45279
45280 *pShareMode = 0;
45281 if ((openMode & MA_OPEN_MODE_READ) != 0) {
45282 *pShareMode |= FILE_SHARE_READ;
45283 }
45284
45285 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
45286 *pCreationDisposition = CREATE_ALWAYS; /* Opening in write mode. Truncate. */
45287 } else {
45288 *pCreationDisposition = OPEN_EXISTING; /* Opening in read mode. File must exist. */
45289 }
45290}
45291
45292static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
45293{
45294 HANDLE hFile;
45295 DWORD dwDesiredAccess;
45296 DWORD dwShareMode;
45297 DWORD dwCreationDisposition;
45298
45299 (void)pVFS;
45300
45301 ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);
45302
45303 hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
45304 if (hFile == INVALID_HANDLE_VALUE) {
45305 return ma_result_from_GetLastError(GetLastError());
45306 }
45307
45308 *pFile = hFile;
45309 return MA_SUCCESS;
45310}
45311
45312static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
45313{
45314 HANDLE hFile;
45315 DWORD dwDesiredAccess;
45316 DWORD dwShareMode;
45317 DWORD dwCreationDisposition;
45318
45319 (void)pVFS;
45320
45321 ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);
45322
45323 hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
45324 if (hFile == INVALID_HANDLE_VALUE) {
45325 return ma_result_from_GetLastError(GetLastError());
45326 }
45327
45328 *pFile = hFile;
45329 return MA_SUCCESS;
45330}
45331
45332static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file)
45333{
45334 (void)pVFS;
45335
45336 if (CloseHandle((HANDLE)file) == 0) {
45337 return ma_result_from_GetLastError(GetLastError());
45338 }
45339
45340 return MA_SUCCESS;
45341}
45342
45343
45344static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
45345{
45346 ma_result result = MA_SUCCESS;
45347 size_t totalBytesRead;
45348
45349 (void)pVFS;
45350
45351 totalBytesRead = 0;
45352 while (totalBytesRead < sizeInBytes) {
45353 size_t bytesRemaining;
45354 DWORD bytesToRead;
45355 DWORD bytesRead;
45356 BOOL readResult;
45357
45358 bytesRemaining = sizeInBytes - totalBytesRead;
45359 if (bytesRemaining >= 0xFFFFFFFF) {
45360 bytesToRead = 0xFFFFFFFF;
45361 } else {
45362 bytesToRead = (DWORD)bytesRemaining;
45363 }
45364
45365 readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL);
45366 if (readResult == 1 && bytesRead == 0) {
45367 result = MA_AT_END;
45368 break; /* EOF */
45369 }
45370
45371 totalBytesRead += bytesRead;
45372
45373 if (bytesRead < bytesToRead) {
45374 break; /* EOF */
45375 }
45376
45377 if (readResult == 0) {
45378 result = ma_result_from_GetLastError(GetLastError());
45379 break;
45380 }
45381 }
45382
45383 if (pBytesRead != NULL) {
45384 *pBytesRead = totalBytesRead;
45385 }
45386
45387 return result;
45388}
45389
45390static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
45391{
45392 ma_result result = MA_SUCCESS;
45393 size_t totalBytesWritten;
45394
45395 (void)pVFS;
45396
45397 totalBytesWritten = 0;
45398 while (totalBytesWritten < sizeInBytes) {
45399 size_t bytesRemaining;
45400 DWORD bytesToWrite;
45401 DWORD bytesWritten;
45402 BOOL writeResult;
45403
45404 bytesRemaining = sizeInBytes - totalBytesWritten;
45405 if (bytesRemaining >= 0xFFFFFFFF) {
45406 bytesToWrite = 0xFFFFFFFF;
45407 } else {
45408 bytesToWrite = (DWORD)bytesRemaining;
45409 }
45410
45411 writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL);
45412 totalBytesWritten += bytesWritten;
45413
45414 if (writeResult == 0) {
45415 result = ma_result_from_GetLastError(GetLastError());
45416 break;
45417 }
45418 }
45419
45420 if (pBytesWritten != NULL) {
45421 *pBytesWritten = totalBytesWritten;
45422 }
45423
45424 return result;
45425}
45426
45427
45428static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
45429{
45430 LARGE_INTEGER liDistanceToMove;
45431 DWORD dwMoveMethod;
45432 BOOL result;
45433
45434 (void)pVFS;
45435
45436 liDistanceToMove.QuadPart = offset;
45437
45438 /* */ if (origin == ma_seek_origin_current) {
45439 dwMoveMethod = FILE_CURRENT;
45440 } else if (origin == ma_seek_origin_end) {
45441 dwMoveMethod = FILE_END;
45442 } else {
45443 dwMoveMethod = FILE_BEGIN;
45444 }
45445
45446#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__)
45447 /* No SetFilePointerEx() so restrict to 31 bits. */
45448 if (origin > 0x7FFFFFFF) {
45449 return MA_OUT_OF_RANGE;
45450 }
45451
45452 result = SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod);
45453#else
45454 result = SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod);
45455#endif
45456 if (result == 0) {
45457 return ma_result_from_GetLastError(GetLastError());
45458 }
45459
45460 return MA_SUCCESS;
45461}
45462
45463static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
45464{
45465 LARGE_INTEGER liZero;
45466 LARGE_INTEGER liTell;
45467 BOOL result;
45468#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__)
45469 LONG tell;
45470#endif
45471
45472 (void)pVFS;
45473
45474 liZero.QuadPart = 0;
45475
45476#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__)
45477 result = SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT);
45478 liTell.QuadPart = tell;
45479#else
45480 result = SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT);
45481#endif
45482 if (result == 0) {
45483 return ma_result_from_GetLastError(GetLastError());
45484 }
45485
45486 if (pCursor != NULL) {
45487 *pCursor = liTell.QuadPart;
45488 }
45489
45490 return MA_SUCCESS;
45491}
45492
45493static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
45494{
45495 BY_HANDLE_FILE_INFORMATION fi;
45496 BOOL result;
45497
45498 (void)pVFS;
45499
45500 result = GetFileInformationByHandle((HANDLE)file, &fi);
45501 if (result == 0) {
45502 return ma_result_from_GetLastError(GetLastError());
45503 }
45504
45505 pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow);
45506
45507 return MA_SUCCESS;
45508}
45509#else
45510static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
45511{
45512 ma_result result;
45513 FILE* pFileStd;
45514 const char* pOpenModeStr;
45515
45516 MA_ASSERT(pFilePath != NULL);
45517 MA_ASSERT(openMode != 0);
45518 MA_ASSERT(pFile != NULL);
45519
45520 (void)pVFS;
45521
45522 if ((openMode & MA_OPEN_MODE_READ) != 0) {
45523 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
45524 pOpenModeStr = "r+";
45525 } else {
45526 pOpenModeStr = "rb";
45527 }
45528 } else {
45529 pOpenModeStr = "wb";
45530 }
45531
45532 result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr);
45533 if (result != MA_SUCCESS) {
45534 return result;
45535 }
45536
45537 *pFile = pFileStd;
45538
45539 return MA_SUCCESS;
45540}
45541
45542static ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
45543{
45544 ma_result result;
45545 FILE* pFileStd;
45546 const wchar_t* pOpenModeStr;
45547
45548 MA_ASSERT(pFilePath != NULL);
45549 MA_ASSERT(openMode != 0);
45550 MA_ASSERT(pFile != NULL);
45551
45552 (void)pVFS;
45553
45554 if ((openMode & MA_OPEN_MODE_READ) != 0) {
45555 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
45556 pOpenModeStr = L"r+";
45557 } else {
45558 pOpenModeStr = L"rb";
45559 }
45560 } else {
45561 pOpenModeStr = L"wb";
45562 }
45563
45564 result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL);
45565 if (result != MA_SUCCESS) {
45566 return result;
45567 }
45568
45569 *pFile = pFileStd;
45570
45571 return MA_SUCCESS;
45572}
45573
45574static ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file)
45575{
45576 MA_ASSERT(file != NULL);
45577
45578 (void)pVFS;
45579
45580 fclose((FILE*)file);
45581
45582 return MA_SUCCESS;
45583}
45584
45585static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
45586{
45587 size_t result;
45588
45589 MA_ASSERT(file != NULL);
45590 MA_ASSERT(pDst != NULL);
45591
45592 (void)pVFS;
45593
45594 result = fread(pDst, 1, sizeInBytes, (FILE*)file);
45595
45596 if (pBytesRead != NULL) {
45597 *pBytesRead = result;
45598 }
45599
45600 if (result != sizeInBytes) {
45601 if (result == 0 && feof((FILE*)file)) {
45602 return MA_AT_END;
45603 } else {
45604 return ma_result_from_errno(ferror((FILE*)file));
45605 }
45606 }
45607
45608 return MA_SUCCESS;
45609}
45610
45611static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
45612{
45613 size_t result;
45614
45615 MA_ASSERT(file != NULL);
45616 MA_ASSERT(pSrc != NULL);
45617
45618 (void)pVFS;
45619
45620 result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file);
45621
45622 if (pBytesWritten != NULL) {
45623 *pBytesWritten = result;
45624 }
45625
45626 if (result != sizeInBytes) {
45627 return ma_result_from_errno(ferror((FILE*)file));
45628 }
45629
45630 return MA_SUCCESS;
45631}
45632
45633static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
45634{
45635 int result;
45636 int whence;
45637
45638 MA_ASSERT(file != NULL);
45639
45640 (void)pVFS;
45641
45642 if (origin == ma_seek_origin_start) {
45643 whence = SEEK_SET;
45644 } else if (origin == ma_seek_origin_end) {
45645 whence = SEEK_END;
45646 } else {
45647 whence = SEEK_CUR;
45648 }
45649
45650#if defined(_WIN32)
45651 #if defined(_MSC_VER) && _MSC_VER > 1200
45652 result = _fseeki64((FILE*)file, offset, whence);
45653 #else
45654 /* No _fseeki64() so restrict to 31 bits. */
45655 if (origin > 0x7FFFFFFF) {
45656 return MA_OUT_OF_RANGE;
45657 }
45658
45659 result = fseek((FILE*)file, (int)offset, whence);
45660 #endif
45661#else
45662 result = fseek((FILE*)file, (long int)offset, whence);
45663#endif
45664 if (result != 0) {
45665 return MA_ERROR;
45666 }
45667
45668 return MA_SUCCESS;
45669}
45670
45671static ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
45672{
45673 ma_int64 result;
45674
45675 MA_ASSERT(file != NULL);
45676 MA_ASSERT(pCursor != NULL);
45677
45678 (void)pVFS;
45679
45680#if defined(_WIN32)
45681 #if defined(_MSC_VER) && _MSC_VER > 1200
45682 result = _ftelli64((FILE*)file);
45683 #else
45684 result = ftell((FILE*)file);
45685 #endif
45686#else
45687 result = ftell((FILE*)file);
45688#endif
45689
45690 *pCursor = result;
45691
45692 return MA_SUCCESS;
45693}
45694
45695#if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD)
45696int fileno(FILE *stream);
45697#endif
45698
45699static ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
45700{
45701 int fd;
45702 struct stat info;
45703
45704 MA_ASSERT(file != NULL);
45705 MA_ASSERT(pInfo != NULL);
45706
45707 (void)pVFS;
45708
45709#if defined(_MSC_VER)
45710 fd = _fileno((FILE*)file);
45711#else
45712 fd = fileno((FILE*)file);
45713#endif
45714
45715 if (fstat(fd, &info) != 0) {
45716 return ma_result_from_errno(errno);
45717 }
45718
45719 pInfo->sizeInBytes = info.st_size;
45720
45721 return MA_SUCCESS;
45722}
45723#endif
45724
45725
45726static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
45727{
45728 if (pFile == NULL) {
45729 return MA_INVALID_ARGS;
45730 }
45731
45732 *pFile = NULL;
45733
45734 if (pFilePath == NULL || openMode == 0) {
45735 return MA_INVALID_ARGS;
45736 }
45737
45738#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
45739 return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile);
45740#else
45741 return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile);
45742#endif
45743}
45744
45745static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
45746{
45747 if (pFile == NULL) {
45748 return MA_INVALID_ARGS;
45749 }
45750
45751 *pFile = NULL;
45752
45753 if (pFilePath == NULL || openMode == 0) {
45754 return MA_INVALID_ARGS;
45755 }
45756
45757#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
45758 return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile);
45759#else
45760 return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile);
45761#endif
45762}
45763
45764static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file)
45765{
45766 if (file == NULL) {
45767 return MA_INVALID_ARGS;
45768 }
45769
45770#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
45771 return ma_default_vfs_close__win32(pVFS, file);
45772#else
45773 return ma_default_vfs_close__stdio(pVFS, file);
45774#endif
45775}
45776
45777static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
45778{
45779 if (pBytesRead != NULL) {
45780 *pBytesRead = 0;
45781 }
45782
45783 if (file == NULL || pDst == NULL) {
45784 return MA_INVALID_ARGS;
45785 }
45786
45787#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
45788 return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead);
45789#else
45790 return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead);
45791#endif
45792}
45793
45794static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
45795{
45796 if (pBytesWritten != NULL) {
45797 *pBytesWritten = 0;
45798 }
45799
45800 if (file == NULL || pSrc == NULL) {
45801 return MA_INVALID_ARGS;
45802 }
45803
45804#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
45805 return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
45806#else
45807 return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
45808#endif
45809}
45810
45811static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
45812{
45813 if (file == NULL) {
45814 return MA_INVALID_ARGS;
45815 }
45816
45817#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
45818 return ma_default_vfs_seek__win32(pVFS, file, offset, origin);
45819#else
45820 return ma_default_vfs_seek__stdio(pVFS, file, offset, origin);
45821#endif
45822}
45823
45824static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
45825{
45826 if (pCursor == NULL) {
45827 return MA_INVALID_ARGS;
45828 }
45829
45830 *pCursor = 0;
45831
45832 if (file == NULL) {
45833 return MA_INVALID_ARGS;
45834 }
45835
45836#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
45837 return ma_default_vfs_tell__win32(pVFS, file, pCursor);
45838#else
45839 return ma_default_vfs_tell__stdio(pVFS, file, pCursor);
45840#endif
45841}
45842
45843static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
45844{
45845 if (pInfo == NULL) {
45846 return MA_INVALID_ARGS;
45847 }
45848
45849 MA_ZERO_OBJECT(pInfo);
45850
45851 if (file == NULL) {
45852 return MA_INVALID_ARGS;
45853 }
45854
45855#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
45856 return ma_default_vfs_info__win32(pVFS, file, pInfo);
45857#else
45858 return ma_default_vfs_info__stdio(pVFS, file, pInfo);
45859#endif
45860}
45861
45862
45863MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks)
45864{
45865 if (pVFS == NULL) {
45866 return MA_INVALID_ARGS;
45867 }
45868
45869 pVFS->cb.onOpen = ma_default_vfs_open;
45870 pVFS->cb.onOpenW = ma_default_vfs_open_w;
45871 pVFS->cb.onClose = ma_default_vfs_close;
45872 pVFS->cb.onRead = ma_default_vfs_read;
45873 pVFS->cb.onWrite = ma_default_vfs_write;
45874 pVFS->cb.onSeek = ma_default_vfs_seek;
45875 pVFS->cb.onTell = ma_default_vfs_tell;
45876 pVFS->cb.onInfo = ma_default_vfs_info;
45877 ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks);
45878
45879 return MA_SUCCESS;
45880}
45881
45882
45883MA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
45884{
45885 if (pVFS != NULL) {
45886 return ma_vfs_open(pVFS, pFilePath, openMode, pFile);
45887 } else {
45888 return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile);
45889 }
45890}
45891
45892MA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
45893{
45894 if (pVFS != NULL) {
45895 return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile);
45896 } else {
45897 return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile);
45898 }
45899}
45900
45901MA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file)
45902{
45903 if (pVFS != NULL) {
45904 return ma_vfs_close(pVFS, file);
45905 } else {
45906 return ma_default_vfs_close(pVFS, file);
45907 }
45908}
45909
45910MA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
45911{
45912 if (pVFS != NULL) {
45913 return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead);
45914 } else {
45915 return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead);
45916 }
45917}
45918
45919MA_API ma_result ma_vfs_or_default_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
45920{
45921 if (pVFS != NULL) {
45922 return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
45923 } else {
45924 return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
45925 }
45926}
45927
45928MA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
45929{
45930 if (pVFS != NULL) {
45931 return ma_vfs_seek(pVFS, file, offset, origin);
45932 } else {
45933 return ma_default_vfs_seek(pVFS, file, offset, origin);
45934 }
45935}
45936
45937MA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
45938{
45939 if (pVFS != NULL) {
45940 return ma_vfs_tell(pVFS, file, pCursor);
45941 } else {
45942 return ma_default_vfs_tell(pVFS, file, pCursor);
45943 }
45944}
45945
45946MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
45947{
45948 if (pVFS != NULL) {
45949 return ma_vfs_info(pVFS, file, pInfo);
45950 } else {
45951 return ma_default_vfs_info(pVFS, file, pInfo);
45952 }
45953}
45954
45955
45956
45957/**************************************************************************************************************************************************************
45958
45959Decoding and Encoding Headers. These are auto-generated from a tool.
45960
45961**************************************************************************************************************************************************************/
45962#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
45963/* dr_wav_h begin */
45964#ifndef dr_wav_h
45965#define dr_wav_h
45966#ifdef __cplusplus
45967extern "C" {
45968#endif
45969#define DRWAV_STRINGIFY(x) #x
45970#define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x)
45971#define DRWAV_VERSION_MAJOR 0
45972#define DRWAV_VERSION_MINOR 13
45973#define DRWAV_VERSION_REVISION 0
45974#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)
45975#include <stddef.h>
45976typedef signed char drwav_int8;
45977typedef unsigned char drwav_uint8;
45978typedef signed short drwav_int16;
45979typedef unsigned short drwav_uint16;
45980typedef signed int drwav_int32;
45981typedef unsigned int drwav_uint32;
45982#if defined(_MSC_VER) && !defined(__clang__)
45983 typedef signed __int64 drwav_int64;
45984 typedef unsigned __int64 drwav_uint64;
45985#else
45986 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
45987 #pragma GCC diagnostic push
45988 #pragma GCC diagnostic ignored "-Wlong-long"
45989 #if defined(__clang__)
45990 #pragma GCC diagnostic ignored "-Wc++11-long-long"
45991 #endif
45992 #endif
45993 typedef signed long long drwav_int64;
45994 typedef unsigned long long drwav_uint64;
45995 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
45996 #pragma GCC diagnostic pop
45997 #endif
45998#endif
45999#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
46000 typedef drwav_uint64 drwav_uintptr;
46001#else
46002 typedef drwav_uint32 drwav_uintptr;
46003#endif
46004typedef drwav_uint8 drwav_bool8;
46005typedef drwav_uint32 drwav_bool32;
46006#define DRWAV_TRUE 1
46007#define DRWAV_FALSE 0
46008#if !defined(DRWAV_API)
46009 #if defined(DRWAV_DLL)
46010 #if defined(_WIN32)
46011 #define DRWAV_DLL_IMPORT __declspec(dllimport)
46012 #define DRWAV_DLL_EXPORT __declspec(dllexport)
46013 #define DRWAV_DLL_PRIVATE static
46014 #else
46015 #if defined(__GNUC__) && __GNUC__ >= 4
46016 #define DRWAV_DLL_IMPORT __attribute__((visibility("default")))
46017 #define DRWAV_DLL_EXPORT __attribute__((visibility("default")))
46018 #define DRWAV_DLL_PRIVATE __attribute__((visibility("hidden")))
46019 #else
46020 #define DRWAV_DLL_IMPORT
46021 #define DRWAV_DLL_EXPORT
46022 #define DRWAV_DLL_PRIVATE static
46023 #endif
46024 #endif
46025 #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION)
46026 #define DRWAV_API DRWAV_DLL_EXPORT
46027 #else
46028 #define DRWAV_API DRWAV_DLL_IMPORT
46029 #endif
46030 #define DRWAV_PRIVATE DRWAV_DLL_PRIVATE
46031 #else
46032 #define DRWAV_API extern
46033 #define DRWAV_PRIVATE static
46034 #endif
46035#endif
46036typedef drwav_int32 drwav_result;
46037#define DRWAV_SUCCESS 0
46038#define DRWAV_ERROR -1
46039#define DRWAV_INVALID_ARGS -2
46040#define DRWAV_INVALID_OPERATION -3
46041#define DRWAV_OUT_OF_MEMORY -4
46042#define DRWAV_OUT_OF_RANGE -5
46043#define DRWAV_ACCESS_DENIED -6
46044#define DRWAV_DOES_NOT_EXIST -7
46045#define DRWAV_ALREADY_EXISTS -8
46046#define DRWAV_TOO_MANY_OPEN_FILES -9
46047#define DRWAV_INVALID_FILE -10
46048#define DRWAV_TOO_BIG -11
46049#define DRWAV_PATH_TOO_LONG -12
46050#define DRWAV_NAME_TOO_LONG -13
46051#define DRWAV_NOT_DIRECTORY -14
46052#define DRWAV_IS_DIRECTORY -15
46053#define DRWAV_DIRECTORY_NOT_EMPTY -16
46054#define DRWAV_END_OF_FILE -17
46055#define DRWAV_NO_SPACE -18
46056#define DRWAV_BUSY -19
46057#define DRWAV_IO_ERROR -20
46058#define DRWAV_INTERRUPT -21
46059#define DRWAV_UNAVAILABLE -22
46060#define DRWAV_ALREADY_IN_USE -23
46061#define DRWAV_BAD_ADDRESS -24
46062#define DRWAV_BAD_SEEK -25
46063#define DRWAV_BAD_PIPE -26
46064#define DRWAV_DEADLOCK -27
46065#define DRWAV_TOO_MANY_LINKS -28
46066#define DRWAV_NOT_IMPLEMENTED -29
46067#define DRWAV_NO_MESSAGE -30
46068#define DRWAV_BAD_MESSAGE -31
46069#define DRWAV_NO_DATA_AVAILABLE -32
46070#define DRWAV_INVALID_DATA -33
46071#define DRWAV_TIMEOUT -34
46072#define DRWAV_NO_NETWORK -35
46073#define DRWAV_NOT_UNIQUE -36
46074#define DRWAV_NOT_SOCKET -37
46075#define DRWAV_NO_ADDRESS -38
46076#define DRWAV_BAD_PROTOCOL -39
46077#define DRWAV_PROTOCOL_UNAVAILABLE -40
46078#define DRWAV_PROTOCOL_NOT_SUPPORTED -41
46079#define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED -42
46080#define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED -43
46081#define DRWAV_SOCKET_NOT_SUPPORTED -44
46082#define DRWAV_CONNECTION_RESET -45
46083#define DRWAV_ALREADY_CONNECTED -46
46084#define DRWAV_NOT_CONNECTED -47
46085#define DRWAV_CONNECTION_REFUSED -48
46086#define DRWAV_NO_HOST -49
46087#define DRWAV_IN_PROGRESS -50
46088#define DRWAV_CANCELLED -51
46089#define DRWAV_MEMORY_ALREADY_MAPPED -52
46090#define DRWAV_AT_END -53
46091#define DR_WAVE_FORMAT_PCM 0x1
46092#define DR_WAVE_FORMAT_ADPCM 0x2
46093#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3
46094#define DR_WAVE_FORMAT_ALAW 0x6
46095#define DR_WAVE_FORMAT_MULAW 0x7
46096#define DR_WAVE_FORMAT_DVI_ADPCM 0x11
46097#define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE
46098#define DRWAV_SEQUENTIAL 0x00000001
46099DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision);
46100DRWAV_API const char* drwav_version_string(void);
46101typedef enum
46102{
46103 drwav_seek_origin_start,
46104 drwav_seek_origin_current
46105} drwav_seek_origin;
46106typedef enum
46107{
46108 drwav_container_riff,
46109 drwav_container_w64,
46110 drwav_container_rf64
46111} drwav_container;
46112typedef struct
46113{
46114 union
46115 {
46116 drwav_uint8 fourcc[4];
46117 drwav_uint8 guid[16];
46118 } id;
46119 drwav_uint64 sizeInBytes;
46120 unsigned int paddingSize;
46121} drwav_chunk_header;
46122typedef struct
46123{
46124 drwav_uint16 formatTag;
46125 drwav_uint16 channels;
46126 drwav_uint32 sampleRate;
46127 drwav_uint32 avgBytesPerSec;
46128 drwav_uint16 blockAlign;
46129 drwav_uint16 bitsPerSample;
46130 drwav_uint16 extendedSize;
46131 drwav_uint16 validBitsPerSample;
46132 drwav_uint32 channelMask;
46133 drwav_uint8 subFormat[16];
46134} drwav_fmt;
46135DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT);
46136typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
46137typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);
46138typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin);
46139typedef 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);
46140typedef struct
46141{
46142 void* pUserData;
46143 void* (* onMalloc)(size_t sz, void* pUserData);
46144 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
46145 void (* onFree)(void* p, void* pUserData);
46146} drwav_allocation_callbacks;
46147typedef struct
46148{
46149 const drwav_uint8* data;
46150 size_t dataSize;
46151 size_t currentReadPos;
46152} drwav__memory_stream;
46153typedef struct
46154{
46155 void** ppData;
46156 size_t* pDataSize;
46157 size_t dataSize;
46158 size_t dataCapacity;
46159 size_t currentWritePos;
46160} drwav__memory_stream_write;
46161typedef struct
46162{
46163 drwav_container container;
46164 drwav_uint32 format;
46165 drwav_uint32 channels;
46166 drwav_uint32 sampleRate;
46167 drwav_uint32 bitsPerSample;
46168} drwav_data_format;
46169typedef enum
46170{
46171 drwav_metadata_type_none = 0,
46172 drwav_metadata_type_unknown = 1 << 0,
46173 drwav_metadata_type_smpl = 1 << 1,
46174 drwav_metadata_type_inst = 1 << 2,
46175 drwav_metadata_type_cue = 1 << 3,
46176 drwav_metadata_type_acid = 1 << 4,
46177 drwav_metadata_type_bext = 1 << 5,
46178 drwav_metadata_type_list_label = 1 << 6,
46179 drwav_metadata_type_list_note = 1 << 7,
46180 drwav_metadata_type_list_labelled_cue_region = 1 << 8,
46181 drwav_metadata_type_list_info_software = 1 << 9,
46182 drwav_metadata_type_list_info_copyright = 1 << 10,
46183 drwav_metadata_type_list_info_title = 1 << 11,
46184 drwav_metadata_type_list_info_artist = 1 << 12,
46185 drwav_metadata_type_list_info_comment = 1 << 13,
46186 drwav_metadata_type_list_info_date = 1 << 14,
46187 drwav_metadata_type_list_info_genre = 1 << 15,
46188 drwav_metadata_type_list_info_album = 1 << 16,
46189 drwav_metadata_type_list_info_tracknumber = 1 << 17,
46190 drwav_metadata_type_list_all_info_strings = drwav_metadata_type_list_info_software
46191 | drwav_metadata_type_list_info_copyright
46192 | drwav_metadata_type_list_info_title
46193 | drwav_metadata_type_list_info_artist
46194 | drwav_metadata_type_list_info_comment
46195 | drwav_metadata_type_list_info_date
46196 | drwav_metadata_type_list_info_genre
46197 | drwav_metadata_type_list_info_album
46198 | drwav_metadata_type_list_info_tracknumber,
46199 drwav_metadata_type_list_all_adtl = drwav_metadata_type_list_label
46200 | drwav_metadata_type_list_note
46201 | drwav_metadata_type_list_labelled_cue_region,
46202 drwav_metadata_type_all = -2,
46203 drwav_metadata_type_all_including_unknown = -1
46204} drwav_metadata_type;
46205typedef enum
46206{
46207 drwav_smpl_loop_type_forward = 0,
46208 drwav_smpl_loop_type_pingpong = 1,
46209 drwav_smpl_loop_type_backward = 2
46210} drwav_smpl_loop_type;
46211typedef struct
46212{
46213 drwav_uint32 cuePointId;
46214 drwav_uint32 type;
46215 drwav_uint32 firstSampleByteOffset;
46216 drwav_uint32 lastSampleByteOffset;
46217 drwav_uint32 sampleFraction;
46218 drwav_uint32 playCount;
46219} drwav_smpl_loop;
46220typedef struct
46221{
46222 drwav_uint32 manufacturerId;
46223 drwav_uint32 productId;
46224 drwav_uint32 samplePeriodNanoseconds;
46225 drwav_uint32 midiUnityNote;
46226 drwav_uint32 midiPitchFraction;
46227 drwav_uint32 smpteFormat;
46228 drwav_uint32 smpteOffset;
46229 drwav_uint32 sampleLoopCount;
46230 drwav_uint32 samplerSpecificDataSizeInBytes;
46231 drwav_smpl_loop* pLoops;
46232 drwav_uint8* pSamplerSpecificData;
46233} drwav_smpl;
46234typedef struct
46235{
46236 drwav_int8 midiUnityNote;
46237 drwav_int8 fineTuneCents;
46238 drwav_int8 gainDecibels;
46239 drwav_int8 lowNote;
46240 drwav_int8 highNote;
46241 drwav_int8 lowVelocity;
46242 drwav_int8 highVelocity;
46243} drwav_inst;
46244typedef struct
46245{
46246 drwav_uint32 id;
46247 drwav_uint32 playOrderPosition;
46248 drwav_uint8 dataChunkId[4];
46249 drwav_uint32 chunkStart;
46250 drwav_uint32 blockStart;
46251 drwav_uint32 sampleByteOffset;
46252} drwav_cue_point;
46253typedef struct
46254{
46255 drwav_uint32 cuePointCount;
46256 drwav_cue_point *pCuePoints;
46257} drwav_cue;
46258typedef enum
46259{
46260 drwav_acid_flag_one_shot = 1,
46261 drwav_acid_flag_root_note_set = 2,
46262 drwav_acid_flag_stretch = 4,
46263 drwav_acid_flag_disk_based = 8,
46264 drwav_acid_flag_acidizer = 16
46265} drwav_acid_flag;
46266typedef struct
46267{
46268 drwav_uint32 flags;
46269 drwav_uint16 midiUnityNote;
46270 drwav_uint16 reserved1;
46271 float reserved2;
46272 drwav_uint32 numBeats;
46273 drwav_uint16 meterDenominator;
46274 drwav_uint16 meterNumerator;
46275 float tempo;
46276} drwav_acid;
46277typedef struct
46278{
46279 drwav_uint32 cuePointId;
46280 drwav_uint32 stringLength;
46281 char* pString;
46282} drwav_list_label_or_note;
46283typedef struct
46284{
46285 char* pDescription;
46286 char* pOriginatorName;
46287 char* pOriginatorReference;
46288 char pOriginationDate[10];
46289 char pOriginationTime[8];
46290 drwav_uint64 timeReference;
46291 drwav_uint16 version;
46292 char* pCodingHistory;
46293 drwav_uint32 codingHistorySize;
46294 drwav_uint8* pUMID;
46295 drwav_uint16 loudnessValue;
46296 drwav_uint16 loudnessRange;
46297 drwav_uint16 maxTruePeakLevel;
46298 drwav_uint16 maxMomentaryLoudness;
46299 drwav_uint16 maxShortTermLoudness;
46300} drwav_bext;
46301typedef struct
46302{
46303 drwav_uint32 stringLength;
46304 char* pString;
46305} drwav_list_info_text;
46306typedef struct
46307{
46308 drwav_uint32 cuePointId;
46309 drwav_uint32 sampleLength;
46310 drwav_uint8 purposeId[4];
46311 drwav_uint16 country;
46312 drwav_uint16 language;
46313 drwav_uint16 dialect;
46314 drwav_uint16 codePage;
46315 drwav_uint32 stringLength;
46316 char* pString;
46317} drwav_list_labelled_cue_region;
46318typedef enum
46319{
46320 drwav_metadata_location_invalid,
46321 drwav_metadata_location_top_level,
46322 drwav_metadata_location_inside_info_list,
46323 drwav_metadata_location_inside_adtl_list
46324} drwav_metadata_location;
46325typedef struct
46326{
46327 drwav_uint8 id[4];
46328 drwav_metadata_location chunkLocation;
46329 drwav_uint32 dataSizeInBytes;
46330 drwav_uint8* pData;
46331} drwav_unknown_metadata;
46332typedef struct
46333{
46334 drwav_metadata_type type;
46335 union
46336 {
46337 drwav_cue cue;
46338 drwav_smpl smpl;
46339 drwav_acid acid;
46340 drwav_inst inst;
46341 drwav_bext bext;
46342 drwav_list_label_or_note labelOrNote;
46343 drwav_list_labelled_cue_region labelledCueRegion;
46344 drwav_list_info_text infoText;
46345 drwav_unknown_metadata unknown;
46346 } data;
46347} drwav_metadata;
46348typedef struct
46349{
46350 drwav_read_proc onRead;
46351 drwav_write_proc onWrite;
46352 drwav_seek_proc onSeek;
46353 void* pUserData;
46354 drwav_allocation_callbacks allocationCallbacks;
46355 drwav_container container;
46356 drwav_fmt fmt;
46357 drwav_uint32 sampleRate;
46358 drwav_uint16 channels;
46359 drwav_uint16 bitsPerSample;
46360 drwav_uint16 translatedFormatTag;
46361 drwav_uint64 totalPCMFrameCount;
46362 drwav_uint64 dataChunkDataSize;
46363 drwav_uint64 dataChunkDataPos;
46364 drwav_uint64 bytesRemaining;
46365 drwav_uint64 readCursorInPCMFrames;
46366 drwav_uint64 dataChunkDataSizeTargetWrite;
46367 drwav_bool32 isSequentialWrite;
46368 drwav_metadata_type allowedMetadataTypes;
46369 drwav_metadata* pMetadata;
46370 drwav_uint32 metadataCount;
46371 drwav__memory_stream memoryStream;
46372 drwav__memory_stream_write memoryStreamWrite;
46373 struct
46374 {
46375 drwav_uint32 bytesRemainingInBlock;
46376 drwav_uint16 predictor[2];
46377 drwav_int32 delta[2];
46378 drwav_int32 cachedFrames[4];
46379 drwav_uint32 cachedFrameCount;
46380 drwav_int32 prevFrames[2][2];
46381 } msadpcm;
46382 struct
46383 {
46384 drwav_uint32 bytesRemainingInBlock;
46385 drwav_int32 predictor[2];
46386 drwav_int32 stepIndex[2];
46387 drwav_int32 cachedFrames[16];
46388 drwav_uint32 cachedFrameCount;
46389 } ima;
46390} drwav;
46391DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
46392DRWAV_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);
46393DRWAV_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);
46394DRWAV_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);
46395DRWAV_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);
46396DRWAV_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);
46397DRWAV_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);
46398DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount);
46399DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav);
46400DRWAV_API drwav_result drwav_uninit(drwav* pWav);
46401DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut);
46402DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
46403DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
46404DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
46405DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex);
46406DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor);
46407DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength);
46408DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData);
46409DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
46410DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
46411DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
46412#ifndef DR_WAV_NO_CONVERSION_API
46413DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
46414DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
46415DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
46416DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
46417DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
46418DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount);
46419DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount);
46420DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount);
46421DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
46422DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
46423DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
46424DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
46425DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
46426DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
46427DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount);
46428DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
46429DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount);
46430DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount);
46431DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
46432DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
46433DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
46434DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
46435DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
46436DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
46437DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount);
46438DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
46439DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount);
46440DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount);
46441DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
46442DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
46443#endif
46444#ifndef DR_WAV_NO_STDIO
46445DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks);
46446DRWAV_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);
46447DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks);
46448DRWAV_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);
46449DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
46450DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
46451DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
46452DRWAV_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);
46453DRWAV_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);
46454DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
46455DRWAV_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);
46456DRWAV_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);
46457#endif
46458DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks);
46459DRWAV_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);
46460DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
46461DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
46462DRWAV_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);
46463DRWAV_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);
46464#ifndef DR_WAV_NO_CONVERSION_API
46465DRWAV_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);
46466DRWAV_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);
46467DRWAV_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);
46468#ifndef DR_WAV_NO_STDIO
46469DRWAV_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);
46470DRWAV_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);
46471DRWAV_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);
46472DRWAV_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);
46473DRWAV_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);
46474DRWAV_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);
46475#endif
46476DRWAV_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);
46477DRWAV_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);
46478DRWAV_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);
46479#endif
46480DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks);
46481DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data);
46482DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data);
46483DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data);
46484DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data);
46485DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data);
46486DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data);
46487DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data);
46488DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]);
46489DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b);
46490#ifdef __cplusplus
46491}
46492#endif
46493#endif
46494/* dr_wav_h end */
46495#endif /* MA_NO_WAV */
46496
46497#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
46498/* dr_flac_h begin */
46499#ifndef dr_flac_h
46500#define dr_flac_h
46501#ifdef __cplusplus
46502extern "C" {
46503#endif
46504#define DRFLAC_STRINGIFY(x) #x
46505#define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x)
46506#define DRFLAC_VERSION_MAJOR 0
46507#define DRFLAC_VERSION_MINOR 12
46508#define DRFLAC_VERSION_REVISION 29
46509#define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION)
46510#include <stddef.h>
46511typedef signed char drflac_int8;
46512typedef unsigned char drflac_uint8;
46513typedef signed short drflac_int16;
46514typedef unsigned short drflac_uint16;
46515typedef signed int drflac_int32;
46516typedef unsigned int drflac_uint32;
46517#if defined(_MSC_VER)
46518 typedef signed __int64 drflac_int64;
46519 typedef unsigned __int64 drflac_uint64;
46520#else
46521 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
46522 #pragma GCC diagnostic push
46523 #pragma GCC diagnostic ignored "-Wlong-long"
46524 #if defined(__clang__)
46525 #pragma GCC diagnostic ignored "-Wc++11-long-long"
46526 #endif
46527 #endif
46528 typedef signed long long drflac_int64;
46529 typedef unsigned long long drflac_uint64;
46530 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
46531 #pragma GCC diagnostic pop
46532 #endif
46533#endif
46534#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
46535 typedef drflac_uint64 drflac_uintptr;
46536#else
46537 typedef drflac_uint32 drflac_uintptr;
46538#endif
46539typedef drflac_uint8 drflac_bool8;
46540typedef drflac_uint32 drflac_bool32;
46541#define DRFLAC_TRUE 1
46542#define DRFLAC_FALSE 0
46543#if !defined(DRFLAC_API)
46544 #if defined(DRFLAC_DLL)
46545 #if defined(_WIN32)
46546 #define DRFLAC_DLL_IMPORT __declspec(dllimport)
46547 #define DRFLAC_DLL_EXPORT __declspec(dllexport)
46548 #define DRFLAC_DLL_PRIVATE static
46549 #else
46550 #if defined(__GNUC__) && __GNUC__ >= 4
46551 #define DRFLAC_DLL_IMPORT __attribute__((visibility("default")))
46552 #define DRFLAC_DLL_EXPORT __attribute__((visibility("default")))
46553 #define DRFLAC_DLL_PRIVATE __attribute__((visibility("hidden")))
46554 #else
46555 #define DRFLAC_DLL_IMPORT
46556 #define DRFLAC_DLL_EXPORT
46557 #define DRFLAC_DLL_PRIVATE static
46558 #endif
46559 #endif
46560 #if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION)
46561 #define DRFLAC_API DRFLAC_DLL_EXPORT
46562 #else
46563 #define DRFLAC_API DRFLAC_DLL_IMPORT
46564 #endif
46565 #define DRFLAC_PRIVATE DRFLAC_DLL_PRIVATE
46566 #else
46567 #define DRFLAC_API extern
46568 #define DRFLAC_PRIVATE static
46569 #endif
46570#endif
46571#if defined(_MSC_VER) && _MSC_VER >= 1700
46572 #define DRFLAC_DEPRECATED __declspec(deprecated)
46573#elif (defined(__GNUC__) && __GNUC__ >= 4)
46574 #define DRFLAC_DEPRECATED __attribute__((deprecated))
46575#elif defined(__has_feature)
46576 #if __has_feature(attribute_deprecated)
46577 #define DRFLAC_DEPRECATED __attribute__((deprecated))
46578 #else
46579 #define DRFLAC_DEPRECATED
46580 #endif
46581#else
46582 #define DRFLAC_DEPRECATED
46583#endif
46584DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision);
46585DRFLAC_API const char* drflac_version_string(void);
46586#ifndef DR_FLAC_BUFFER_SIZE
46587#define DR_FLAC_BUFFER_SIZE 4096
46588#endif
46589#if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
46590#define DRFLAC_64BIT
46591#endif
46592#ifdef DRFLAC_64BIT
46593typedef drflac_uint64 drflac_cache_t;
46594#else
46595typedef drflac_uint32 drflac_cache_t;
46596#endif
46597#define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0
46598#define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1
46599#define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2
46600#define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3
46601#define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4
46602#define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5
46603#define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6
46604#define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127
46605#define DRFLAC_PICTURE_TYPE_OTHER 0
46606#define DRFLAC_PICTURE_TYPE_FILE_ICON 1
46607#define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2
46608#define DRFLAC_PICTURE_TYPE_COVER_FRONT 3
46609#define DRFLAC_PICTURE_TYPE_COVER_BACK 4
46610#define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5
46611#define DRFLAC_PICTURE_TYPE_MEDIA 6
46612#define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7
46613#define DRFLAC_PICTURE_TYPE_ARTIST 8
46614#define DRFLAC_PICTURE_TYPE_CONDUCTOR 9
46615#define DRFLAC_PICTURE_TYPE_BAND 10
46616#define DRFLAC_PICTURE_TYPE_COMPOSER 11
46617#define DRFLAC_PICTURE_TYPE_LYRICIST 12
46618#define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13
46619#define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14
46620#define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15
46621#define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16
46622#define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17
46623#define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18
46624#define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19
46625#define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20
46626typedef enum
46627{
46628 drflac_container_native,
46629 drflac_container_ogg,
46630 drflac_container_unknown
46631} drflac_container;
46632typedef enum
46633{
46634 drflac_seek_origin_start,
46635 drflac_seek_origin_current
46636} drflac_seek_origin;
46637#pragma pack(2)
46638typedef struct
46639{
46640 drflac_uint64 firstPCMFrame;
46641 drflac_uint64 flacFrameOffset;
46642 drflac_uint16 pcmFrameCount;
46643} drflac_seekpoint;
46644#pragma pack()
46645typedef struct
46646{
46647 drflac_uint16 minBlockSizeInPCMFrames;
46648 drflac_uint16 maxBlockSizeInPCMFrames;
46649 drflac_uint32 minFrameSizeInPCMFrames;
46650 drflac_uint32 maxFrameSizeInPCMFrames;
46651 drflac_uint32 sampleRate;
46652 drflac_uint8 channels;
46653 drflac_uint8 bitsPerSample;
46654 drflac_uint64 totalPCMFrameCount;
46655 drflac_uint8 md5[16];
46656} drflac_streaminfo;
46657typedef struct
46658{
46659 drflac_uint32 type;
46660 const void* pRawData;
46661 drflac_uint32 rawDataSize;
46662 union
46663 {
46664 drflac_streaminfo streaminfo;
46665 struct
46666 {
46667 int unused;
46668 } padding;
46669 struct
46670 {
46671 drflac_uint32 id;
46672 const void* pData;
46673 drflac_uint32 dataSize;
46674 } application;
46675 struct
46676 {
46677 drflac_uint32 seekpointCount;
46678 const drflac_seekpoint* pSeekpoints;
46679 } seektable;
46680 struct
46681 {
46682 drflac_uint32 vendorLength;
46683 const char* vendor;
46684 drflac_uint32 commentCount;
46685 const void* pComments;
46686 } vorbis_comment;
46687 struct
46688 {
46689 char catalog[128];
46690 drflac_uint64 leadInSampleCount;
46691 drflac_bool32 isCD;
46692 drflac_uint8 trackCount;
46693 const void* pTrackData;
46694 } cuesheet;
46695 struct
46696 {
46697 drflac_uint32 type;
46698 drflac_uint32 mimeLength;
46699 const char* mime;
46700 drflac_uint32 descriptionLength;
46701 const char* description;
46702 drflac_uint32 width;
46703 drflac_uint32 height;
46704 drflac_uint32 colorDepth;
46705 drflac_uint32 indexColorCount;
46706 drflac_uint32 pictureDataSize;
46707 const drflac_uint8* pPictureData;
46708 } picture;
46709 } data;
46710} drflac_metadata;
46711typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
46712typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin);
46713typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata);
46714typedef struct
46715{
46716 void* pUserData;
46717 void* (* onMalloc)(size_t sz, void* pUserData);
46718 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
46719 void (* onFree)(void* p, void* pUserData);
46720} drflac_allocation_callbacks;
46721typedef struct
46722{
46723 const drflac_uint8* data;
46724 size_t dataSize;
46725 size_t currentReadPos;
46726} drflac__memory_stream;
46727typedef struct
46728{
46729 drflac_read_proc onRead;
46730 drflac_seek_proc onSeek;
46731 void* pUserData;
46732 size_t unalignedByteCount;
46733 drflac_cache_t unalignedCache;
46734 drflac_uint32 nextL2Line;
46735 drflac_uint32 consumedBits;
46736 drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)];
46737 drflac_cache_t cache;
46738 drflac_uint16 crc16;
46739 drflac_cache_t crc16Cache;
46740 drflac_uint32 crc16CacheIgnoredBytes;
46741} drflac_bs;
46742typedef struct
46743{
46744 drflac_uint8 subframeType;
46745 drflac_uint8 wastedBitsPerSample;
46746 drflac_uint8 lpcOrder;
46747 drflac_int32* pSamplesS32;
46748} drflac_subframe;
46749typedef struct
46750{
46751 drflac_uint64 pcmFrameNumber;
46752 drflac_uint32 flacFrameNumber;
46753 drflac_uint32 sampleRate;
46754 drflac_uint16 blockSizeInPCMFrames;
46755 drflac_uint8 channelAssignment;
46756 drflac_uint8 bitsPerSample;
46757 drflac_uint8 crc8;
46758} drflac_frame_header;
46759typedef struct
46760{
46761 drflac_frame_header header;
46762 drflac_uint32 pcmFramesRemaining;
46763 drflac_subframe subframes[8];
46764} drflac_frame;
46765typedef struct
46766{
46767 drflac_meta_proc onMeta;
46768 void* pUserDataMD;
46769 drflac_allocation_callbacks allocationCallbacks;
46770 drflac_uint32 sampleRate;
46771 drflac_uint8 channels;
46772 drflac_uint8 bitsPerSample;
46773 drflac_uint16 maxBlockSizeInPCMFrames;
46774 drflac_uint64 totalPCMFrameCount;
46775 drflac_container container;
46776 drflac_uint32 seekpointCount;
46777 drflac_frame currentFLACFrame;
46778 drflac_uint64 currentPCMFrame;
46779 drflac_uint64 firstFLACFramePosInBytes;
46780 drflac__memory_stream memoryStream;
46781 drflac_int32* pDecodedSamples;
46782 drflac_seekpoint* pSeekpoints;
46783 void* _oggbs;
46784 drflac_bool32 _noSeekTableSeek : 1;
46785 drflac_bool32 _noBinarySearchSeek : 1;
46786 drflac_bool32 _noBruteForceSeek : 1;
46787 drflac_bs bs;
46788 drflac_uint8 pExtraData[1];
46789} drflac;
46790DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
46791DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
46792DRFLAC_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);
46793DRFLAC_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);
46794DRFLAC_API void drflac_close(drflac* pFlac);
46795DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut);
46796DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut);
46797DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut);
46798DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex);
46799#ifndef DR_FLAC_NO_STDIO
46800DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks);
46801DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks);
46802DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
46803DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
46804#endif
46805DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks);
46806DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
46807DRFLAC_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);
46808DRFLAC_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);
46809DRFLAC_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);
46810#ifndef DR_FLAC_NO_STDIO
46811DRFLAC_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);
46812DRFLAC_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);
46813DRFLAC_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);
46814#endif
46815DRFLAC_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);
46816DRFLAC_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);
46817DRFLAC_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);
46818DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks);
46819typedef struct
46820{
46821 drflac_uint32 countRemaining;
46822 const char* pRunningData;
46823} drflac_vorbis_comment_iterator;
46824DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments);
46825DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut);
46826typedef struct
46827{
46828 drflac_uint32 countRemaining;
46829 const char* pRunningData;
46830} drflac_cuesheet_track_iterator;
46831#pragma pack(4)
46832typedef struct
46833{
46834 drflac_uint64 offset;
46835 drflac_uint8 index;
46836 drflac_uint8 reserved[3];
46837} drflac_cuesheet_track_index;
46838#pragma pack()
46839typedef struct
46840{
46841 drflac_uint64 offset;
46842 drflac_uint8 trackNumber;
46843 char ISRC[12];
46844 drflac_bool8 isAudio;
46845 drflac_bool8 preEmphasis;
46846 drflac_uint8 indexCount;
46847 const drflac_cuesheet_track_index* pIndexPoints;
46848} drflac_cuesheet_track;
46849DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData);
46850DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack);
46851#ifdef __cplusplus
46852}
46853#endif
46854#endif
46855/* dr_flac_h end */
46856#endif /* MA_NO_FLAC */
46857
46858#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
46859/* dr_mp3_h begin */
46860#ifndef dr_mp3_h
46861#define dr_mp3_h
46862#ifdef __cplusplus
46863extern "C" {
46864#endif
46865#define DRMP3_STRINGIFY(x) #x
46866#define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x)
46867#define DRMP3_VERSION_MAJOR 0
46868#define DRMP3_VERSION_MINOR 6
46869#define DRMP3_VERSION_REVISION 27
46870#define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION)
46871#include <stddef.h>
46872typedef signed char drmp3_int8;
46873typedef unsigned char drmp3_uint8;
46874typedef signed short drmp3_int16;
46875typedef unsigned short drmp3_uint16;
46876typedef signed int drmp3_int32;
46877typedef unsigned int drmp3_uint32;
46878#if defined(_MSC_VER)
46879 typedef signed __int64 drmp3_int64;
46880 typedef unsigned __int64 drmp3_uint64;
46881#else
46882 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
46883 #pragma GCC diagnostic push
46884 #pragma GCC diagnostic ignored "-Wlong-long"
46885 #if defined(__clang__)
46886 #pragma GCC diagnostic ignored "-Wc++11-long-long"
46887 #endif
46888 #endif
46889 typedef signed long long drmp3_int64;
46890 typedef unsigned long long drmp3_uint64;
46891 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
46892 #pragma GCC diagnostic pop
46893 #endif
46894#endif
46895#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
46896 typedef drmp3_uint64 drmp3_uintptr;
46897#else
46898 typedef drmp3_uint32 drmp3_uintptr;
46899#endif
46900typedef drmp3_uint8 drmp3_bool8;
46901typedef drmp3_uint32 drmp3_bool32;
46902#define DRMP3_TRUE 1
46903#define DRMP3_FALSE 0
46904#if !defined(DRMP3_API)
46905 #if defined(DRMP3_DLL)
46906 #if defined(_WIN32)
46907 #define DRMP3_DLL_IMPORT __declspec(dllimport)
46908 #define DRMP3_DLL_EXPORT __declspec(dllexport)
46909 #define DRMP3_DLL_PRIVATE static
46910 #else
46911 #if defined(__GNUC__) && __GNUC__ >= 4
46912 #define DRMP3_DLL_IMPORT __attribute__((visibility("default")))
46913 #define DRMP3_DLL_EXPORT __attribute__((visibility("default")))
46914 #define DRMP3_DLL_PRIVATE __attribute__((visibility("hidden")))
46915 #else
46916 #define DRMP3_DLL_IMPORT
46917 #define DRMP3_DLL_EXPORT
46918 #define DRMP3_DLL_PRIVATE static
46919 #endif
46920 #endif
46921 #if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION)
46922 #define DRMP3_API DRMP3_DLL_EXPORT
46923 #else
46924 #define DRMP3_API DRMP3_DLL_IMPORT
46925 #endif
46926 #define DRMP3_PRIVATE DRMP3_DLL_PRIVATE
46927 #else
46928 #define DRMP3_API extern
46929 #define DRMP3_PRIVATE static
46930 #endif
46931#endif
46932typedef drmp3_int32 drmp3_result;
46933#define DRMP3_SUCCESS 0
46934#define DRMP3_ERROR -1
46935#define DRMP3_INVALID_ARGS -2
46936#define DRMP3_INVALID_OPERATION -3
46937#define DRMP3_OUT_OF_MEMORY -4
46938#define DRMP3_OUT_OF_RANGE -5
46939#define DRMP3_ACCESS_DENIED -6
46940#define DRMP3_DOES_NOT_EXIST -7
46941#define DRMP3_ALREADY_EXISTS -8
46942#define DRMP3_TOO_MANY_OPEN_FILES -9
46943#define DRMP3_INVALID_FILE -10
46944#define DRMP3_TOO_BIG -11
46945#define DRMP3_PATH_TOO_LONG -12
46946#define DRMP3_NAME_TOO_LONG -13
46947#define DRMP3_NOT_DIRECTORY -14
46948#define DRMP3_IS_DIRECTORY -15
46949#define DRMP3_DIRECTORY_NOT_EMPTY -16
46950#define DRMP3_END_OF_FILE -17
46951#define DRMP3_NO_SPACE -18
46952#define DRMP3_BUSY -19
46953#define DRMP3_IO_ERROR -20
46954#define DRMP3_INTERRUPT -21
46955#define DRMP3_UNAVAILABLE -22
46956#define DRMP3_ALREADY_IN_USE -23
46957#define DRMP3_BAD_ADDRESS -24
46958#define DRMP3_BAD_SEEK -25
46959#define DRMP3_BAD_PIPE -26
46960#define DRMP3_DEADLOCK -27
46961#define DRMP3_TOO_MANY_LINKS -28
46962#define DRMP3_NOT_IMPLEMENTED -29
46963#define DRMP3_NO_MESSAGE -30
46964#define DRMP3_BAD_MESSAGE -31
46965#define DRMP3_NO_DATA_AVAILABLE -32
46966#define DRMP3_INVALID_DATA -33
46967#define DRMP3_TIMEOUT -34
46968#define DRMP3_NO_NETWORK -35
46969#define DRMP3_NOT_UNIQUE -36
46970#define DRMP3_NOT_SOCKET -37
46971#define DRMP3_NO_ADDRESS -38
46972#define DRMP3_BAD_PROTOCOL -39
46973#define DRMP3_PROTOCOL_UNAVAILABLE -40
46974#define DRMP3_PROTOCOL_NOT_SUPPORTED -41
46975#define DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED -42
46976#define DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED -43
46977#define DRMP3_SOCKET_NOT_SUPPORTED -44
46978#define DRMP3_CONNECTION_RESET -45
46979#define DRMP3_ALREADY_CONNECTED -46
46980#define DRMP3_NOT_CONNECTED -47
46981#define DRMP3_CONNECTION_REFUSED -48
46982#define DRMP3_NO_HOST -49
46983#define DRMP3_IN_PROGRESS -50
46984#define DRMP3_CANCELLED -51
46985#define DRMP3_MEMORY_ALREADY_MAPPED -52
46986#define DRMP3_AT_END -53
46987#define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152
46988#define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2)
46989#ifdef _MSC_VER
46990 #define DRMP3_INLINE __forceinline
46991#elif defined(__GNUC__)
46992 #if defined(__STRICT_ANSI__)
46993 #define DRMP3_INLINE __inline__ __attribute__((always_inline))
46994 #else
46995 #define DRMP3_INLINE inline __attribute__((always_inline))
46996 #endif
46997#elif defined(__WATCOMC__)
46998 #define DRMP3_INLINE __inline
46999#else
47000 #define DRMP3_INLINE
47001#endif
47002DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision);
47003DRMP3_API const char* drmp3_version_string(void);
47004typedef struct
47005{
47006 int frame_bytes, channels, hz, layer, bitrate_kbps;
47007} drmp3dec_frame_info;
47008typedef struct
47009{
47010 float mdct_overlap[2][9*32], qmf_state[15*2*32];
47011 int reserv, free_format_bytes;
47012 drmp3_uint8 header[4], reserv_buf[511];
47013} drmp3dec;
47014DRMP3_API void drmp3dec_init(drmp3dec *dec);
47015DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info);
47016DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples);
47017typedef enum
47018{
47019 drmp3_seek_origin_start,
47020 drmp3_seek_origin_current
47021} drmp3_seek_origin;
47022typedef struct
47023{
47024 drmp3_uint64 seekPosInBytes;
47025 drmp3_uint64 pcmFrameIndex;
47026 drmp3_uint16 mp3FramesToDiscard;
47027 drmp3_uint16 pcmFramesToDiscard;
47028} drmp3_seek_point;
47029typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
47030typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin);
47031typedef struct
47032{
47033 void* pUserData;
47034 void* (* onMalloc)(size_t sz, void* pUserData);
47035 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
47036 void (* onFree)(void* p, void* pUserData);
47037} drmp3_allocation_callbacks;
47038typedef struct
47039{
47040 drmp3_uint32 channels;
47041 drmp3_uint32 sampleRate;
47042} drmp3_config;
47043typedef struct
47044{
47045 drmp3dec decoder;
47046 drmp3dec_frame_info frameInfo;
47047 drmp3_uint32 channels;
47048 drmp3_uint32 sampleRate;
47049 drmp3_read_proc onRead;
47050 drmp3_seek_proc onSeek;
47051 void* pUserData;
47052 drmp3_allocation_callbacks allocationCallbacks;
47053 drmp3_uint32 mp3FrameChannels;
47054 drmp3_uint32 mp3FrameSampleRate;
47055 drmp3_uint32 pcmFramesConsumedInMP3Frame;
47056 drmp3_uint32 pcmFramesRemainingInMP3Frame;
47057 drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME];
47058 drmp3_uint64 currentPCMFrame;
47059 drmp3_uint64 streamCursor;
47060 drmp3_seek_point* pSeekPoints;
47061 drmp3_uint32 seekPointCount;
47062 size_t dataSize;
47063 size_t dataCapacity;
47064 size_t dataConsumed;
47065 drmp3_uint8* pData;
47066 drmp3_bool32 atEnd : 1;
47067 struct
47068 {
47069 const drmp3_uint8* pData;
47070 size_t dataSize;
47071 size_t currentReadPos;
47072 } memory;
47073} drmp3;
47074DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks);
47075DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks);
47076#ifndef DR_MP3_NO_STDIO
47077DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks);
47078DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks);
47079#endif
47080DRMP3_API void drmp3_uninit(drmp3* pMP3);
47081DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut);
47082DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut);
47083DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex);
47084DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3);
47085DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3);
47086DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount);
47087DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints);
47088DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints);
47089DRMP3_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);
47090DRMP3_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);
47091DRMP3_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);
47092DRMP3_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);
47093#ifndef DR_MP3_NO_STDIO
47094DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
47095DRMP3_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);
47096#endif
47097DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks);
47098DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks);
47099#ifdef __cplusplus
47100}
47101#endif
47102#endif
47103/* dr_mp3_h end */
47104#endif /* MA_NO_MP3 */
47105
47106
47107/**************************************************************************************************************************************************************
47108
47109Decoding
47110
47111**************************************************************************************************************************************************************/
47112#ifndef MA_NO_DECODING
47113
47114static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
47115{
47116 size_t bytesRead;
47117
47118 MA_ASSERT(pDecoder != NULL);
47119 MA_ASSERT(pBufferOut != NULL);
47120 MA_ASSERT(bytesToRead > 0); /* It's an error to call this with a byte count of zero. */
47121
47122 bytesRead = pDecoder->onRead(pDecoder, pBufferOut, bytesToRead);
47123
47124 if (pBytesRead != NULL) {
47125 *pBytesRead = bytesRead;
47126 }
47127
47128 if (bytesRead == 0) {
47129 return MA_AT_END;
47130 }
47131
47132 return MA_SUCCESS;
47133}
47134
47135static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin)
47136{
47137 ma_bool32 wasSuccessful;
47138
47139 MA_ASSERT(pDecoder != NULL);
47140
47141 wasSuccessful = pDecoder->onSeek(pDecoder, byteOffset, origin);
47142 if (wasSuccessful) {
47143 return MA_SUCCESS;
47144 } else {
47145 return MA_ERROR;
47146 }
47147}
47148
47149static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor)
47150{
47151 MA_ASSERT(pDecoder != NULL);
47152
47153 if (pDecoder->onTell == NULL) {
47154 return MA_NOT_IMPLEMENTED;
47155 }
47156
47157 return pDecoder->onTell(pDecoder, pCursor);
47158}
47159
47160
47161MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat)
47162{
47163 ma_decoding_backend_config config;
47164
47165 MA_ZERO_OBJECT(&config);
47166 config.preferredFormat = preferredFormat;
47167
47168 return config;
47169}
47170
47171
47172MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
47173{
47174 ma_decoder_config config;
47175 MA_ZERO_OBJECT(&config);
47176 config.format = outputFormat;
47177 config.channels = ma_min(outputChannels, ma_countof(config.channelMap));
47178 config.sampleRate = outputSampleRate;
47179 config.resampling.algorithm = ma_resample_algorithm_linear;
47180 config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
47181 config.resampling.speex.quality = 3;
47182 config.encodingFormat = ma_encoding_format_unknown;
47183
47184 /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */
47185
47186 return config;
47187}
47188
47189MA_API ma_decoder_config ma_decoder_config_init_default()
47190{
47191 return ma_decoder_config_init(ma_format_unknown, 0, 0);
47192}
47193
47194MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig)
47195{
47196 ma_decoder_config config;
47197 if (pConfig != NULL) {
47198 config = *pConfig;
47199 } else {
47200 MA_ZERO_OBJECT(&config);
47201 }
47202
47203 return config;
47204}
47205
47206static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig)
47207{
47208 ma_result result;
47209 ma_data_converter_config converterConfig;
47210 ma_format internalFormat;
47211 ma_uint32 internalChannels;
47212 ma_uint32 internalSampleRate;
47213 ma_channel internalChannelMap[MA_MAX_CHANNELS];
47214
47215 MA_ASSERT(pDecoder != NULL);
47216 MA_ASSERT(pConfig != NULL);
47217
47218 result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate);
47219 if (result != MA_SUCCESS) {
47220 return result; /* Failed to retrieve the internal data format. */
47221 }
47222
47223 /* Channel map needs to be retrieved separately. */
47224 if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onGetChannelMap != NULL) {
47225 pDecoder->pBackendVTable->onGetChannelMap(pDecoder->pBackendUserData, pDecoder->pBackend, internalChannelMap, ma_countof(internalChannelMap));
47226 } else {
47227 ma_get_standard_channel_map(ma_standard_channel_map_default, ma_min(internalChannels, ma_countof(internalChannelMap)), internalChannelMap);
47228 }
47229
47230
47231
47232 /* Make sure we're not asking for too many channels. */
47233 if (pConfig->channels > MA_MAX_CHANNELS) {
47234 return MA_INVALID_ARGS;
47235 }
47236
47237 /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */
47238 if (internalChannels > MA_MAX_CHANNELS) {
47239 return MA_INVALID_ARGS;
47240 }
47241
47242
47243 /* Output format. */
47244 if (pConfig->format == ma_format_unknown) {
47245 pDecoder->outputFormat = internalFormat;
47246 } else {
47247 pDecoder->outputFormat = pConfig->format;
47248 }
47249
47250 if (pConfig->channels == 0) {
47251 pDecoder->outputChannels = internalChannels;
47252 } else {
47253 pDecoder->outputChannels = pConfig->channels;
47254 }
47255
47256 if (pConfig->sampleRate == 0) {
47257 pDecoder->outputSampleRate = internalSampleRate;
47258 } else {
47259 pDecoder->outputSampleRate = pConfig->sampleRate;
47260 }
47261
47262 if (ma_channel_map_blank(pDecoder->outputChannels, pConfig->channelMap)) {
47263 ma_get_standard_channel_map(ma_standard_channel_map_default, pDecoder->outputChannels, pDecoder->outputChannelMap);
47264 } else {
47265 MA_COPY_MEMORY(pDecoder->outputChannelMap, pConfig->channelMap, sizeof(pConfig->channelMap));
47266 }
47267
47268
47269 converterConfig = ma_data_converter_config_init(
47270 internalFormat, pDecoder->outputFormat,
47271 internalChannels, pDecoder->outputChannels,
47272 internalSampleRate, pDecoder->outputSampleRate
47273 );
47274 ma_channel_map_copy(converterConfig.channelMapIn, internalChannelMap, internalChannels);
47275 ma_channel_map_copy(converterConfig.channelMapOut, pDecoder->outputChannelMap, pDecoder->outputChannels);
47276 converterConfig.channelMixMode = pConfig->channelMixMode;
47277 converterConfig.ditherMode = pConfig->ditherMode;
47278 converterConfig.resampling.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */
47279 converterConfig.resampling.algorithm = pConfig->resampling.algorithm;
47280 converterConfig.resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder;
47281 converterConfig.resampling.speex.quality = pConfig->resampling.speex.quality;
47282
47283 return ma_data_converter_init(&converterConfig, &pDecoder->converter);
47284}
47285
47286
47287
47288static ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
47289{
47290 ma_decoder* pDecoder = (ma_decoder*)pUserData;
47291 MA_ASSERT(pDecoder != NULL);
47292
47293 return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead);
47294}
47295
47296static ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin)
47297{
47298 ma_decoder* pDecoder = (ma_decoder*)pUserData;
47299 MA_ASSERT(pDecoder != NULL);
47300
47301 return ma_decoder_seek_bytes(pDecoder, offset, origin);
47302}
47303
47304static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor)
47305{
47306 ma_decoder* pDecoder = (ma_decoder*)pUserData;
47307 MA_ASSERT(pDecoder != NULL);
47308
47309 return ma_decoder_tell_bytes(pDecoder, pCursor);
47310}
47311
47312
47313static ma_result ma_decoder_init_from_vtable(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47314{
47315 ma_result result;
47316 ma_decoding_backend_config backendConfig;
47317 ma_data_source* pBackend;
47318
47319 MA_ASSERT(pVTable != NULL);
47320 MA_ASSERT(pConfig != NULL);
47321 MA_ASSERT(pDecoder != NULL);
47322
47323 if (pVTable->onInit == NULL) {
47324 return MA_NOT_IMPLEMENTED;
47325 }
47326
47327 backendConfig = ma_decoding_backend_config_init(pConfig->format);
47328
47329 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);
47330 if (result != MA_SUCCESS) {
47331 return result; /* Failed to initialize the backend from this vtable. */
47332 }
47333
47334 /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */
47335 pDecoder->pBackend = pBackend;
47336 pDecoder->pBackendVTable = pVTable;
47337 pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;
47338
47339 return MA_SUCCESS;
47340}
47341
47342
47343
47344static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
47345{
47346 ma_result result = MA_NO_BACKEND;
47347 size_t ivtable;
47348
47349 MA_ASSERT(pConfig != NULL);
47350 MA_ASSERT(pDecoder != NULL);
47351
47352 if (pConfig->ppCustomBackendVTables == NULL) {
47353 return MA_NO_BACKEND;
47354 }
47355
47356 /* The order each backend is listed is what defines the priority. */
47357 for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {
47358 const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];
47359 if (pVTable != NULL && pVTable->onInit != NULL) {
47360 result = ma_decoder_init_from_vtable(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder);
47361 if (result == MA_SUCCESS) {
47362 return MA_SUCCESS;
47363 } else {
47364 /* 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. */
47365 result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start);
47366 if (result != MA_SUCCESS) {
47367 return result; /* Failed to seek back to the start. */
47368 }
47369 }
47370 } else {
47371 /* No vtable. */
47372 }
47373 }
47374
47375 /* Getting here means we couldn't find a backend. */
47376 return MA_NO_BACKEND;
47377}
47378
47379
47380/* WAV */
47381#ifdef dr_wav_h
47382#define MA_HAS_WAV
47383
47384typedef struct
47385{
47386 ma_data_source_base ds;
47387 ma_read_proc onRead;
47388 ma_seek_proc onSeek;
47389 ma_tell_proc onTell;
47390 void* pReadSeekTellUserData;
47391 ma_format format; /* Can be f32, s16 or s32. */
47392#if !defined(MA_NO_WAV)
47393 drwav dr;
47394#endif
47395} ma_wav;
47396
47397MA_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);
47398MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav);
47399MA_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);
47400MA_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);
47401MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks);
47402MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
47403MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex);
47404MA_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);
47405MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor);
47406MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength);
47407
47408
47409static ma_result ma_wav_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
47410{
47411 return ma_wav_read_pcm_frames((ma_wav*)pDataSource, pFramesOut, frameCount, pFramesRead);
47412}
47413
47414static ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
47415{
47416 return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex);
47417}
47418
47419static ma_result ma_wav_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
47420{
47421 return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, NULL, 0);
47422}
47423
47424static ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
47425{
47426 return ma_wav_get_cursor_in_pcm_frames((ma_wav*)pDataSource, pCursor);
47427}
47428
47429static ma_result ma_wav_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
47430{
47431 return ma_wav_get_length_in_pcm_frames((ma_wav*)pDataSource, pLength);
47432}
47433
47434static ma_data_source_vtable g_ma_wav_ds_vtable =
47435{
47436 ma_wav_ds_read,
47437 ma_wav_ds_seek,
47438 NULL, /* onMap() */
47439 NULL, /* onUnmap() */
47440 ma_wav_ds_get_data_format,
47441 ma_wav_ds_get_cursor,
47442 ma_wav_ds_get_length
47443};
47444
47445
47446#if !defined(MA_NO_WAV)
47447static drwav_allocation_callbacks drwav_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks)
47448{
47449 drwav_allocation_callbacks callbacks;
47450
47451 if (pAllocationCallbacks != NULL) {
47452 callbacks.onMalloc = pAllocationCallbacks->onMalloc;
47453 callbacks.onRealloc = pAllocationCallbacks->onRealloc;
47454 callbacks.onFree = pAllocationCallbacks->onFree;
47455 callbacks.pUserData = pAllocationCallbacks->pUserData;
47456 } else {
47457 callbacks.onMalloc = ma__malloc_default;
47458 callbacks.onRealloc = ma__realloc_default;
47459 callbacks.onFree = ma__free_default;
47460 callbacks.pUserData = NULL;
47461 }
47462
47463 return callbacks;
47464}
47465
47466static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
47467{
47468 ma_wav* pWav = (ma_wav*)pUserData;
47469 ma_result result;
47470 size_t bytesRead;
47471
47472 MA_ASSERT(pWav != NULL);
47473
47474 result = pWav->onRead(pWav->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
47475 (void)result;
47476
47477 return bytesRead;
47478}
47479
47480static drwav_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, drwav_seek_origin origin)
47481{
47482 ma_wav* pWav = (ma_wav*)pUserData;
47483 ma_result result;
47484 ma_seek_origin maSeekOrigin;
47485
47486 MA_ASSERT(pWav != NULL);
47487
47488 maSeekOrigin = ma_seek_origin_start;
47489 if (origin == drwav_seek_origin_current) {
47490 maSeekOrigin = ma_seek_origin_current;
47491 }
47492
47493 result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin);
47494 if (result != MA_SUCCESS) {
47495 return MA_FALSE;
47496 }
47497
47498 return MA_TRUE;
47499}
47500#endif
47501
47502static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav)
47503{
47504 ma_result result;
47505 ma_data_source_config dataSourceConfig;
47506
47507 if (pWav == NULL) {
47508 return MA_INVALID_ARGS;
47509 }
47510
47511 MA_ZERO_OBJECT(pWav);
47512 pWav->format = ma_format_f32; /* f32 by default. */
47513
47514 if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) {
47515 pWav->format = pConfig->preferredFormat;
47516 } else {
47517 /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
47518 }
47519
47520 dataSourceConfig = ma_data_source_config_init();
47521 dataSourceConfig.vtable = &g_ma_wav_ds_vtable;
47522
47523 result = ma_data_source_init(&dataSourceConfig, &pWav->ds);
47524 if (result != MA_SUCCESS) {
47525 return result; /* Failed to initialize the base data source. */
47526 }
47527
47528 return MA_SUCCESS;
47529}
47530
47531MA_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)
47532{
47533 ma_result result;
47534
47535 result = ma_wav_init_internal(pConfig, pWav);
47536 if (result != MA_SUCCESS) {
47537 return result;
47538 }
47539
47540 if (onRead == NULL || onSeek == NULL) {
47541 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
47542 }
47543
47544 pWav->onRead = onRead;
47545 pWav->onSeek = onSeek;
47546 pWav->onTell = onTell;
47547 pWav->pReadSeekTellUserData = pReadSeekTellUserData;
47548
47549 #if !defined(MA_NO_WAV)
47550 {
47551 drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
47552 drwav_bool32 wavResult;
47553
47554 wavResult = drwav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, &wavAllocationCallbacks);
47555 if (wavResult != MA_TRUE) {
47556 return MA_INVALID_FILE;
47557 }
47558
47559 return MA_SUCCESS;
47560 }
47561 #else
47562 {
47563 /* wav is disabled. */
47564 (void)pAllocationCallbacks;
47565 return MA_NOT_IMPLEMENTED;
47566 }
47567 #endif
47568}
47569
47570MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav)
47571{
47572 ma_result result;
47573
47574 result = ma_wav_init_internal(pConfig, pWav);
47575 if (result != MA_SUCCESS) {
47576 return result;
47577 }
47578
47579 #if !defined(MA_NO_WAV)
47580 {
47581 drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
47582 drwav_bool32 wavResult;
47583
47584 wavResult = drwav_init_file(&pWav->dr, pFilePath, &wavAllocationCallbacks);
47585 if (wavResult != MA_TRUE) {
47586 return MA_INVALID_FILE;
47587 }
47588
47589 return MA_SUCCESS;
47590 }
47591 #else
47592 {
47593 /* wav is disabled. */
47594 (void)pFilePath;
47595 (void)pAllocationCallbacks;
47596 return MA_NOT_IMPLEMENTED;
47597 }
47598 #endif
47599}
47600
47601MA_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)
47602{
47603 ma_result result;
47604
47605 result = ma_wav_init_internal(pConfig, pWav);
47606 if (result != MA_SUCCESS) {
47607 return result;
47608 }
47609
47610 #if !defined(MA_NO_WAV)
47611 {
47612 drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
47613 drwav_bool32 wavResult;
47614
47615 wavResult = drwav_init_file_w(&pWav->dr, pFilePath, &wavAllocationCallbacks);
47616 if (wavResult != MA_TRUE) {
47617 return MA_INVALID_FILE;
47618 }
47619
47620 return MA_SUCCESS;
47621 }
47622 #else
47623 {
47624 /* wav is disabled. */
47625 (void)pFilePath;
47626 (void)pAllocationCallbacks;
47627 return MA_NOT_IMPLEMENTED;
47628 }
47629 #endif
47630}
47631
47632MA_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)
47633{
47634 ma_result result;
47635
47636 result = ma_wav_init_internal(pConfig, pWav);
47637 if (result != MA_SUCCESS) {
47638 return result;
47639 }
47640
47641 #if !defined(MA_NO_WAV)
47642 {
47643 drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
47644 drwav_bool32 wavResult;
47645
47646 wavResult = drwav_init_memory(&pWav->dr, pData, dataSize, &wavAllocationCallbacks);
47647 if (wavResult != MA_TRUE) {
47648 return MA_INVALID_FILE;
47649 }
47650
47651 return MA_SUCCESS;
47652 }
47653 #else
47654 {
47655 /* wav is disabled. */
47656 (void)pData;
47657 (void)dataSize;
47658 (void)pAllocationCallbacks;
47659 return MA_NOT_IMPLEMENTED;
47660 }
47661 #endif
47662}
47663
47664MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks)
47665{
47666 if (pWav == NULL) {
47667 return;
47668 }
47669
47670 (void)pAllocationCallbacks;
47671
47672 #if !defined(MA_NO_WAV)
47673 {
47674 drwav_uninit(&pWav->dr);
47675 }
47676 #else
47677 {
47678 /* wav is disabled. Should never hit this since initialization would have failed. */
47679 MA_ASSERT(MA_FALSE);
47680 }
47681 #endif
47682
47683 ma_data_source_uninit(&pWav->ds);
47684}
47685
47686MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
47687{
47688 if (pWav == NULL) {
47689 return MA_INVALID_ARGS;
47690 }
47691
47692 #if !defined(MA_NO_WAV)
47693 {
47694 /* We always use floating point format. */
47695 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
47696 ma_uint64 totalFramesRead = 0;
47697 ma_format format;
47698
47699 ma_wav_get_data_format(pWav, &format, NULL, NULL, NULL, 0);
47700
47701 switch (format)
47702 {
47703 case ma_format_f32:
47704 {
47705 totalFramesRead = drwav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut);
47706 } break;
47707
47708 case ma_format_s16:
47709 {
47710 totalFramesRead = drwav_read_pcm_frames_s16(&pWav->dr, frameCount, (drwav_int16*)pFramesOut);
47711 } break;
47712
47713 case ma_format_s32:
47714 {
47715 totalFramesRead = drwav_read_pcm_frames_s32(&pWav->dr, frameCount, (drwav_int32*)pFramesOut);
47716 } break;
47717
47718 /* Fallback to a raw read. */
47719 case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to supported format. */
47720 default:
47721 {
47722 totalFramesRead = drwav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut);
47723 } break;
47724 }
47725
47726 /* In the future we'll update dr_wav to return MA_AT_END for us. */
47727 if (totalFramesRead == 0) {
47728 result = MA_AT_END;
47729 }
47730
47731 if (pFramesRead != NULL) {
47732 *pFramesRead = totalFramesRead;
47733 }
47734
47735 return result;
47736 }
47737 #else
47738 {
47739 /* wav is disabled. Should never hit this since initialization would have failed. */
47740 MA_ASSERT(MA_FALSE);
47741
47742 (void)pFramesOut;
47743 (void)frameCount;
47744 (void)pFramesRead;
47745
47746 return MA_NOT_IMPLEMENTED;
47747 }
47748 #endif
47749}
47750
47751MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex)
47752{
47753 if (pWav == NULL) {
47754 return MA_INVALID_ARGS;
47755 }
47756
47757 #if !defined(MA_NO_WAV)
47758 {
47759 drwav_bool32 wavResult;
47760
47761 wavResult = drwav_seek_to_pcm_frame(&pWav->dr, frameIndex);
47762 if (wavResult != DRWAV_TRUE) {
47763 return MA_ERROR;
47764 }
47765
47766 return MA_SUCCESS;
47767 }
47768 #else
47769 {
47770 /* wav is disabled. Should never hit this since initialization would have failed. */
47771 MA_ASSERT(MA_FALSE);
47772
47773 (void)frameIndex;
47774
47775 return MA_NOT_IMPLEMENTED;
47776 }
47777 #endif
47778}
47779
47780MA_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)
47781{
47782 /* Defaults for safety. */
47783 if (pFormat != NULL) {
47784 *pFormat = ma_format_unknown;
47785 }
47786 if (pChannels != NULL) {
47787 *pChannels = 0;
47788 }
47789 if (pSampleRate != NULL) {
47790 *pSampleRate = 0;
47791 }
47792 if (pChannelMap != NULL) {
47793 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
47794 }
47795
47796 if (pWav == NULL) {
47797 return MA_INVALID_OPERATION;
47798 }
47799
47800 if (pFormat != NULL) {
47801 *pFormat = pWav->format;
47802 }
47803
47804 #if !defined(MA_NO_WAV)
47805 {
47806 if (pChannels != NULL) {
47807 *pChannels = pWav->dr.channels;
47808 }
47809
47810 if (pSampleRate != NULL) {
47811 *pSampleRate = pWav->dr.sampleRate;
47812 }
47813
47814 if (pChannelMap != NULL) {
47815 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, (ma_uint32)ma_min(pWav->dr.channels, channelMapCap), pChannelMap);
47816 }
47817
47818 return MA_SUCCESS;
47819 }
47820 #else
47821 {
47822 /* wav is disabled. Should never hit this since initialization would have failed. */
47823 MA_ASSERT(MA_FALSE);
47824 return MA_NOT_IMPLEMENTED;
47825 }
47826 #endif
47827}
47828
47829MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor)
47830{
47831 if (pCursor == NULL) {
47832 return MA_INVALID_ARGS;
47833 }
47834
47835 *pCursor = 0; /* Safety. */
47836
47837 if (pWav == NULL) {
47838 return MA_INVALID_ARGS;
47839 }
47840
47841 #if !defined(MA_NO_WAV)
47842 {
47843 drwav_result wavResult = drwav_get_cursor_in_pcm_frames(&pWav->dr, pCursor);
47844 if (wavResult != DRWAV_SUCCESS) {
47845 return (ma_result)wavResult; /* dr_wav result codes map to miniaudio's. */
47846 }
47847
47848 return MA_SUCCESS;
47849 }
47850 #else
47851 {
47852 /* wav is disabled. Should never hit this since initialization would have failed. */
47853 MA_ASSERT(MA_FALSE);
47854 return MA_NOT_IMPLEMENTED;
47855 }
47856 #endif
47857}
47858
47859MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength)
47860{
47861 if (pLength == NULL) {
47862 return MA_INVALID_ARGS;
47863 }
47864
47865 *pLength = 0; /* Safety. */
47866
47867 if (pWav == NULL) {
47868 return MA_INVALID_ARGS;
47869 }
47870
47871 #if !defined(MA_NO_WAV)
47872 {
47873 drwav_result wavResult = drwav_get_length_in_pcm_frames(&pWav->dr, pLength);
47874 if (wavResult != DRWAV_SUCCESS) {
47875 return (ma_result)wavResult; /* dr_wav result codes map to miniaudio's. */
47876 }
47877
47878 return MA_SUCCESS;
47879 }
47880 #else
47881 {
47882 /* wav is disabled. Should never hit this since initialization would have failed. */
47883 MA_ASSERT(MA_FALSE);
47884 return MA_NOT_IMPLEMENTED;
47885 }
47886 #endif
47887}
47888
47889
47890static 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)
47891{
47892 ma_result result;
47893 ma_wav* pWav;
47894
47895 (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. */
47896
47897 /* For now we're just allocating the decoder backend on the heap. */
47898 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
47899 if (pWav == NULL) {
47900 return MA_OUT_OF_MEMORY;
47901 }
47902
47903 result = ma_wav_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pWav);
47904 if (result != MA_SUCCESS) {
47905 ma_free(pWav, pAllocationCallbacks);
47906 return result;
47907 }
47908
47909 *ppBackend = pWav;
47910
47911 return MA_SUCCESS;
47912}
47913
47914static 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)
47915{
47916 ma_result result;
47917 ma_wav* pWav;
47918
47919 (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. */
47920
47921 /* For now we're just allocating the decoder backend on the heap. */
47922 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
47923 if (pWav == NULL) {
47924 return MA_OUT_OF_MEMORY;
47925 }
47926
47927 result = ma_wav_init_file(pFilePath, pConfig, pAllocationCallbacks, pWav);
47928 if (result != MA_SUCCESS) {
47929 ma_free(pWav, pAllocationCallbacks);
47930 return result;
47931 }
47932
47933 *ppBackend = pWav;
47934
47935 return MA_SUCCESS;
47936}
47937
47938static 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)
47939{
47940 ma_result result;
47941 ma_wav* pWav;
47942
47943 (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. */
47944
47945 /* For now we're just allocating the decoder backend on the heap. */
47946 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
47947 if (pWav == NULL) {
47948 return MA_OUT_OF_MEMORY;
47949 }
47950
47951 result = ma_wav_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pWav);
47952 if (result != MA_SUCCESS) {
47953 ma_free(pWav, pAllocationCallbacks);
47954 return result;
47955 }
47956
47957 *ppBackend = pWav;
47958
47959 return MA_SUCCESS;
47960}
47961
47962static 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)
47963{
47964 ma_result result;
47965 ma_wav* pWav;
47966
47967 (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. */
47968
47969 /* For now we're just allocating the decoder backend on the heap. */
47970 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
47971 if (pWav == NULL) {
47972 return MA_OUT_OF_MEMORY;
47973 }
47974
47975 result = ma_wav_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pWav);
47976 if (result != MA_SUCCESS) {
47977 ma_free(pWav, pAllocationCallbacks);
47978 return result;
47979 }
47980
47981 *ppBackend = pWav;
47982
47983 return MA_SUCCESS;
47984}
47985
47986static void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
47987{
47988 ma_wav* pWav = (ma_wav*)pBackend;
47989
47990 (void)pUserData;
47991
47992 ma_wav_uninit(pWav, pAllocationCallbacks);
47993 ma_free(pWav, pAllocationCallbacks);
47994}
47995
47996static ma_result ma_decoding_backend_get_channel_map__wav(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap)
47997{
47998 ma_wav* pWav = (ma_wav*)pBackend;
47999
48000 (void)pUserData;
48001
48002 return ma_wav_get_data_format(pWav, NULL, NULL, NULL, pChannelMap, channelMapCap);
48003}
48004
48005static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav =
48006{
48007 ma_decoding_backend_init__wav,
48008 ma_decoding_backend_init_file__wav,
48009 ma_decoding_backend_init_file_w__wav,
48010 ma_decoding_backend_init_memory__wav,
48011 ma_decoding_backend_uninit__wav,
48012 ma_decoding_backend_get_channel_map__wav
48013};
48014
48015static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
48016{
48017 return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder);
48018}
48019#endif /* dr_wav_h */
48020
48021/* FLAC */
48022#ifdef dr_flac_h
48023#define MA_HAS_FLAC
48024
48025typedef struct
48026{
48027 ma_data_source_base ds;
48028 ma_read_proc onRead;
48029 ma_seek_proc onSeek;
48030 ma_tell_proc onTell;
48031 void* pReadSeekTellUserData;
48032 ma_format format; /* Can be f32, s16 or s32. */
48033#if !defined(MA_NO_FLAC)
48034 drflac* dr;
48035#endif
48036} ma_flac;
48037
48038MA_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);
48039MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac);
48040MA_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);
48041MA_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);
48042MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks);
48043MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
48044MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex);
48045MA_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);
48046MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor);
48047MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength);
48048
48049
48050static ma_result ma_flac_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
48051{
48052 return ma_flac_read_pcm_frames((ma_flac*)pDataSource, pFramesOut, frameCount, pFramesRead);
48053}
48054
48055static ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
48056{
48057 return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex);
48058}
48059
48060static ma_result ma_flac_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
48061{
48062 return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, NULL, 0);
48063}
48064
48065static ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
48066{
48067 return ma_flac_get_cursor_in_pcm_frames((ma_flac*)pDataSource, pCursor);
48068}
48069
48070static ma_result ma_flac_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
48071{
48072 return ma_flac_get_length_in_pcm_frames((ma_flac*)pDataSource, pLength);
48073}
48074
48075static ma_data_source_vtable g_ma_flac_ds_vtable =
48076{
48077 ma_flac_ds_read,
48078 ma_flac_ds_seek,
48079 NULL, /* onMap() */
48080 NULL, /* onUnmap() */
48081 ma_flac_ds_get_data_format,
48082 ma_flac_ds_get_cursor,
48083 ma_flac_ds_get_length
48084};
48085
48086
48087#if !defined(MA_NO_FLAC)
48088static drflac_allocation_callbacks drflac_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks)
48089{
48090 drflac_allocation_callbacks callbacks;
48091
48092 if (pAllocationCallbacks != NULL) {
48093 callbacks.onMalloc = pAllocationCallbacks->onMalloc;
48094 callbacks.onRealloc = pAllocationCallbacks->onRealloc;
48095 callbacks.onFree = pAllocationCallbacks->onFree;
48096 callbacks.pUserData = pAllocationCallbacks->pUserData;
48097 } else {
48098 callbacks.onMalloc = ma__malloc_default;
48099 callbacks.onRealloc = ma__realloc_default;
48100 callbacks.onFree = ma__free_default;
48101 callbacks.pUserData = NULL;
48102 }
48103
48104 return callbacks;
48105}
48106
48107static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
48108{
48109 ma_flac* pFlac = (ma_flac*)pUserData;
48110 ma_result result;
48111 size_t bytesRead;
48112
48113 MA_ASSERT(pFlac != NULL);
48114
48115 result = pFlac->onRead(pFlac->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
48116 (void)result;
48117
48118 return bytesRead;
48119}
48120
48121static drflac_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, drflac_seek_origin origin)
48122{
48123 ma_flac* pFlac = (ma_flac*)pUserData;
48124 ma_result result;
48125 ma_seek_origin maSeekOrigin;
48126
48127 MA_ASSERT(pFlac != NULL);
48128
48129 maSeekOrigin = ma_seek_origin_start;
48130 if (origin == drflac_seek_origin_current) {
48131 maSeekOrigin = ma_seek_origin_current;
48132 }
48133
48134 result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin);
48135 if (result != MA_SUCCESS) {
48136 return MA_FALSE;
48137 }
48138
48139 return MA_TRUE;
48140}
48141#endif
48142
48143static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac)
48144{
48145 ma_result result;
48146 ma_data_source_config dataSourceConfig;
48147
48148 if (pFlac == NULL) {
48149 return MA_INVALID_ARGS;
48150 }
48151
48152 MA_ZERO_OBJECT(pFlac);
48153 pFlac->format = ma_format_f32; /* f32 by default. */
48154
48155 if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) {
48156 pFlac->format = pConfig->preferredFormat;
48157 } else {
48158 /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
48159 }
48160
48161 dataSourceConfig = ma_data_source_config_init();
48162 dataSourceConfig.vtable = &g_ma_flac_ds_vtable;
48163
48164 result = ma_data_source_init(&dataSourceConfig, &pFlac->ds);
48165 if (result != MA_SUCCESS) {
48166 return result; /* Failed to initialize the base data source. */
48167 }
48168
48169 return MA_SUCCESS;
48170}
48171
48172MA_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)
48173{
48174 ma_result result;
48175
48176 result = ma_flac_init_internal(pConfig, pFlac);
48177 if (result != MA_SUCCESS) {
48178 return result;
48179 }
48180
48181 if (onRead == NULL || onSeek == NULL) {
48182 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
48183 }
48184
48185 pFlac->onRead = onRead;
48186 pFlac->onSeek = onSeek;
48187 pFlac->onTell = onTell;
48188 pFlac->pReadSeekTellUserData = pReadSeekTellUserData;
48189
48190 #if !defined(MA_NO_FLAC)
48191 {
48192 drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
48193
48194 pFlac->dr = drflac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, &flacAllocationCallbacks);
48195 if (pFlac->dr == NULL) {
48196 return MA_INVALID_FILE;
48197 }
48198
48199 return MA_SUCCESS;
48200 }
48201 #else
48202 {
48203 /* flac is disabled. */
48204 (void)pAllocationCallbacks;
48205 return MA_NOT_IMPLEMENTED;
48206 }
48207 #endif
48208}
48209
48210MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac)
48211{
48212 ma_result result;
48213
48214 result = ma_flac_init_internal(pConfig, pFlac);
48215 if (result != MA_SUCCESS) {
48216 return result;
48217 }
48218
48219 #if !defined(MA_NO_FLAC)
48220 {
48221 drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
48222
48223 pFlac->dr = drflac_open_file(pFilePath, &flacAllocationCallbacks);
48224 if (pFlac->dr == NULL) {
48225 return MA_INVALID_FILE;
48226 }
48227
48228 return MA_SUCCESS;
48229 }
48230 #else
48231 {
48232 /* flac is disabled. */
48233 (void)pFilePath;
48234 (void)pAllocationCallbacks;
48235 return MA_NOT_IMPLEMENTED;
48236 }
48237 #endif
48238}
48239
48240MA_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)
48241{
48242 ma_result result;
48243
48244 result = ma_flac_init_internal(pConfig, pFlac);
48245 if (result != MA_SUCCESS) {
48246 return result;
48247 }
48248
48249 #if !defined(MA_NO_FLAC)
48250 {
48251 drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
48252
48253 pFlac->dr = drflac_open_file_w(pFilePath, &flacAllocationCallbacks);
48254 if (pFlac->dr == NULL) {
48255 return MA_INVALID_FILE;
48256 }
48257
48258 return MA_SUCCESS;
48259 }
48260 #else
48261 {
48262 /* flac is disabled. */
48263 (void)pFilePath;
48264 (void)pAllocationCallbacks;
48265 return MA_NOT_IMPLEMENTED;
48266 }
48267 #endif
48268}
48269
48270MA_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)
48271{
48272 ma_result result;
48273
48274 result = ma_flac_init_internal(pConfig, pFlac);
48275 if (result != MA_SUCCESS) {
48276 return result;
48277 }
48278
48279 #if !defined(MA_NO_FLAC)
48280 {
48281 drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
48282
48283 pFlac->dr = drflac_open_memory(pData, dataSize, &flacAllocationCallbacks);
48284 if (pFlac->dr == NULL) {
48285 return MA_INVALID_FILE;
48286 }
48287
48288 return MA_SUCCESS;
48289 }
48290 #else
48291 {
48292 /* flac is disabled. */
48293 (void)pData;
48294 (void)dataSize;
48295 (void)pAllocationCallbacks;
48296 return MA_NOT_IMPLEMENTED;
48297 }
48298 #endif
48299}
48300
48301MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks)
48302{
48303 if (pFlac == NULL) {
48304 return;
48305 }
48306
48307 (void)pAllocationCallbacks;
48308
48309 #if !defined(MA_NO_FLAC)
48310 {
48311 drflac_close(pFlac->dr);
48312 }
48313 #else
48314 {
48315 /* flac is disabled. Should never hit this since initialization would have failed. */
48316 MA_ASSERT(MA_FALSE);
48317 }
48318 #endif
48319
48320 ma_data_source_uninit(&pFlac->ds);
48321}
48322
48323MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
48324{
48325 if (pFlac == NULL) {
48326 return MA_INVALID_ARGS;
48327 }
48328
48329 #if !defined(MA_NO_FLAC)
48330 {
48331 /* We always use floating point format. */
48332 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
48333 ma_uint64 totalFramesRead = 0;
48334 ma_format format;
48335
48336 ma_flac_get_data_format(pFlac, &format, NULL, NULL, NULL, 0);
48337
48338 switch (format)
48339 {
48340 case ma_format_f32:
48341 {
48342 totalFramesRead = drflac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut);
48343 } break;
48344
48345 case ma_format_s16:
48346 {
48347 totalFramesRead = drflac_read_pcm_frames_s16(pFlac->dr, frameCount, (drflac_int16*)pFramesOut);
48348 } break;
48349
48350 case ma_format_s32:
48351 {
48352 totalFramesRead = drflac_read_pcm_frames_s32(pFlac->dr, frameCount, (drflac_int32*)pFramesOut);
48353 } break;
48354
48355 case ma_format_u8:
48356 case ma_format_s24:
48357 case ma_format_unknown:
48358 default:
48359 {
48360 return MA_INVALID_OPERATION;
48361 };
48362 }
48363
48364 /* In the future we'll update dr_flac to return MA_AT_END for us. */
48365 if (totalFramesRead == 0) {
48366 result = MA_AT_END;
48367 }
48368
48369 if (pFramesRead != NULL) {
48370 *pFramesRead = totalFramesRead;
48371 }
48372
48373 return result;
48374 }
48375 #else
48376 {
48377 /* flac is disabled. Should never hit this since initialization would have failed. */
48378 MA_ASSERT(MA_FALSE);
48379
48380 (void)pFramesOut;
48381 (void)frameCount;
48382 (void)pFramesRead;
48383
48384 return MA_NOT_IMPLEMENTED;
48385 }
48386 #endif
48387}
48388
48389MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex)
48390{
48391 if (pFlac == NULL) {
48392 return MA_INVALID_ARGS;
48393 }
48394
48395 #if !defined(MA_NO_FLAC)
48396 {
48397 drflac_bool32 flacResult;
48398
48399 flacResult = drflac_seek_to_pcm_frame(pFlac->dr, frameIndex);
48400 if (flacResult != DRFLAC_TRUE) {
48401 return MA_ERROR;
48402 }
48403
48404 return MA_SUCCESS;
48405 }
48406 #else
48407 {
48408 /* flac is disabled. Should never hit this since initialization would have failed. */
48409 MA_ASSERT(MA_FALSE);
48410
48411 (void)frameIndex;
48412
48413 return MA_NOT_IMPLEMENTED;
48414 }
48415 #endif
48416}
48417
48418MA_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)
48419{
48420 /* Defaults for safety. */
48421 if (pFormat != NULL) {
48422 *pFormat = ma_format_unknown;
48423 }
48424 if (pChannels != NULL) {
48425 *pChannels = 0;
48426 }
48427 if (pSampleRate != NULL) {
48428 *pSampleRate = 0;
48429 }
48430 if (pChannelMap != NULL) {
48431 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
48432 }
48433
48434 if (pFlac == NULL) {
48435 return MA_INVALID_OPERATION;
48436 }
48437
48438 if (pFormat != NULL) {
48439 *pFormat = pFlac->format;
48440 }
48441
48442 #if !defined(MA_NO_FLAC)
48443 {
48444 if (pChannels != NULL) {
48445 *pChannels = pFlac->dr->channels;
48446 }
48447
48448 if (pSampleRate != NULL) {
48449 *pSampleRate = pFlac->dr->sampleRate;
48450 }
48451
48452 if (pChannelMap != NULL) {
48453 ma_get_standard_channel_map(ma_standard_channel_map_microsoft, (ma_uint32)ma_min(pFlac->dr->channels, channelMapCap), pChannelMap);
48454 }
48455
48456 return MA_SUCCESS;
48457 }
48458 #else
48459 {
48460 /* flac is disabled. Should never hit this since initialization would have failed. */
48461 MA_ASSERT(MA_FALSE);
48462 return MA_NOT_IMPLEMENTED;
48463 }
48464 #endif
48465}
48466
48467MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor)
48468{
48469 if (pCursor == NULL) {
48470 return MA_INVALID_ARGS;
48471 }
48472
48473 *pCursor = 0; /* Safety. */
48474
48475 if (pFlac == NULL) {
48476 return MA_INVALID_ARGS;
48477 }
48478
48479 #if !defined(MA_NO_FLAC)
48480 {
48481 *pCursor = pFlac->dr->currentPCMFrame;
48482
48483 return MA_SUCCESS;
48484 }
48485 #else
48486 {
48487 /* flac is disabled. Should never hit this since initialization would have failed. */
48488 MA_ASSERT(MA_FALSE);
48489 return MA_NOT_IMPLEMENTED;
48490 }
48491 #endif
48492}
48493
48494MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength)
48495{
48496 if (pLength == NULL) {
48497 return MA_INVALID_ARGS;
48498 }
48499
48500 *pLength = 0; /* Safety. */
48501
48502 if (pFlac == NULL) {
48503 return MA_INVALID_ARGS;
48504 }
48505
48506 #if !defined(MA_NO_FLAC)
48507 {
48508 *pLength = pFlac->dr->totalPCMFrameCount;
48509
48510 return MA_SUCCESS;
48511 }
48512 #else
48513 {
48514 /* flac is disabled. Should never hit this since initialization would have failed. */
48515 MA_ASSERT(MA_FALSE);
48516 return MA_NOT_IMPLEMENTED;
48517 }
48518 #endif
48519}
48520
48521
48522static 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)
48523{
48524 ma_result result;
48525 ma_flac* pFlac;
48526
48527 (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. */
48528
48529 /* For now we're just allocating the decoder backend on the heap. */
48530 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
48531 if (pFlac == NULL) {
48532 return MA_OUT_OF_MEMORY;
48533 }
48534
48535 result = ma_flac_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pFlac);
48536 if (result != MA_SUCCESS) {
48537 ma_free(pFlac, pAllocationCallbacks);
48538 return result;
48539 }
48540
48541 *ppBackend = pFlac;
48542
48543 return MA_SUCCESS;
48544}
48545
48546static 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)
48547{
48548 ma_result result;
48549 ma_flac* pFlac;
48550
48551 (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. */
48552
48553 /* For now we're just allocating the decoder backend on the heap. */
48554 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
48555 if (pFlac == NULL) {
48556 return MA_OUT_OF_MEMORY;
48557 }
48558
48559 result = ma_flac_init_file(pFilePath, pConfig, pAllocationCallbacks, pFlac);
48560 if (result != MA_SUCCESS) {
48561 ma_free(pFlac, pAllocationCallbacks);
48562 return result;
48563 }
48564
48565 *ppBackend = pFlac;
48566
48567 return MA_SUCCESS;
48568}
48569
48570static 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)
48571{
48572 ma_result result;
48573 ma_flac* pFlac;
48574
48575 (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. */
48576
48577 /* For now we're just allocating the decoder backend on the heap. */
48578 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
48579 if (pFlac == NULL) {
48580 return MA_OUT_OF_MEMORY;
48581 }
48582
48583 result = ma_flac_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pFlac);
48584 if (result != MA_SUCCESS) {
48585 ma_free(pFlac, pAllocationCallbacks);
48586 return result;
48587 }
48588
48589 *ppBackend = pFlac;
48590
48591 return MA_SUCCESS;
48592}
48593
48594static 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)
48595{
48596 ma_result result;
48597 ma_flac* pFlac;
48598
48599 (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. */
48600
48601 /* For now we're just allocating the decoder backend on the heap. */
48602 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
48603 if (pFlac == NULL) {
48604 return MA_OUT_OF_MEMORY;
48605 }
48606
48607 result = ma_flac_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pFlac);
48608 if (result != MA_SUCCESS) {
48609 ma_free(pFlac, pAllocationCallbacks);
48610 return result;
48611 }
48612
48613 *ppBackend = pFlac;
48614
48615 return MA_SUCCESS;
48616}
48617
48618static void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
48619{
48620 ma_flac* pFlac = (ma_flac*)pBackend;
48621
48622 (void)pUserData;
48623
48624 ma_flac_uninit(pFlac, pAllocationCallbacks);
48625 ma_free(pFlac, pAllocationCallbacks);
48626}
48627
48628static ma_result ma_decoding_backend_get_channel_map__flac(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap)
48629{
48630 ma_flac* pFlac = (ma_flac*)pBackend;
48631
48632 (void)pUserData;
48633
48634 return ma_flac_get_data_format(pFlac, NULL, NULL, NULL, pChannelMap, channelMapCap);
48635}
48636
48637static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac =
48638{
48639 ma_decoding_backend_init__flac,
48640 ma_decoding_backend_init_file__flac,
48641 ma_decoding_backend_init_file_w__flac,
48642 ma_decoding_backend_init_memory__flac,
48643 ma_decoding_backend_uninit__flac,
48644 ma_decoding_backend_get_channel_map__flac
48645};
48646
48647static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
48648{
48649 return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder);
48650}
48651#endif /* dr_flac_h */
48652
48653/* MP3 */
48654#ifdef dr_mp3_h
48655#define MA_HAS_MP3
48656
48657typedef struct
48658{
48659 ma_data_source_base ds;
48660 ma_read_proc onRead;
48661 ma_seek_proc onSeek;
48662 ma_tell_proc onTell;
48663 void* pReadSeekTellUserData;
48664 ma_format format; /* Can be f32 or s16. */
48665#if !defined(MA_NO_MP3)
48666 drmp3 dr;
48667#endif
48668} ma_mp3;
48669
48670MA_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);
48671MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3);
48672MA_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);
48673MA_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);
48674MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks);
48675MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
48676MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex);
48677MA_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);
48678MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor);
48679MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength);
48680
48681
48682static ma_result ma_mp3_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
48683{
48684 return ma_mp3_read_pcm_frames((ma_mp3*)pDataSource, pFramesOut, frameCount, pFramesRead);
48685}
48686
48687static ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
48688{
48689 return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex);
48690}
48691
48692static ma_result ma_mp3_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
48693{
48694 return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, NULL, 0);
48695}
48696
48697static ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
48698{
48699 return ma_mp3_get_cursor_in_pcm_frames((ma_mp3*)pDataSource, pCursor);
48700}
48701
48702static ma_result ma_mp3_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
48703{
48704 return ma_mp3_get_length_in_pcm_frames((ma_mp3*)pDataSource, pLength);
48705}
48706
48707static ma_data_source_vtable g_ma_mp3_ds_vtable =
48708{
48709 ma_mp3_ds_read,
48710 ma_mp3_ds_seek,
48711 NULL, /* onMap() */
48712 NULL, /* onUnmap() */
48713 ma_mp3_ds_get_data_format,
48714 ma_mp3_ds_get_cursor,
48715 ma_mp3_ds_get_length
48716};
48717
48718
48719#if !defined(MA_NO_MP3)
48720static drmp3_allocation_callbacks drmp3_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks)
48721{
48722 drmp3_allocation_callbacks callbacks;
48723
48724 if (pAllocationCallbacks != NULL) {
48725 callbacks.onMalloc = pAllocationCallbacks->onMalloc;
48726 callbacks.onRealloc = pAllocationCallbacks->onRealloc;
48727 callbacks.onFree = pAllocationCallbacks->onFree;
48728 callbacks.pUserData = pAllocationCallbacks->pUserData;
48729 } else {
48730 callbacks.onMalloc = ma__malloc_default;
48731 callbacks.onRealloc = ma__realloc_default;
48732 callbacks.onFree = ma__free_default;
48733 callbacks.pUserData = NULL;
48734 }
48735
48736 return callbacks;
48737}
48738
48739static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
48740{
48741 ma_mp3* pMP3 = (ma_mp3*)pUserData;
48742 ma_result result;
48743 size_t bytesRead;
48744
48745 MA_ASSERT(pMP3 != NULL);
48746
48747 result = pMP3->onRead(pMP3->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
48748 (void)result;
48749
48750 return bytesRead;
48751}
48752
48753static drmp3_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, drmp3_seek_origin origin)
48754{
48755 ma_mp3* pMP3 = (ma_mp3*)pUserData;
48756 ma_result result;
48757 ma_seek_origin maSeekOrigin;
48758
48759 MA_ASSERT(pMP3 != NULL);
48760
48761 maSeekOrigin = ma_seek_origin_start;
48762 if (origin == drmp3_seek_origin_current) {
48763 maSeekOrigin = ma_seek_origin_current;
48764 }
48765
48766 result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin);
48767 if (result != MA_SUCCESS) {
48768 return MA_FALSE;
48769 }
48770
48771 return MA_TRUE;
48772}
48773#endif
48774
48775static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3)
48776{
48777 ma_result result;
48778 ma_data_source_config dataSourceConfig;
48779
48780 if (pMP3 == NULL) {
48781 return MA_INVALID_ARGS;
48782 }
48783
48784 MA_ZERO_OBJECT(pMP3);
48785 pMP3->format = ma_format_f32; /* f32 by default. */
48786
48787 if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) {
48788 pMP3->format = pConfig->preferredFormat;
48789 } else {
48790 /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
48791 }
48792
48793 dataSourceConfig = ma_data_source_config_init();
48794 dataSourceConfig.vtable = &g_ma_mp3_ds_vtable;
48795
48796 result = ma_data_source_init(&dataSourceConfig, &pMP3->ds);
48797 if (result != MA_SUCCESS) {
48798 return result; /* Failed to initialize the base data source. */
48799 }
48800
48801 return MA_SUCCESS;
48802}
48803
48804MA_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)
48805{
48806 ma_result result;
48807
48808 result = ma_mp3_init_internal(pConfig, pMP3);
48809 if (result != MA_SUCCESS) {
48810 return result;
48811 }
48812
48813 if (onRead == NULL || onSeek == NULL) {
48814 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
48815 }
48816
48817 pMP3->onRead = onRead;
48818 pMP3->onSeek = onSeek;
48819 pMP3->onTell = onTell;
48820 pMP3->pReadSeekTellUserData = pReadSeekTellUserData;
48821
48822 #if !defined(MA_NO_MP3)
48823 {
48824 drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
48825 drmp3_bool32 mp3Result;
48826
48827 mp3Result = drmp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, &mp3AllocationCallbacks);
48828 if (mp3Result != MA_TRUE) {
48829 return MA_INVALID_FILE;
48830 }
48831
48832 return MA_SUCCESS;
48833 }
48834 #else
48835 {
48836 /* mp3 is disabled. */
48837 (void)pAllocationCallbacks;
48838 return MA_NOT_IMPLEMENTED;
48839 }
48840 #endif
48841}
48842
48843MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3)
48844{
48845 ma_result result;
48846
48847 result = ma_mp3_init_internal(pConfig, pMP3);
48848 if (result != MA_SUCCESS) {
48849 return result;
48850 }
48851
48852 #if !defined(MA_NO_MP3)
48853 {
48854 drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
48855 drmp3_bool32 mp3Result;
48856
48857 mp3Result = drmp3_init_file(&pMP3->dr, pFilePath, &mp3AllocationCallbacks);
48858 if (mp3Result != MA_TRUE) {
48859 return MA_INVALID_FILE;
48860 }
48861
48862 return MA_SUCCESS;
48863 }
48864 #else
48865 {
48866 /* mp3 is disabled. */
48867 (void)pFilePath;
48868 (void)pAllocationCallbacks;
48869 return MA_NOT_IMPLEMENTED;
48870 }
48871 #endif
48872}
48873
48874MA_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)
48875{
48876 ma_result result;
48877
48878 result = ma_mp3_init_internal(pConfig, pMP3);
48879 if (result != MA_SUCCESS) {
48880 return result;
48881 }
48882
48883 #if !defined(MA_NO_MP3)
48884 {
48885 drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
48886 drmp3_bool32 mp3Result;
48887
48888 mp3Result = drmp3_init_file_w(&pMP3->dr, pFilePath, &mp3AllocationCallbacks);
48889 if (mp3Result != MA_TRUE) {
48890 return MA_INVALID_FILE;
48891 }
48892
48893 return MA_SUCCESS;
48894 }
48895 #else
48896 {
48897 /* mp3 is disabled. */
48898 (void)pFilePath;
48899 (void)pAllocationCallbacks;
48900 return MA_NOT_IMPLEMENTED;
48901 }
48902 #endif
48903}
48904
48905MA_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)
48906{
48907 ma_result result;
48908
48909 result = ma_mp3_init_internal(pConfig, pMP3);
48910 if (result != MA_SUCCESS) {
48911 return result;
48912 }
48913
48914 #if !defined(MA_NO_MP3)
48915 {
48916 drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
48917 drmp3_bool32 mp3Result;
48918
48919 mp3Result = drmp3_init_memory(&pMP3->dr, pData, dataSize, &mp3AllocationCallbacks);
48920 if (mp3Result != MA_TRUE) {
48921 return MA_INVALID_FILE;
48922 }
48923
48924 return MA_SUCCESS;
48925 }
48926 #else
48927 {
48928 /* mp3 is disabled. */
48929 (void)pData;
48930 (void)dataSize;
48931 (void)pAllocationCallbacks;
48932 return MA_NOT_IMPLEMENTED;
48933 }
48934 #endif
48935}
48936
48937MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks)
48938{
48939 if (pMP3 == NULL) {
48940 return;
48941 }
48942
48943 (void)pAllocationCallbacks;
48944
48945 #if !defined(MA_NO_MP3)
48946 {
48947 drmp3_uninit(&pMP3->dr);
48948 }
48949 #else
48950 {
48951 /* mp3 is disabled. Should never hit this since initialization would have failed. */
48952 MA_ASSERT(MA_FALSE);
48953 }
48954 #endif
48955
48956 ma_data_source_uninit(&pMP3->ds);
48957}
48958
48959MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
48960{
48961 if (pMP3 == NULL) {
48962 return MA_INVALID_ARGS;
48963 }
48964
48965 #if !defined(MA_NO_MP3)
48966 {
48967 /* We always use floating point format. */
48968 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
48969 ma_uint64 totalFramesRead = 0;
48970 ma_format format;
48971
48972 ma_mp3_get_data_format(pMP3, &format, NULL, NULL, NULL, 0);
48973
48974 switch (format)
48975 {
48976 case ma_format_f32:
48977 {
48978 totalFramesRead = drmp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut);
48979 } break;
48980
48981 case ma_format_s16:
48982 {
48983 totalFramesRead = drmp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (drmp3_int16*)pFramesOut);
48984 } break;
48985
48986 case ma_format_u8:
48987 case ma_format_s24:
48988 case ma_format_s32:
48989 case ma_format_unknown:
48990 default:
48991 {
48992 return MA_INVALID_OPERATION;
48993 };
48994 }
48995
48996 /* In the future we'll update dr_mp3 to return MA_AT_END for us. */
48997 if (totalFramesRead == 0) {
48998 result = MA_AT_END;
48999 }
49000
49001 if (pFramesRead != NULL) {
49002 *pFramesRead = totalFramesRead;
49003 }
49004
49005 return result;
49006 }
49007 #else
49008 {
49009 /* mp3 is disabled. Should never hit this since initialization would have failed. */
49010 MA_ASSERT(MA_FALSE);
49011
49012 (void)pFramesOut;
49013 (void)frameCount;
49014 (void)pFramesRead;
49015
49016 return MA_NOT_IMPLEMENTED;
49017 }
49018 #endif
49019}
49020
49021MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex)
49022{
49023 if (pMP3 == NULL) {
49024 return MA_INVALID_ARGS;
49025 }
49026
49027 #if !defined(MA_NO_MP3)
49028 {
49029 drmp3_bool32 mp3Result;
49030
49031 mp3Result = drmp3_seek_to_pcm_frame(&pMP3->dr, frameIndex);
49032 if (mp3Result != DRMP3_TRUE) {
49033 return MA_ERROR;
49034 }
49035
49036 return MA_SUCCESS;
49037 }
49038 #else
49039 {
49040 /* mp3 is disabled. Should never hit this since initialization would have failed. */
49041 MA_ASSERT(MA_FALSE);
49042
49043 (void)frameIndex;
49044
49045 return MA_NOT_IMPLEMENTED;
49046 }
49047 #endif
49048}
49049
49050MA_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)
49051{
49052 /* Defaults for safety. */
49053 if (pFormat != NULL) {
49054 *pFormat = ma_format_unknown;
49055 }
49056 if (pChannels != NULL) {
49057 *pChannels = 0;
49058 }
49059 if (pSampleRate != NULL) {
49060 *pSampleRate = 0;
49061 }
49062 if (pChannelMap != NULL) {
49063 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
49064 }
49065
49066 if (pMP3 == NULL) {
49067 return MA_INVALID_OPERATION;
49068 }
49069
49070 if (pFormat != NULL) {
49071 *pFormat = pMP3->format;
49072 }
49073
49074 #if !defined(MA_NO_MP3)
49075 {
49076 if (pChannels != NULL) {
49077 *pChannels = pMP3->dr.channels;
49078 }
49079
49080 if (pSampleRate != NULL) {
49081 *pSampleRate = pMP3->dr.sampleRate;
49082 }
49083
49084 if (pChannelMap != NULL) {
49085 ma_get_standard_channel_map(ma_standard_channel_map_default, (ma_uint32)ma_min(pMP3->dr.channels, channelMapCap), pChannelMap);
49086 }
49087
49088 return MA_SUCCESS;
49089 }
49090 #else
49091 {
49092 /* mp3 is disabled. Should never hit this since initialization would have failed. */
49093 MA_ASSERT(MA_FALSE);
49094 return MA_NOT_IMPLEMENTED;
49095 }
49096 #endif
49097}
49098
49099MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor)
49100{
49101 if (pCursor == NULL) {
49102 return MA_INVALID_ARGS;
49103 }
49104
49105 *pCursor = 0; /* Safety. */
49106
49107 if (pMP3 == NULL) {
49108 return MA_INVALID_ARGS;
49109 }
49110
49111 #if !defined(MA_NO_MP3)
49112 {
49113 *pCursor = pMP3->dr.currentPCMFrame;
49114
49115 return MA_SUCCESS;
49116 }
49117 #else
49118 {
49119 /* mp3 is disabled. Should never hit this since initialization would have failed. */
49120 MA_ASSERT(MA_FALSE);
49121 return MA_NOT_IMPLEMENTED;
49122 }
49123 #endif
49124}
49125
49126MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength)
49127{
49128 if (pLength == NULL) {
49129 return MA_INVALID_ARGS;
49130 }
49131
49132 *pLength = 0; /* Safety. */
49133
49134 if (pMP3 == NULL) {
49135 return MA_INVALID_ARGS;
49136 }
49137
49138 #if !defined(MA_NO_MP3)
49139 {
49140 *pLength = drmp3_get_pcm_frame_count(&pMP3->dr);
49141
49142 return MA_SUCCESS;
49143 }
49144 #else
49145 {
49146 /* mp3 is disabled. Should never hit this since initialization would have failed. */
49147 MA_ASSERT(MA_FALSE);
49148 return MA_NOT_IMPLEMENTED;
49149 }
49150 #endif
49151}
49152
49153
49154static 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)
49155{
49156 ma_result result;
49157 ma_mp3* pMP3;
49158
49159 (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. */
49160
49161 /* For now we're just allocating the decoder backend on the heap. */
49162 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
49163 if (pMP3 == NULL) {
49164 return MA_OUT_OF_MEMORY;
49165 }
49166
49167 result = ma_mp3_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pMP3);
49168 if (result != MA_SUCCESS) {
49169 ma_free(pMP3, pAllocationCallbacks);
49170 return result;
49171 }
49172
49173 *ppBackend = pMP3;
49174
49175 return MA_SUCCESS;
49176}
49177
49178static 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)
49179{
49180 ma_result result;
49181 ma_mp3* pMP3;
49182
49183 (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. */
49184
49185 /* For now we're just allocating the decoder backend on the heap. */
49186 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
49187 if (pMP3 == NULL) {
49188 return MA_OUT_OF_MEMORY;
49189 }
49190
49191 result = ma_mp3_init_file(pFilePath, pConfig, pAllocationCallbacks, pMP3);
49192 if (result != MA_SUCCESS) {
49193 ma_free(pMP3, pAllocationCallbacks);
49194 return result;
49195 }
49196
49197 *ppBackend = pMP3;
49198
49199 return MA_SUCCESS;
49200}
49201
49202static 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)
49203{
49204 ma_result result;
49205 ma_mp3* pMP3;
49206
49207 (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. */
49208
49209 /* For now we're just allocating the decoder backend on the heap. */
49210 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
49211 if (pMP3 == NULL) {
49212 return MA_OUT_OF_MEMORY;
49213 }
49214
49215 result = ma_mp3_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pMP3);
49216 if (result != MA_SUCCESS) {
49217 ma_free(pMP3, pAllocationCallbacks);
49218 return result;
49219 }
49220
49221 *ppBackend = pMP3;
49222
49223 return MA_SUCCESS;
49224}
49225
49226static 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)
49227{
49228 ma_result result;
49229 ma_mp3* pMP3;
49230
49231 (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. */
49232
49233 /* For now we're just allocating the decoder backend on the heap. */
49234 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
49235 if (pMP3 == NULL) {
49236 return MA_OUT_OF_MEMORY;
49237 }
49238
49239 result = ma_mp3_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pMP3);
49240 if (result != MA_SUCCESS) {
49241 ma_free(pMP3, pAllocationCallbacks);
49242 return result;
49243 }
49244
49245 *ppBackend = pMP3;
49246
49247 return MA_SUCCESS;
49248}
49249
49250static void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
49251{
49252 ma_mp3* pMP3 = (ma_mp3*)pBackend;
49253
49254 (void)pUserData;
49255
49256 ma_mp3_uninit(pMP3, pAllocationCallbacks);
49257 ma_free(pMP3, pAllocationCallbacks);
49258}
49259
49260static ma_result ma_decoding_backend_get_channel_map__mp3(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap)
49261{
49262 ma_mp3* pMP3 = (ma_mp3*)pBackend;
49263
49264 (void)pUserData;
49265
49266 return ma_mp3_get_data_format(pMP3, NULL, NULL, NULL, pChannelMap, channelMapCap);
49267}
49268
49269static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 =
49270{
49271 ma_decoding_backend_init__mp3,
49272 ma_decoding_backend_init_file__mp3,
49273 ma_decoding_backend_init_file_w__mp3,
49274 ma_decoding_backend_init_memory__mp3,
49275 ma_decoding_backend_uninit__mp3,
49276 ma_decoding_backend_get_channel_map__mp3
49277};
49278
49279static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
49280{
49281 return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder);
49282}
49283#endif /* dr_mp3_h */
49284
49285/* Vorbis */
49286#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
49287#define MA_HAS_VORBIS
49288
49289/* The size in bytes of each chunk of data to read from the Vorbis stream. */
49290#define MA_VORBIS_DATA_CHUNK_SIZE 4096
49291
49292typedef struct
49293{
49294 ma_data_source_base ds;
49295 ma_read_proc onRead;
49296 ma_seek_proc onSeek;
49297 ma_tell_proc onTell;
49298 void* pReadSeekTellUserData;
49299 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. */
49300 ma_format format; /* Only f32 is allowed with stb_vorbis. */
49301 ma_uint32 channels;
49302 ma_uint32 sampleRate;
49303 ma_uint64 cursor;
49304#if !defined(MA_NO_VORBIS)
49305 stb_vorbis* stb;
49306 ma_bool32 usingPushMode;
49307 struct
49308 {
49309 ma_uint8* pData;
49310 size_t dataSize;
49311 size_t dataCapacity;
49312 ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */
49313 ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */
49314 float** ppPacketData;
49315 } push;
49316#endif
49317} ma_stbvorbis;
49318
49319MA_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);
49320MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis);
49321MA_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);
49322MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks);
49323MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
49324MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex);
49325MA_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);
49326MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor);
49327MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength);
49328
49329
49330static ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
49331{
49332 return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead);
49333}
49334
49335static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
49336{
49337 return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex);
49338}
49339
49340static ma_result ma_stbvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
49341{
49342 return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, NULL, 0);
49343}
49344
49345static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
49346{
49347 return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor);
49348}
49349
49350static ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
49351{
49352 return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength);
49353}
49354
49355static ma_data_source_vtable g_ma_stbvorbis_ds_vtable =
49356{
49357 ma_stbvorbis_ds_read,
49358 ma_stbvorbis_ds_seek,
49359 NULL, /* onMap() */
49360 NULL, /* onUnmap() */
49361 ma_stbvorbis_ds_get_data_format,
49362 ma_stbvorbis_ds_get_cursor,
49363 ma_stbvorbis_ds_get_length
49364};
49365
49366
49367static ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis)
49368{
49369 ma_result result;
49370 ma_data_source_config dataSourceConfig;
49371
49372 (void)pConfig;
49373
49374 if (pVorbis == NULL) {
49375 return MA_INVALID_ARGS;
49376 }
49377
49378 MA_ZERO_OBJECT(pVorbis);
49379 pVorbis->format = ma_format_f32; /* Only supporting f32. */
49380
49381 dataSourceConfig = ma_data_source_config_init();
49382 dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable;
49383
49384 result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds);
49385 if (result != MA_SUCCESS) {
49386 return result; /* Failed to initialize the base data source. */
49387 }
49388
49389 return MA_SUCCESS;
49390}
49391
49392#if !defined(MA_NO_VORBIS)
49393static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis)
49394{
49395 stb_vorbis_info info;
49396
49397 MA_ASSERT(pVorbis != NULL);
49398
49399 info = stb_vorbis_get_info(pVorbis->stb);
49400
49401 pVorbis->channels = info.channels;
49402 pVorbis->sampleRate = info.sample_rate;
49403
49404 return MA_SUCCESS;
49405}
49406#endif
49407
49408MA_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)
49409{
49410 ma_result result;
49411
49412 result = ma_stbvorbis_init_internal(pConfig, pVorbis);
49413 if (result != MA_SUCCESS) {
49414 return result;
49415 }
49416
49417 if (onRead == NULL || onSeek == NULL) {
49418 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
49419 }
49420
49421 pVorbis->onRead = onRead;
49422 pVorbis->onSeek = onSeek;
49423 pVorbis->onTell = onTell;
49424 pVorbis->pReadSeekTellUserData = pReadSeekTellUserData;
49425 ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks);
49426
49427 #if !defined(MA_NO_VORBIS)
49428 {
49429 /*
49430 stb_vorbis lacks a callback based API for it's pulling API which means we're stuck with the
49431 pushing API. In order for us to be able to successfully initialize the decoder we need to
49432 supply it with enough data. We need to keep loading data until we have enough.
49433 */
49434 stb_vorbis* stb;
49435 size_t dataSize = 0;
49436 size_t dataCapacity = 0;
49437 ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */
49438
49439 for (;;) {
49440 int vorbisError;
49441 int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */
49442 size_t bytesRead;
49443 ma_uint8* pNewData;
49444
49445 /* Allocate memory for the new chunk. */
49446 dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE;
49447 pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, pAllocationCallbacks);
49448 if (pNewData == NULL) {
49449 ma_free(pData, pAllocationCallbacks);
49450 return MA_OUT_OF_MEMORY;
49451 }
49452
49453 pData = pNewData;
49454
49455 /* Read in the next chunk. */
49456 result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead);
49457 dataSize += bytesRead;
49458
49459 if (result != MA_SUCCESS) {
49460 ma_free(pData, pAllocationCallbacks);
49461 return result;
49462 }
49463
49464 /* We have a maximum of 31 bits with stb_vorbis. */
49465 if (dataSize > INT_MAX) {
49466 ma_free(pData, pAllocationCallbacks);
49467 return MA_TOO_BIG;
49468 }
49469
49470 stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL);
49471 if (stb != NULL) {
49472 /*
49473 Successfully opened the Vorbis decoder. We might have some leftover unprocessed
49474 data so we'll need to move that down to the front.
49475 */
49476 dataSize -= (size_t)consumedDataSize; /* Consume the data. */
49477 MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize);
49478 break;
49479 } else {
49480 /* Failed to open the decoder. */
49481 if (vorbisError == VORBIS_need_more_data) {
49482 continue;
49483 } else {
49484 ma_free(pData, pAllocationCallbacks);
49485 return MA_ERROR; /* Failed to open the stb_vorbis decoder. */
49486 }
49487 }
49488 }
49489
49490 MA_ASSERT(stb != NULL);
49491 pVorbis->stb = stb;
49492 pVorbis->push.pData = pData;
49493 pVorbis->push.dataSize = dataSize;
49494 pVorbis->push.dataCapacity = dataCapacity;
49495
49496 pVorbis->usingPushMode = MA_TRUE;
49497
49498 result = ma_stbvorbis_post_init(pVorbis);
49499 if (result != MA_SUCCESS) {
49500 stb_vorbis_close(pVorbis->stb);
49501 ma_free(pData, pAllocationCallbacks);
49502 return result;
49503 }
49504
49505 return MA_SUCCESS;
49506 }
49507 #else
49508 {
49509 /* vorbis is disabled. */
49510 (void)pAllocationCallbacks;
49511 return MA_NOT_IMPLEMENTED;
49512 }
49513 #endif
49514}
49515
49516MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis)
49517{
49518 ma_result result;
49519
49520 result = ma_stbvorbis_init_internal(pConfig, pVorbis);
49521 if (result != MA_SUCCESS) {
49522 return result;
49523 }
49524
49525 #if !defined(MA_NO_VORBIS)
49526 {
49527 (void)pAllocationCallbacks; /* Don't know how to make use of this with stb_vorbis. */
49528
49529 /* We can use stb_vorbis' pull mode for file based streams. */
49530 pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL);
49531 if (pVorbis->stb == NULL) {
49532 return MA_INVALID_FILE;
49533 }
49534
49535 pVorbis->usingPushMode = MA_FALSE;
49536
49537 result = ma_stbvorbis_post_init(pVorbis);
49538 if (result != MA_SUCCESS) {
49539 stb_vorbis_close(pVorbis->stb);
49540 return result;
49541 }
49542
49543 return MA_SUCCESS;
49544 }
49545 #else
49546 {
49547 /* vorbis is disabled. */
49548 (void)pFilePath;
49549 (void)pAllocationCallbacks;
49550 return MA_NOT_IMPLEMENTED;
49551 }
49552 #endif
49553}
49554
49555MA_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)
49556{
49557 ma_result result;
49558
49559 result = ma_stbvorbis_init_internal(pConfig, pVorbis);
49560 if (result != MA_SUCCESS) {
49561 return result;
49562 }
49563
49564 #if !defined(MA_NO_VORBIS)
49565 {
49566 (void)pAllocationCallbacks;
49567
49568 /* stb_vorbis uses an int as it's size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */
49569 if (dataSize > INT_MAX) {
49570 return MA_TOO_BIG;
49571 }
49572
49573 pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL);
49574 if (pVorbis->stb == NULL) {
49575 return MA_INVALID_FILE;
49576 }
49577
49578 pVorbis->usingPushMode = MA_FALSE;
49579
49580 result = ma_stbvorbis_post_init(pVorbis);
49581 if (result != MA_SUCCESS) {
49582 stb_vorbis_close(pVorbis->stb);
49583 return result;
49584 }
49585
49586 return MA_SUCCESS;
49587 }
49588 #else
49589 {
49590 /* vorbis is disabled. */
49591 (void)pData;
49592 (void)dataSize;
49593 (void)pAllocationCallbacks;
49594 return MA_NOT_IMPLEMENTED;
49595 }
49596 #endif
49597}
49598
49599MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks)
49600{
49601 if (pVorbis == NULL) {
49602 return;
49603 }
49604
49605 #if !defined(MA_NO_VORBIS)
49606 {
49607 stb_vorbis_close(pVorbis->stb);
49608
49609 /* We'll have to clear some memory if we're using push mode. */
49610 if (pVorbis->usingPushMode) {
49611 ma_free(pVorbis->push.pData, pAllocationCallbacks);
49612 }
49613 }
49614 #else
49615 {
49616 /* vorbis is disabled. Should never hit this since initialization would have failed. */
49617 MA_ASSERT(MA_FALSE);
49618 }
49619 #endif
49620
49621 ma_data_source_uninit(&pVorbis->ds);
49622}
49623
49624MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
49625{
49626 if (pVorbis == NULL) {
49627 return MA_INVALID_ARGS;
49628 }
49629
49630 #if !defined(MA_NO_VORBIS)
49631 {
49632 /* We always use floating point format. */
49633 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
49634 ma_uint64 totalFramesRead = 0;
49635 ma_format format;
49636 ma_uint32 channels;
49637
49638 ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0);
49639
49640 if (format == ma_format_f32) {
49641 /* We read differently depending on whether or not we're using push mode. */
49642 if (pVorbis->usingPushMode) {
49643 /* Push mode. This is the complex case. */
49644 float* pFramesOutF32 = (float*)pFramesOut;
49645
49646 while (totalFramesRead < frameCount) {
49647 /* The first thing to do is read from any already-cached frames. */
49648 ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */
49649
49650 /* The output pointer can be null in which case we just treate it as a seek. */
49651 if (pFramesOut != NULL) {
49652 ma_uint64 iFrame;
49653 for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) {
49654 ma_uint32 iChannel;
49655 for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) {
49656 pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame];
49657 }
49658
49659 pFramesOutF32 += pVorbis->channels;
49660 }
49661 }
49662
49663 /* Update pointers and counters. */
49664 pVorbis->push.framesConsumed += framesToReadFromCache;
49665 pVorbis->push.framesRemaining -= framesToReadFromCache;
49666 totalFramesRead += framesToReadFromCache;
49667
49668 /* Don't bother reading any more frames right now if we've just finished loading. */
49669 if (totalFramesRead == frameCount) {
49670 break;
49671 }
49672
49673 MA_ASSERT(pVorbis->push.framesRemaining == 0);
49674
49675 /* Getting here means we've run out of cached frames. We'll need to load some more. */
49676 for (;;) {
49677 int samplesRead = 0;
49678 int consumedDataSize;
49679
49680 /* We need to case dataSize to an int, so make sure we can do it safely. */
49681 if (pVorbis->push.dataSize > INT_MAX) {
49682 break; /* Too big. */
49683 }
49684
49685 consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead);
49686 if (consumedDataSize != 0) {
49687 /* Successfully decoded a Vorbis frame. Consume the data. */
49688 pVorbis->push.dataSize -= (size_t)consumedDataSize;
49689 MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize);
49690
49691 pVorbis->push.framesConsumed = 0;
49692 pVorbis->push.framesRemaining = samplesRead;
49693
49694 break;
49695 } else {
49696 /* Not enough data. Read more. */
49697 size_t bytesRead;
49698
49699 /* Expand the data buffer if necessary. */
49700 if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) {
49701 size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE;
49702 ma_uint8* pNewData;
49703
49704 pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks);
49705 if (pNewData == NULL) {
49706 result = MA_OUT_OF_MEMORY;
49707 break;
49708 }
49709
49710 pVorbis->push.pData = pNewData;
49711 pVorbis->push.dataCapacity = newCap;
49712 }
49713
49714 /* We should have enough room to load some data. */
49715 result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead);
49716 pVorbis->push.dataSize += bytesRead;
49717
49718 if (result != MA_SUCCESS) {
49719 break; /* Failed to read any data. Get out. */
49720 }
49721 }
49722 }
49723
49724 /* 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). */
49725 if (result != MA_SUCCESS) {
49726 break;
49727 }
49728 }
49729 } else {
49730 /* 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. */
49731 while (totalFramesRead < frameCount) {
49732 ma_uint64 framesRemaining = (frameCount - totalFramesRead);
49733 int framesRead;
49734
49735 if (framesRemaining > INT_MAX) {
49736 framesRemaining = INT_MAX;
49737 }
49738
49739 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. */
49740 totalFramesRead += framesRead;
49741
49742 if (framesRead < framesRemaining) {
49743 break; /* Nothing left to read. Get out. */
49744 }
49745 }
49746 }
49747 } else {
49748 result = MA_INVALID_ARGS;
49749 }
49750
49751 pVorbis->cursor += totalFramesRead;
49752
49753 if (totalFramesRead == 0) {
49754 result = MA_AT_END;
49755 }
49756
49757 if (pFramesRead != NULL) {
49758 *pFramesRead = totalFramesRead;
49759 }
49760
49761 return result;
49762 }
49763 #else
49764 {
49765 /* vorbis is disabled. Should never hit this since initialization would have failed. */
49766 MA_ASSERT(MA_FALSE);
49767
49768 (void)pFramesOut;
49769 (void)frameCount;
49770 (void)pFramesRead;
49771
49772 return MA_NOT_IMPLEMENTED;
49773 }
49774 #endif
49775}
49776
49777MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex)
49778{
49779 if (pVorbis == NULL) {
49780 return MA_INVALID_ARGS;
49781 }
49782
49783 #if !defined(MA_NO_VORBIS)
49784 {
49785 /* Different seeking methods depending on whether or not we're using push mode. */
49786 if (pVorbis->usingPushMode) {
49787 /* Push mode. This is the complex case. */
49788 ma_result result;
49789 float buffer[4096];
49790
49791 /*
49792 This is terribly inefficient because stb_vorbis does not have a good seeking solution with it's push API. Currently this just performs
49793 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
49794 find the one containing the sample we need. Then we know exactly where to seek for stb_vorbis.
49795
49796 TODO: Use seeking logic documented for stb_vorbis_flush_pushdata().
49797 */
49798
49799 /* Seek to the start of the file to begin with. */
49800 result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start);
49801 if (result != MA_SUCCESS) {
49802 return result;
49803 }
49804
49805 stb_vorbis_flush_pushdata(pVorbis->stb);
49806 pVorbis->push.framesRemaining = 0;
49807 pVorbis->push.dataSize = 0;
49808
49809 /* Move the cursor back to the start. We'll increment this in the loop below. */
49810 pVorbis->cursor = 0;
49811
49812 while (pVorbis->cursor < frameIndex) {
49813 ma_uint64 framesRead;
49814 ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels;
49815 if (framesToRead > (frameIndex - pVorbis->cursor)) {
49816 framesToRead = (frameIndex - pVorbis->cursor);
49817 }
49818
49819 result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead);
49820 pVorbis->cursor += framesRead;
49821
49822 if (result != MA_SUCCESS) {
49823 return result;
49824 }
49825 }
49826 } else {
49827 /* Pull mode. This is the simple case. */
49828 int vorbisResult;
49829
49830 if (frameIndex > UINT_MAX) {
49831 return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */
49832 }
49833
49834 vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex); /* Safe cast. */
49835 if (vorbisResult == 0) {
49836 return MA_ERROR; /* See failed. */
49837 }
49838
49839 pVorbis->cursor = frameIndex;
49840 }
49841
49842 return MA_SUCCESS;
49843 }
49844 #else
49845 {
49846 /* vorbis is disabled. Should never hit this since initialization would have failed. */
49847 MA_ASSERT(MA_FALSE);
49848
49849 (void)frameIndex;
49850
49851 return MA_NOT_IMPLEMENTED;
49852 }
49853 #endif
49854}
49855
49856MA_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)
49857{
49858 /* Defaults for safety. */
49859 if (pFormat != NULL) {
49860 *pFormat = ma_format_unknown;
49861 }
49862 if (pChannels != NULL) {
49863 *pChannels = 0;
49864 }
49865 if (pSampleRate != NULL) {
49866 *pSampleRate = 0;
49867 }
49868 if (pChannelMap != NULL) {
49869 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
49870 }
49871
49872 if (pVorbis == NULL) {
49873 return MA_INVALID_OPERATION;
49874 }
49875
49876 if (pFormat != NULL) {
49877 *pFormat = pVorbis->format;
49878 }
49879
49880 #if !defined(MA_NO_VORBIS)
49881 {
49882 if (pChannels != NULL) {
49883 *pChannels = pVorbis->channels;
49884 }
49885
49886 if (pSampleRate != NULL) {
49887 *pSampleRate = pVorbis->sampleRate;
49888 }
49889
49890 if (pChannelMap != NULL) {
49891 ma_get_standard_channel_map(ma_standard_channel_map_vorbis, (ma_uint32)ma_min(pVorbis->channels, channelMapCap), pChannelMap);
49892 }
49893
49894 return MA_SUCCESS;
49895 }
49896 #else
49897 {
49898 /* vorbis is disabled. Should never hit this since initialization would have failed. */
49899 MA_ASSERT(MA_FALSE);
49900 return MA_NOT_IMPLEMENTED;
49901 }
49902 #endif
49903}
49904
49905MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor)
49906{
49907 if (pCursor == NULL) {
49908 return MA_INVALID_ARGS;
49909 }
49910
49911 *pCursor = 0; /* Safety. */
49912
49913 if (pVorbis == NULL) {
49914 return MA_INVALID_ARGS;
49915 }
49916
49917 #if !defined(MA_NO_VORBIS)
49918 {
49919 *pCursor = pVorbis->cursor;
49920
49921 return MA_SUCCESS;
49922 }
49923 #else
49924 {
49925 /* vorbis is disabled. Should never hit this since initialization would have failed. */
49926 MA_ASSERT(MA_FALSE);
49927 return MA_NOT_IMPLEMENTED;
49928 }
49929 #endif
49930}
49931
49932MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength)
49933{
49934 if (pLength == NULL) {
49935 return MA_INVALID_ARGS;
49936 }
49937
49938 *pLength = 0; /* Safety. */
49939
49940 if (pVorbis == NULL) {
49941 return MA_INVALID_ARGS;
49942 }
49943
49944 #if !defined(MA_NO_VORBIS)
49945 {
49946 if (pVorbis->usingPushMode) {
49947 *pLength = 0; /* I don't know of a good way to determine this reliably with stb_vorbis and push mode. */
49948 } else {
49949 *pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb);
49950 }
49951
49952 return MA_SUCCESS;
49953 }
49954 #else
49955 {
49956 /* vorbis is disabled. Should never hit this since initialization would have failed. */
49957 MA_ASSERT(MA_FALSE);
49958 return MA_NOT_IMPLEMENTED;
49959 }
49960 #endif
49961}
49962
49963
49964static 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)
49965{
49966 ma_result result;
49967 ma_stbvorbis* pVorbis;
49968
49969 (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. */
49970
49971 /* For now we're just allocating the decoder backend on the heap. */
49972 pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
49973 if (pVorbis == NULL) {
49974 return MA_OUT_OF_MEMORY;
49975 }
49976
49977 result = ma_stbvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis);
49978 if (result != MA_SUCCESS) {
49979 ma_free(pVorbis, pAllocationCallbacks);
49980 return result;
49981 }
49982
49983 *ppBackend = pVorbis;
49984
49985 return MA_SUCCESS;
49986}
49987
49988static 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)
49989{
49990 ma_result result;
49991 ma_stbvorbis* pVorbis;
49992
49993 (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. */
49994
49995 /* For now we're just allocating the decoder backend on the heap. */
49996 pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
49997 if (pVorbis == NULL) {
49998 return MA_OUT_OF_MEMORY;
49999 }
50000
50001 result = ma_stbvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis);
50002 if (result != MA_SUCCESS) {
50003 ma_free(pVorbis, pAllocationCallbacks);
50004 return result;
50005 }
50006
50007 *ppBackend = pVorbis;
50008
50009 return MA_SUCCESS;
50010}
50011
50012static 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)
50013{
50014 ma_result result;
50015 ma_stbvorbis* pVorbis;
50016
50017 (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. */
50018
50019 /* For now we're just allocating the decoder backend on the heap. */
50020 pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
50021 if (pVorbis == NULL) {
50022 return MA_OUT_OF_MEMORY;
50023 }
50024
50025 result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis);
50026 if (result != MA_SUCCESS) {
50027 ma_free(pVorbis, pAllocationCallbacks);
50028 return result;
50029 }
50030
50031 *ppBackend = pVorbis;
50032
50033 return MA_SUCCESS;
50034}
50035
50036static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
50037{
50038 ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend;
50039
50040 (void)pUserData;
50041
50042 ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks);
50043 ma_free(pVorbis, pAllocationCallbacks);
50044}
50045
50046static ma_result ma_decoding_backend_get_channel_map__stbvorbis(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap)
50047{
50048 ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend;
50049
50050 (void)pUserData;
50051
50052 return ma_stbvorbis_get_data_format(pVorbis, NULL, NULL, NULL, pChannelMap, channelMapCap);
50053}
50054
50055static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis =
50056{
50057 ma_decoding_backend_init__stbvorbis,
50058 ma_decoding_backend_init_file__stbvorbis,
50059 NULL, /* onInitFileW() */
50060 ma_decoding_backend_init_memory__stbvorbis,
50061 ma_decoding_backend_uninit__stbvorbis,
50062 ma_decoding_backend_get_channel_map__stbvorbis
50063};
50064
50065static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50066{
50067 return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder);
50068}
50069#endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */
50070
50071
50072
50073static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50074{
50075 MA_ASSERT(pDecoder != NULL);
50076
50077 if (pConfig != NULL) {
50078 return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks);
50079 } else {
50080 pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default();
50081 return MA_SUCCESS;
50082 }
50083}
50084
50085static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
50086{
50087 ma_uint64 framesRead = ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount);
50088
50089 if (pFramesRead != NULL) {
50090 *pFramesRead = framesRead;
50091 }
50092
50093 if (framesRead == 0) {
50094 return MA_AT_END;
50095 }
50096
50097 return MA_SUCCESS;
50098}
50099
50100static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
50101{
50102 return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex);
50103}
50104
50105static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
50106{
50107 ma_decoder* pDecoder = (ma_decoder*)pDataSource;
50108
50109 *pFormat = pDecoder->outputFormat;
50110 *pChannels = pDecoder->outputChannels;
50111 *pSampleRate = pDecoder->outputSampleRate;
50112
50113 return MA_SUCCESS;
50114}
50115
50116static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
50117{
50118 ma_decoder* pDecoder = (ma_decoder*)pDataSource;
50119
50120 return ma_decoder_get_cursor_in_pcm_frames(pDecoder, pCursor);
50121}
50122
50123static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
50124{
50125 ma_decoder* pDecoder = (ma_decoder*)pDataSource;
50126
50127 *pLength = ma_decoder_get_length_in_pcm_frames(pDecoder);
50128 if (*pLength == 0) {
50129 return MA_NOT_IMPLEMENTED;
50130 }
50131
50132 return MA_SUCCESS;
50133}
50134
50135static ma_data_source_vtable g_ma_decoder_data_source_vtable =
50136{
50137 ma_decoder__data_source_on_read,
50138 ma_decoder__data_source_on_seek,
50139 NULL, /* onMap */
50140 NULL, /* onUnmap */
50141 ma_decoder__data_source_on_get_data_format,
50142 ma_decoder__data_source_on_get_cursor,
50143 ma_decoder__data_source_on_get_length
50144};
50145
50146static 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)
50147{
50148 ma_result result;
50149 ma_data_source_config dataSourceConfig;
50150
50151 MA_ASSERT(pConfig != NULL);
50152
50153 if (pDecoder == NULL) {
50154 return MA_INVALID_ARGS;
50155 }
50156
50157 MA_ZERO_OBJECT(pDecoder);
50158
50159 if (onRead == NULL || onSeek == NULL) {
50160 return MA_INVALID_ARGS;
50161 }
50162
50163 dataSourceConfig = ma_data_source_config_init();
50164 dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable;
50165
50166 result = ma_data_source_init(&dataSourceConfig, &pDecoder->ds);
50167 if (result != MA_SUCCESS) {
50168 return result;
50169 }
50170
50171 pDecoder->onRead = onRead;
50172 pDecoder->onSeek = onSeek;
50173 pDecoder->onTell = onTell;
50174 pDecoder->pUserData = pUserData;
50175
50176 result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder);
50177 if (result != MA_SUCCESS) {
50178 ma_data_source_uninit(&pDecoder->ds);
50179 return result;
50180 }
50181
50182 return MA_SUCCESS;
50183}
50184
50185static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50186{
50187 ma_result result = MA_SUCCESS;
50188
50189 /* Basic validation in case the internal decoder supports different limits to miniaudio. */
50190 {
50191 /* TODO: Remove this block once we remove MA_MIN_CHANNELS and MA_MAX_CHANNELS. */
50192 ma_uint32 internalChannels;
50193 ma_data_source_get_data_format(pDecoder->pBackend, NULL, &internalChannels, NULL);
50194
50195 if (internalChannels < MA_MIN_CHANNELS || internalChannels > MA_MAX_CHANNELS) {
50196 result = MA_INVALID_DATA;
50197 }
50198 }
50199
50200 if (result == MA_SUCCESS) {
50201 result = ma_decoder__init_data_converter(pDecoder, pConfig);
50202 }
50203
50204 /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */
50205 if (result != MA_SUCCESS) {
50206 ma_decoder_uninit(pDecoder);
50207 return result;
50208 }
50209
50210 return result;
50211}
50212
50213MA_API ma_result ma_decoder_init_wav(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50214{
50215#ifdef MA_HAS_WAV
50216 ma_decoder_config config;
50217
50218 config = ma_decoder_config_init_copy(pConfig);
50219 config.encodingFormat = ma_encoding_format_wav;
50220
50221 return ma_decoder_init(onRead, onSeek, pUserData, &config, pDecoder);
50222#else
50223 (void)onRead;
50224 (void)onSeek;
50225 (void)pUserData;
50226 (void)pConfig;
50227 (void)pDecoder;
50228 return MA_NO_BACKEND;
50229#endif
50230}
50231
50232MA_API ma_result ma_decoder_init_flac(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50233{
50234#ifdef MA_HAS_FLAC
50235 ma_decoder_config config;
50236
50237 config = ma_decoder_config_init_copy(pConfig);
50238 config.encodingFormat = ma_encoding_format_flac;
50239
50240 return ma_decoder_init(onRead, onSeek, pUserData, &config, pDecoder);
50241#else
50242 (void)onRead;
50243 (void)onSeek;
50244 (void)pUserData;
50245 (void)pConfig;
50246 (void)pDecoder;
50247 return MA_NO_BACKEND;
50248#endif
50249}
50250
50251MA_API ma_result ma_decoder_init_mp3(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50252{
50253#ifdef MA_HAS_MP3
50254 ma_decoder_config config;
50255
50256 config = ma_decoder_config_init_copy(pConfig);
50257 config.encodingFormat = ma_encoding_format_mp3;
50258
50259 return ma_decoder_init(onRead, onSeek, pUserData, &config, pDecoder);
50260#else
50261 (void)onRead;
50262 (void)onSeek;
50263 (void)pUserData;
50264 (void)pConfig;
50265 (void)pDecoder;
50266 return MA_NO_BACKEND;
50267#endif
50268}
50269
50270MA_API ma_result ma_decoder_init_vorbis(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50271{
50272#ifdef MA_HAS_VORBIS
50273 ma_decoder_config config;
50274
50275 config = ma_decoder_config_init_copy(pConfig);
50276 config.encodingFormat = ma_encoding_format_vorbis;
50277
50278 return ma_decoder_init(onRead, onSeek, pUserData, &config, pDecoder);
50279#else
50280 (void)onRead;
50281 (void)onSeek;
50282 (void)pUserData;
50283 (void)pConfig;
50284 (void)pDecoder;
50285 return MA_NO_BACKEND;
50286#endif
50287}
50288
50289
50290
50291static 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)
50292{
50293 ma_result result = MA_NO_BACKEND;
50294
50295 MA_ASSERT(pConfig != NULL);
50296 MA_ASSERT(pDecoder != NULL);
50297
50298 /* Silence some warnings in the case that we don't have any decoder backends enabled. */
50299 (void)onRead;
50300 (void)onSeek;
50301 (void)pUserData;
50302
50303
50304 /* If we've specified a specific encoding type, try that first. */
50305 if (pConfig->encodingFormat != ma_encoding_format_unknown) {
50306 #ifdef MA_HAS_WAV
50307 if (pConfig->encodingFormat == ma_encoding_format_wav) {
50308 result = ma_decoder_init_wav__internal(pConfig, pDecoder);
50309 }
50310 #endif
50311 #ifdef MA_HAS_FLAC
50312 if (pConfig->encodingFormat == ma_encoding_format_flac) {
50313 result = ma_decoder_init_flac__internal(pConfig, pDecoder);
50314 }
50315 #endif
50316 #ifdef MA_HAS_MP3
50317 if (pConfig->encodingFormat == ma_encoding_format_mp3) {
50318 result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
50319 }
50320 #endif
50321 #ifdef MA_HAS_VORBIS
50322 if (pConfig->encodingFormat == ma_encoding_format_vorbis) {
50323 result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
50324 }
50325 #endif
50326
50327 /* If we weren't able to initialize the decoder, seek back to the start to give the next attempts a clean start. */
50328 if (result != MA_SUCCESS) {
50329 onSeek(pDecoder, 0, ma_seek_origin_start);
50330 }
50331 }
50332
50333 if (result != MA_SUCCESS) {
50334 /* Getting here means we couldn't load a specific decoding backend based on the encoding format. */
50335
50336 /*
50337 We use trial and error to open a decoder. We prioritize custom decoders so that if they
50338 implement the same encoding format they take priority over the built-in decoders.
50339 */
50340 if (result != MA_SUCCESS) {
50341 result = ma_decoder_init_custom__internal(pConfig, pDecoder);
50342 if (result != MA_SUCCESS) {
50343 onSeek(pDecoder, 0, ma_seek_origin_start);
50344 }
50345 }
50346
50347 /*
50348 If we get to this point and we still haven't found a decoder, and the caller has requested a
50349 specific encoding format, there's no hope for it. Abort.
50350 */
50351 if (pConfig->encodingFormat != ma_encoding_format_unknown) {
50352 return MA_NO_BACKEND;
50353 }
50354
50355 #ifdef MA_HAS_WAV
50356 if (result != MA_SUCCESS) {
50357 result = ma_decoder_init_wav__internal(pConfig, pDecoder);
50358 if (result != MA_SUCCESS) {
50359 onSeek(pDecoder, 0, ma_seek_origin_start);
50360 }
50361 }
50362 #endif
50363 #ifdef MA_HAS_FLAC
50364 if (result != MA_SUCCESS) {
50365 result = ma_decoder_init_flac__internal(pConfig, pDecoder);
50366 if (result != MA_SUCCESS) {
50367 onSeek(pDecoder, 0, ma_seek_origin_start);
50368 }
50369 }
50370 #endif
50371 #ifdef MA_HAS_MP3
50372 if (result != MA_SUCCESS) {
50373 result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
50374 if (result != MA_SUCCESS) {
50375 onSeek(pDecoder, 0, ma_seek_origin_start);
50376 }
50377 }
50378 #endif
50379 #ifdef MA_HAS_VORBIS
50380 if (result != MA_SUCCESS) {
50381 result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
50382 if (result != MA_SUCCESS) {
50383 onSeek(pDecoder, 0, ma_seek_origin_start);
50384 }
50385 }
50386 #endif
50387 }
50388
50389 if (result != MA_SUCCESS) {
50390 return result;
50391 }
50392
50393 return ma_decoder__postinit(pConfig, pDecoder);
50394}
50395
50396MA_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)
50397{
50398 ma_decoder_config config;
50399 ma_result result;
50400
50401 config = ma_decoder_config_init_copy(pConfig);
50402
50403 result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder);
50404 if (result != MA_SUCCESS) {
50405 return result;
50406 }
50407
50408 return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder);
50409}
50410
50411
50412static size_t ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
50413{
50414 size_t bytesRemaining;
50415
50416 MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos);
50417
50418 bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos;
50419 if (bytesToRead > bytesRemaining) {
50420 bytesToRead = bytesRemaining;
50421 }
50422
50423 if (bytesToRead > 0) {
50424 MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead);
50425 pDecoder->data.memory.currentReadPos += bytesToRead;
50426 }
50427
50428 return bytesToRead;
50429}
50430
50431static ma_bool32 ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin)
50432{
50433 if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) {
50434 return MA_FALSE; /* Too far. */
50435 }
50436
50437 if (origin == ma_seek_origin_current) {
50438 if (byteOffset > 0) {
50439 if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) {
50440 byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos); /* Trying to seek too far forward. */
50441 }
50442
50443 pDecoder->data.memory.currentReadPos += (size_t)byteOffset;
50444 } else {
50445 if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) {
50446 byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos; /* Trying to seek too far backwards. */
50447 }
50448
50449 pDecoder->data.memory.currentReadPos -= (size_t)-byteOffset;
50450 }
50451 } else {
50452 if (origin == ma_seek_origin_end) {
50453 if (byteOffset < 0) {
50454 byteOffset = -byteOffset;
50455 }
50456
50457 if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) {
50458 pDecoder->data.memory.currentReadPos = 0; /* Trying to seek too far back. */
50459 } else {
50460 pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - (size_t)byteOffset;
50461 }
50462 } else {
50463 if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) {
50464 pDecoder->data.memory.currentReadPos = (size_t)byteOffset;
50465 } else {
50466 pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize; /* Trying to seek too far forward. */
50467 }
50468 }
50469 }
50470
50471 return MA_TRUE;
50472}
50473
50474static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor)
50475{
50476 MA_ASSERT(pDecoder != NULL);
50477 MA_ASSERT(pCursor != NULL);
50478
50479 *pCursor = (ma_int64)pDecoder->data.memory.currentReadPos;
50480
50481 return MA_SUCCESS;
50482}
50483
50484static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50485{
50486 ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder);
50487 if (result != MA_SUCCESS) {
50488 return result;
50489 }
50490
50491 if (pData == NULL || dataSize == 0) {
50492 return MA_INVALID_ARGS;
50493 }
50494
50495 pDecoder->data.memory.pData = (const ma_uint8*)pData;
50496 pDecoder->data.memory.dataSize = dataSize;
50497 pDecoder->data.memory.currentReadPos = 0;
50498
50499 (void)pConfig;
50500 return MA_SUCCESS;
50501}
50502
50503MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50504{
50505 ma_decoder_config config;
50506 ma_result result;
50507
50508 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
50509
50510 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
50511 if (result != MA_SUCCESS) {
50512 return result;
50513 }
50514
50515 return ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder);
50516}
50517
50518MA_API ma_result ma_decoder_init_memory_wav(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50519{
50520#ifdef MA_HAS_WAV
50521 ma_decoder_config config;
50522
50523 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
50524 config.encodingFormat = ma_encoding_format_wav;
50525
50526 return ma_decoder_init_memory(pData, dataSize, &config, pDecoder);
50527#else
50528 (void)pData;
50529 (void)dataSize;
50530 (void)pConfig;
50531 (void)pDecoder;
50532 return MA_NO_BACKEND;
50533#endif
50534}
50535
50536MA_API ma_result ma_decoder_init_memory_flac(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50537{
50538#ifdef MA_HAS_FLAC
50539 ma_decoder_config config;
50540
50541 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
50542 config.encodingFormat = ma_encoding_format_flac;
50543
50544 return ma_decoder_init_memory(pData, dataSize, &config, pDecoder);
50545#else
50546 (void)pData;
50547 (void)dataSize;
50548 (void)pConfig;
50549 (void)pDecoder;
50550 return MA_NO_BACKEND;
50551#endif
50552}
50553
50554MA_API ma_result ma_decoder_init_memory_mp3(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50555{
50556#ifdef MA_HAS_MP3
50557 ma_decoder_config config;
50558
50559 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
50560 config.encodingFormat = ma_encoding_format_mp3;
50561
50562 return ma_decoder_init_memory(pData, dataSize, &config, pDecoder);
50563#else
50564 (void)pData;
50565 (void)dataSize;
50566 (void)pConfig;
50567 (void)pDecoder;
50568 return MA_NO_BACKEND;
50569#endif
50570}
50571
50572MA_API ma_result ma_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50573{
50574#ifdef MA_HAS_VORBIS
50575 ma_decoder_config config;
50576
50577 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
50578 config.encodingFormat = ma_encoding_format_vorbis;
50579
50580 return ma_decoder_init_memory(pData, dataSize, &config, pDecoder);
50581#else
50582 (void)pData;
50583 (void)dataSize;
50584 (void)pConfig;
50585 (void)pDecoder;
50586 return MA_NO_BACKEND;
50587#endif
50588}
50589
50590
50591
50592#if defined(MA_HAS_WAV) || \
50593 defined(MA_HAS_MP3) || \
50594 defined(MA_HAS_FLAC) || \
50595 defined(MA_HAS_VORBIS) || \
50596 defined(MA_HAS_OPUS)
50597#define MA_HAS_PATH_API
50598#endif
50599
50600#if defined(MA_HAS_PATH_API)
50601static const char* ma_path_file_name(const char* path)
50602{
50603 const char* fileName;
50604
50605 if (path == NULL) {
50606 return NULL;
50607 }
50608
50609 fileName = path;
50610
50611 /* We just loop through the path until we find the last slash. */
50612 while (path[0] != '\0') {
50613 if (path[0] == '/' || path[0] == '\\') {
50614 fileName = path;
50615 }
50616
50617 path += 1;
50618 }
50619
50620 /* At this point the file name is sitting on a slash, so just move forward. */
50621 while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
50622 fileName += 1;
50623 }
50624
50625 return fileName;
50626}
50627
50628static const wchar_t* ma_path_file_name_w(const wchar_t* path)
50629{
50630 const wchar_t* fileName;
50631
50632 if (path == NULL) {
50633 return NULL;
50634 }
50635
50636 fileName = path;
50637
50638 /* We just loop through the path until we find the last slash. */
50639 while (path[0] != '\0') {
50640 if (path[0] == '/' || path[0] == '\\') {
50641 fileName = path;
50642 }
50643
50644 path += 1;
50645 }
50646
50647 /* At this point the file name is sitting on a slash, so just move forward. */
50648 while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
50649 fileName += 1;
50650 }
50651
50652 return fileName;
50653}
50654
50655
50656static const char* ma_path_extension(const char* path)
50657{
50658 const char* extension;
50659 const char* lastOccurance;
50660
50661 if (path == NULL) {
50662 path = "";
50663 }
50664
50665 extension = ma_path_file_name(path);
50666 lastOccurance = NULL;
50667
50668 /* Just find the last '.' and return. */
50669 while (extension[0] != '\0') {
50670 if (extension[0] == '.') {
50671 extension += 1;
50672 lastOccurance = extension;
50673 }
50674
50675 extension += 1;
50676 }
50677
50678 return (lastOccurance != NULL) ? lastOccurance : extension;
50679}
50680
50681static const wchar_t* ma_path_extension_w(const wchar_t* path)
50682{
50683 const wchar_t* extension;
50684 const wchar_t* lastOccurance;
50685
50686 if (path == NULL) {
50687 path = L"";
50688 }
50689
50690 extension = ma_path_file_name_w(path);
50691 lastOccurance = NULL;
50692
50693 /* Just find the last '.' and return. */
50694 while (extension[0] != '\0') {
50695 if (extension[0] == '.') {
50696 extension += 1;
50697 lastOccurance = extension;
50698 }
50699
50700 extension += 1;
50701 }
50702
50703 return (lastOccurance != NULL) ? lastOccurance : extension;
50704}
50705
50706
50707static ma_bool32 ma_path_extension_equal(const char* path, const char* extension)
50708{
50709 const char* ext1;
50710 const char* ext2;
50711
50712 if (path == NULL || extension == NULL) {
50713 return MA_FALSE;
50714 }
50715
50716 ext1 = extension;
50717 ext2 = ma_path_extension(path);
50718
50719#if defined(_MSC_VER) || defined(__DMC__)
50720 return _stricmp(ext1, ext2) == 0;
50721#else
50722 return strcasecmp(ext1, ext2) == 0;
50723#endif
50724}
50725
50726static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension)
50727{
50728 const wchar_t* ext1;
50729 const wchar_t* ext2;
50730
50731 if (path == NULL || extension == NULL) {
50732 return MA_FALSE;
50733 }
50734
50735 ext1 = extension;
50736 ext2 = ma_path_extension_w(path);
50737
50738#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)
50739 return _wcsicmp(ext1, ext2) == 0;
50740#else
50741 /*
50742 I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This
50743 isn't the most efficient way to do it, but it should work OK.
50744 */
50745 {
50746 char ext1MB[4096];
50747 char ext2MB[4096];
50748 const wchar_t* pext1 = ext1;
50749 const wchar_t* pext2 = ext2;
50750 mbstate_t mbs1;
50751 mbstate_t mbs2;
50752
50753 MA_ZERO_OBJECT(&mbs1);
50754 MA_ZERO_OBJECT(&mbs2);
50755
50756 if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) {
50757 return MA_FALSE;
50758 }
50759 if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) {
50760 return MA_FALSE;
50761 }
50762
50763 return strcasecmp(ext1MB, ext2MB) == 0;
50764 }
50765#endif
50766}
50767#endif /* MA_HAS_PATH_API */
50768
50769
50770
50771static size_t ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
50772{
50773 size_t bytesRead;
50774
50775 MA_ASSERT(pDecoder != NULL);
50776 MA_ASSERT(pBufferOut != NULL);
50777
50778 ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, &bytesRead);
50779
50780 return bytesRead;
50781}
50782
50783static ma_bool32 ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin)
50784{
50785 ma_result result;
50786
50787 MA_ASSERT(pDecoder != NULL);
50788
50789 result = ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin);
50790 if (result != MA_SUCCESS) {
50791 return MA_FALSE;
50792 }
50793
50794 return MA_TRUE;
50795}
50796
50797static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor)
50798{
50799 MA_ASSERT(pDecoder != NULL);
50800
50801 return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor);
50802}
50803
50804static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50805{
50806 ma_result result;
50807 ma_vfs_file file;
50808
50809 result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder);
50810 if (result != MA_SUCCESS) {
50811 return result;
50812 }
50813
50814 if (pFilePath == NULL || pFilePath[0] == '\0') {
50815 return MA_INVALID_ARGS;
50816 }
50817
50818 result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
50819 if (result != MA_SUCCESS) {
50820 return result;
50821 }
50822
50823 pDecoder->data.vfs.pVFS = pVFS;
50824 pDecoder->data.vfs.file = file;
50825
50826 return MA_SUCCESS;
50827}
50828
50829MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50830{
50831 ma_result result;
50832 ma_decoder_config config;
50833
50834 config = ma_decoder_config_init_copy(pConfig);
50835 result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder);
50836 if (result != MA_SUCCESS) {
50837 return result;
50838 }
50839
50840 result = MA_NO_BACKEND;
50841
50842 if (config.encodingFormat != ma_encoding_format_unknown) {
50843 #ifdef MA_HAS_WAV
50844 if (config.encodingFormat == ma_encoding_format_wav) {
50845 result = ma_decoder_init_wav__internal(&config, pDecoder);
50846 }
50847 #endif
50848 #ifdef MA_HAS_FLAC
50849 if (config.encodingFormat == ma_encoding_format_flac) {
50850 result = ma_decoder_init_flac__internal(&config, pDecoder);
50851 }
50852 #endif
50853 #ifdef MA_HAS_MP3
50854 if (config.encodingFormat == ma_encoding_format_mp3) {
50855 result = ma_decoder_init_mp3__internal(&config, pDecoder);
50856 }
50857 #endif
50858 #ifdef MA_HAS_VORBIS
50859 if (config.encodingFormat == ma_encoding_format_vorbis) {
50860 result = ma_decoder_init_vorbis__internal(&config, pDecoder);
50861 }
50862 #endif
50863
50864 /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */
50865 if (result != MA_SUCCESS) {
50866 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
50867 }
50868 }
50869
50870 if (result != MA_SUCCESS) {
50871 /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */
50872
50873 /*
50874 We use trial and error to open a decoder. We prioritize custom decoders so that if they
50875 implement the same encoding format they take priority over the built-in decoders.
50876 */
50877 if (result != MA_SUCCESS) {
50878 result = ma_decoder_init_custom__internal(&config, pDecoder);
50879 if (result != MA_SUCCESS) {
50880 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
50881 }
50882 }
50883
50884 /*
50885 If we get to this point and we still haven't found a decoder, and the caller has requested a
50886 specific encoding format, there's no hope for it. Abort.
50887 */
50888 if (config.encodingFormat != ma_encoding_format_unknown) {
50889 return MA_NO_BACKEND;
50890 }
50891
50892 #ifdef MA_HAS_WAV
50893 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) {
50894 result = ma_decoder_init_wav__internal(&config, pDecoder);
50895 if (result != MA_SUCCESS) {
50896 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
50897 }
50898 }
50899 #endif
50900 #ifdef MA_HAS_FLAC
50901 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) {
50902 result = ma_decoder_init_flac__internal(&config, pDecoder);
50903 if (result != MA_SUCCESS) {
50904 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
50905 }
50906 }
50907 #endif
50908 #ifdef MA_HAS_MP3
50909 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) {
50910 result = ma_decoder_init_mp3__internal(&config, pDecoder);
50911 if (result != MA_SUCCESS) {
50912 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
50913 }
50914 }
50915 #endif
50916 }
50917
50918 /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */
50919 if (result != MA_SUCCESS) {
50920 result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder);
50921 } else {
50922 result = ma_decoder__postinit(&config, pDecoder);
50923 }
50924
50925 if (result != MA_SUCCESS) {
50926 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. */
50927 ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
50928 }
50929
50930 return result;
50931 }
50932
50933 return MA_SUCCESS;
50934}
50935
50936MA_API ma_result ma_decoder_init_vfs_wav(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50937{
50938#ifdef MA_HAS_WAV
50939 ma_decoder_config config;
50940
50941 config = ma_decoder_config_init_copy(pConfig);
50942 config.encodingFormat = ma_encoding_format_wav;
50943
50944 return ma_decoder_init_vfs(pVFS, pFilePath, &config, pDecoder);
50945#else
50946 (void)pVFS;
50947 (void)pFilePath;
50948 (void)pConfig;
50949 (void)pDecoder;
50950 return MA_NO_BACKEND;
50951#endif
50952}
50953
50954MA_API ma_result ma_decoder_init_vfs_flac(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50955{
50956#ifdef MA_HAS_FLAC
50957 ma_decoder_config config;
50958
50959 config = ma_decoder_config_init_copy(pConfig);
50960 config.encodingFormat = ma_encoding_format_flac;
50961
50962 return ma_decoder_init_vfs(pVFS, pFilePath, &config, pDecoder);
50963#else
50964 (void)pVFS;
50965 (void)pFilePath;
50966 (void)pConfig;
50967 (void)pDecoder;
50968 return MA_NO_BACKEND;
50969#endif
50970}
50971
50972MA_API ma_result ma_decoder_init_vfs_mp3(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50973{
50974#ifdef MA_HAS_MP3
50975 ma_decoder_config config;
50976
50977 config = ma_decoder_config_init_copy(pConfig);
50978 config.encodingFormat = ma_encoding_format_mp3;
50979
50980 return ma_decoder_init_vfs(pVFS, pFilePath, &config, pDecoder);
50981#else
50982 (void)pVFS;
50983 (void)pFilePath;
50984 (void)pConfig;
50985 (void)pDecoder;
50986 return MA_NO_BACKEND;
50987#endif
50988}
50989
50990MA_API ma_result ma_decoder_init_vfs_vorbis(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
50991{
50992#ifdef MA_HAS_VORBIS
50993 ma_decoder_config config;
50994
50995 config = ma_decoder_config_init_copy(pConfig);
50996 config.encodingFormat = ma_encoding_format_vorbis;
50997
50998 return ma_decoder_init_vfs(pVFS, pFilePath, &config, pDecoder);
50999#else
51000 (void)pVFS;
51001 (void)pFilePath;
51002 (void)pConfig;
51003 (void)pDecoder;
51004 return MA_NO_BACKEND;
51005#endif
51006}
51007
51008
51009
51010static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
51011{
51012 ma_result result;
51013 ma_vfs_file file;
51014
51015 result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder);
51016 if (result != MA_SUCCESS) {
51017 return result;
51018 }
51019
51020 if (pFilePath == NULL || pFilePath[0] == '\0') {
51021 return MA_INVALID_ARGS;
51022 }
51023
51024 result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
51025 if (result != MA_SUCCESS) {
51026 return result;
51027 }
51028
51029 pDecoder->data.vfs.pVFS = pVFS;
51030 pDecoder->data.vfs.file = file;
51031
51032 return MA_SUCCESS;
51033}
51034
51035MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
51036{
51037 ma_result result;
51038 ma_decoder_config config;
51039
51040 config = ma_decoder_config_init_copy(pConfig);
51041 result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder);
51042 if (result != MA_SUCCESS) {
51043 return result;
51044 }
51045
51046 result = MA_NO_BACKEND;
51047
51048 if (config.encodingFormat != ma_encoding_format_unknown) {
51049 #ifdef MA_HAS_WAV
51050 if (config.encodingFormat == ma_encoding_format_wav) {
51051 result = ma_decoder_init_wav__internal(&config, pDecoder);
51052 }
51053 #endif
51054 #ifdef MA_HAS_FLAC
51055 if (config.encodingFormat == ma_encoding_format_flac) {
51056 result = ma_decoder_init_flac__internal(&config, pDecoder);
51057 }
51058 #endif
51059 #ifdef MA_HAS_MP3
51060 if (config.encodingFormat == ma_encoding_format_mp3) {
51061 result = ma_decoder_init_mp3__internal(&config, pDecoder);
51062 }
51063 #endif
51064 #ifdef MA_HAS_VORBIS
51065 if (config.encodingFormat == ma_encoding_format_vorbis) {
51066 result = ma_decoder_init_vorbis__internal(&config, pDecoder);
51067 }
51068 #endif
51069
51070 /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */
51071 if (result != MA_SUCCESS) {
51072 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
51073 }
51074 }
51075
51076 if (result != MA_SUCCESS) {
51077 /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */
51078
51079 /*
51080 We use trial and error to open a decoder. We prioritize custom decoders so that if they
51081 implement the same encoding format they take priority over the built-in decoders.
51082 */
51083 if (result != MA_SUCCESS) {
51084 result = ma_decoder_init_custom__internal(&config, pDecoder);
51085 if (result != MA_SUCCESS) {
51086 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
51087 }
51088 }
51089
51090 /*
51091 If we get to this point and we still haven't found a decoder, and the caller has requested a
51092 specific encoding format, there's no hope for it. Abort.
51093 */
51094 if (config.encodingFormat != ma_encoding_format_unknown) {
51095 return MA_NO_BACKEND;
51096 }
51097
51098 #ifdef MA_HAS_WAV
51099 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) {
51100 result = ma_decoder_init_wav__internal(&config, pDecoder);
51101 if (result != MA_SUCCESS) {
51102 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
51103 }
51104 }
51105 #endif
51106 #ifdef MA_HAS_FLAC
51107 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) {
51108 result = ma_decoder_init_flac__internal(&config, pDecoder);
51109 if (result != MA_SUCCESS) {
51110 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
51111 }
51112 }
51113 #endif
51114 #ifdef MA_HAS_MP3
51115 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) {
51116 result = ma_decoder_init_mp3__internal(&config, pDecoder);
51117 if (result != MA_SUCCESS) {
51118 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
51119 }
51120 }
51121 #endif
51122 }
51123
51124 /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */
51125 if (result != MA_SUCCESS) {
51126 result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder);
51127 } else {
51128 result = ma_decoder__postinit(&config, pDecoder);
51129 }
51130
51131 if (result != MA_SUCCESS) {
51132 ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
51133 return result;
51134 }
51135
51136 return MA_SUCCESS;
51137}
51138
51139MA_API ma_result ma_decoder_init_vfs_wav_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
51140{
51141#ifdef MA_HAS_WAV
51142 ma_decoder_config config;
51143
51144 config = ma_decoder_config_init_copy(pConfig);
51145 config.encodingFormat = ma_encoding_format_wav;
51146
51147 return ma_decoder_init_vfs_w(pVFS, pFilePath, &config, pDecoder);
51148#else
51149 (void)pVFS;
51150 (void)pFilePath;
51151 (void)pConfig;
51152 (void)pDecoder;
51153 return MA_NO_BACKEND;
51154#endif
51155}
51156
51157MA_API ma_result ma_decoder_init_vfs_flac_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
51158{
51159#ifdef MA_HAS_FLAC
51160 ma_decoder_config config;
51161
51162 config = ma_decoder_config_init_copy(pConfig);
51163 config.encodingFormat = ma_encoding_format_flac;
51164
51165 return ma_decoder_init_vfs_w(pVFS, pFilePath, &config, pDecoder);
51166#else
51167 (void)pVFS;
51168 (void)pFilePath;
51169 (void)pConfig;
51170 (void)pDecoder;
51171 return MA_NO_BACKEND;
51172#endif
51173}
51174
51175MA_API ma_result ma_decoder_init_vfs_mp3_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
51176{
51177#ifdef MA_HAS_MP3
51178 ma_decoder_config config;
51179
51180 config = ma_decoder_config_init_copy(pConfig);
51181 config.encodingFormat = ma_encoding_format_mp3;
51182
51183 return ma_decoder_init_vfs_w(pVFS, pFilePath, &config, pDecoder);
51184#else
51185 (void)pVFS;
51186 (void)pFilePath;
51187 (void)pConfig;
51188 (void)pDecoder;
51189 return MA_NO_BACKEND;
51190#endif
51191}
51192
51193MA_API ma_result ma_decoder_init_vfs_vorbis_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
51194{
51195#ifdef MA_HAS_VORBIS
51196 ma_decoder_config config;
51197
51198 config = ma_decoder_config_init_copy(pConfig);
51199 config.encodingFormat = ma_encoding_format_vorbis;
51200
51201 return ma_decoder_init_vfs_w(pVFS, pFilePath, &config, pDecoder);
51202#else
51203 (void)pVFS;
51204 (void)pFilePath;
51205 (void)pConfig;
51206 (void)pDecoder;
51207 return MA_NO_BACKEND;
51208#endif
51209}
51210
51211
51212
51213MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
51214{
51215 return ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder);
51216}
51217
51218MA_API ma_result ma_decoder_init_file_wav(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
51219{
51220 return ma_decoder_init_vfs_wav(NULL, pFilePath, pConfig, pDecoder);
51221}
51222
51223MA_API ma_result ma_decoder_init_file_flac(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
51224{
51225 return ma_decoder_init_vfs_flac(NULL, pFilePath, pConfig, pDecoder);
51226}
51227
51228MA_API ma_result ma_decoder_init_file_mp3(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
51229{
51230 return ma_decoder_init_vfs_mp3(NULL, pFilePath, pConfig, pDecoder);
51231}
51232
51233MA_API ma_result ma_decoder_init_file_vorbis(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
51234{
51235 return ma_decoder_init_vfs_vorbis(NULL, pFilePath, pConfig, pDecoder);
51236}
51237
51238
51239
51240MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
51241{
51242 return ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder);
51243}
51244
51245MA_API ma_result ma_decoder_init_file_wav_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
51246{
51247 return ma_decoder_init_vfs_wav_w(NULL, pFilePath, pConfig, pDecoder);
51248}
51249
51250MA_API ma_result ma_decoder_init_file_flac_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
51251{
51252 return ma_decoder_init_vfs_flac_w(NULL, pFilePath, pConfig, pDecoder);
51253}
51254
51255MA_API ma_result ma_decoder_init_file_mp3_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
51256{
51257 return ma_decoder_init_vfs_mp3_w(NULL, pFilePath, pConfig, pDecoder);
51258}
51259
51260MA_API ma_result ma_decoder_init_file_vorbis_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
51261{
51262 return ma_decoder_init_vfs_vorbis_w(NULL, pFilePath, pConfig, pDecoder);
51263}
51264
51265MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder)
51266{
51267 if (pDecoder == NULL) {
51268 return MA_INVALID_ARGS;
51269 }
51270
51271 if (pDecoder->pBackend != NULL) {
51272 if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {
51273 pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks);
51274 }
51275 }
51276
51277 /* Legacy. */
51278 if (pDecoder->onRead == ma_decoder__on_read_vfs) {
51279 ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file);
51280 pDecoder->data.vfs.file = NULL;
51281 }
51282
51283 ma_data_converter_uninit(&pDecoder->converter);
51284 ma_data_source_uninit(&pDecoder->ds);
51285
51286 return MA_SUCCESS;
51287}
51288
51289MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor)
51290{
51291 if (pCursor == NULL) {
51292 return MA_INVALID_ARGS;
51293 }
51294
51295 *pCursor = 0;
51296
51297 if (pDecoder == NULL) {
51298 return MA_INVALID_ARGS;
51299 }
51300
51301 *pCursor = pDecoder->readPointerInPCMFrames;
51302
51303 return MA_SUCCESS;
51304}
51305
51306MA_API ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder)
51307{
51308 if (pDecoder == NULL) {
51309 return 0;
51310 }
51311
51312 if (pDecoder->pBackend != NULL) {
51313 ma_result result;
51314 ma_uint64 nativeLengthInPCMFrames;
51315 ma_uint32 internalSampleRate;
51316
51317 ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &nativeLengthInPCMFrames);
51318
51319 result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate);
51320 if (result != MA_SUCCESS) {
51321 return 0; /* Failed to retrieve the internal sample rate. */
51322 }
51323
51324 if (internalSampleRate == pDecoder->outputSampleRate) {
51325 return nativeLengthInPCMFrames;
51326 } else {
51327 return ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, nativeLengthInPCMFrames);
51328 }
51329 }
51330
51331 return 0;
51332}
51333
51334MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
51335{
51336 ma_result result;
51337 ma_uint64 totalFramesReadOut;
51338 ma_uint64 totalFramesReadIn;
51339 void* pRunningFramesOut;
51340
51341 if (pDecoder == NULL) {
51342 return 0;
51343 }
51344
51345 if (pDecoder->pBackend == NULL) {
51346 return 0;
51347 }
51348
51349 /* Fast path. */
51350 if (pDecoder->converter.isPassthrough) {
51351 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut, MA_FALSE);
51352 } else {
51353 /*
51354 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
51355 need to run through each sample because we need to ensure it's internal cache is updated.
51356 */
51357 if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) {
51358 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut, MA_FALSE);
51359 } else {
51360 /* Slow path. Need to run everything through the data converter. */
51361 ma_format internalFormat;
51362 ma_uint32 internalChannels;
51363
51364 totalFramesReadOut = 0;
51365 totalFramesReadIn = 0;
51366 pRunningFramesOut = pFramesOut;
51367
51368 result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL);
51369 if (result != MA_SUCCESS) {
51370 return 0; /* Failed to retrieve the internal format and channel count. */
51371 }
51372
51373 while (totalFramesReadOut < frameCount) {
51374 ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */
51375 ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels);
51376 ma_uint64 framesToReadThisIterationIn;
51377 ma_uint64 framesReadThisIterationIn;
51378 ma_uint64 framesToReadThisIterationOut;
51379 ma_uint64 framesReadThisIterationOut;
51380 ma_uint64 requiredInputFrameCount;
51381
51382 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
51383 framesToReadThisIterationIn = framesToReadThisIterationOut;
51384 if (framesToReadThisIterationIn > intermediaryBufferCap) {
51385 framesToReadThisIterationIn = intermediaryBufferCap;
51386 }
51387
51388 requiredInputFrameCount = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut);
51389 if (framesToReadThisIterationIn > requiredInputFrameCount) {
51390 framesToReadThisIterationIn = requiredInputFrameCount;
51391 }
51392
51393 if (requiredInputFrameCount > 0) {
51394 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn, MA_FALSE);
51395 totalFramesReadIn += framesReadThisIterationIn;
51396 } else {
51397 framesReadThisIterationIn = 0;
51398 }
51399
51400 /*
51401 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
51402 input frames, we still want to try processing frames because there may some output frames generated from cached input data.
51403 */
51404 framesReadThisIterationOut = framesToReadThisIterationOut;
51405 result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
51406 if (result != MA_SUCCESS) {
51407 break;
51408 }
51409
51410 totalFramesReadOut += framesReadThisIterationOut;
51411
51412 if (pRunningFramesOut != NULL) {
51413 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
51414 }
51415
51416 if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
51417 break; /* We're done. */
51418 }
51419 }
51420 }
51421 }
51422
51423 pDecoder->readPointerInPCMFrames += totalFramesReadOut;
51424
51425 return totalFramesReadOut;
51426}
51427
51428MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex)
51429{
51430 if (pDecoder == NULL) {
51431 return MA_INVALID_ARGS;
51432 }
51433
51434 if (pDecoder->pBackend != NULL) {
51435 ma_result result;
51436 ma_uint64 internalFrameIndex;
51437 ma_uint32 internalSampleRate;
51438
51439 result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate);
51440 if (result != MA_SUCCESS) {
51441 return result; /* Failed to retrieve the internal sample rate. */
51442 }
51443
51444 if (internalSampleRate == pDecoder->outputSampleRate) {
51445 internalFrameIndex = frameIndex;
51446 } else {
51447 internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex);
51448 }
51449
51450 result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex);
51451 if (result == MA_SUCCESS) {
51452 pDecoder->readPointerInPCMFrames = frameIndex;
51453 }
51454
51455 return result;
51456 }
51457
51458 /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */
51459 return MA_INVALID_ARGS;
51460}
51461
51462MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames)
51463{
51464 ma_uint64 totalFrameCount;
51465
51466 if (pAvailableFrames == NULL) {
51467 return MA_INVALID_ARGS;
51468 }
51469
51470 *pAvailableFrames = 0;
51471
51472 if (pDecoder == NULL) {
51473 return MA_INVALID_ARGS;
51474 }
51475
51476 totalFrameCount = ma_decoder_get_length_in_pcm_frames(pDecoder);
51477 if (totalFrameCount == 0) {
51478 return MA_NOT_IMPLEMENTED;
51479 }
51480
51481 if (totalFrameCount <= pDecoder->readPointerInPCMFrames) {
51482 *pAvailableFrames = 0;
51483 } else {
51484 *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames;
51485 }
51486
51487 return MA_SUCCESS; /* No frames available. */
51488}
51489
51490
51491static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
51492{
51493 ma_uint64 totalFrameCount;
51494 ma_uint64 bpf;
51495 ma_uint64 dataCapInFrames;
51496 void* pPCMFramesOut;
51497
51498 MA_ASSERT(pDecoder != NULL);
51499
51500 totalFrameCount = 0;
51501 bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
51502
51503 /* The frame count is unknown until we try reading. Thus, we just run in a loop. */
51504 dataCapInFrames = 0;
51505 pPCMFramesOut = NULL;
51506 for (;;) {
51507 ma_uint64 frameCountToTryReading;
51508 ma_uint64 framesJustRead;
51509
51510 /* Make room if there's not enough. */
51511 if (totalFrameCount == dataCapInFrames) {
51512 void* pNewPCMFramesOut;
51513 ma_uint64 oldDataCapInFrames = dataCapInFrames;
51514 ma_uint64 newDataCapInFrames = dataCapInFrames*2;
51515 if (newDataCapInFrames == 0) {
51516 newDataCapInFrames = 4096;
51517 }
51518
51519 if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) {
51520 ma__free_from_callbacks(pPCMFramesOut, &pDecoder->allocationCallbacks);
51521 return MA_TOO_BIG;
51522 }
51523
51524
51525 pNewPCMFramesOut = (void*)ma__realloc_from_callbacks(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), (size_t)(oldDataCapInFrames * bpf), &pDecoder->allocationCallbacks);
51526 if (pNewPCMFramesOut == NULL) {
51527 ma__free_from_callbacks(pPCMFramesOut, &pDecoder->allocationCallbacks);
51528 return MA_OUT_OF_MEMORY;
51529 }
51530
51531 dataCapInFrames = newDataCapInFrames;
51532 pPCMFramesOut = pNewPCMFramesOut;
51533 }
51534
51535 frameCountToTryReading = dataCapInFrames - totalFrameCount;
51536 MA_ASSERT(frameCountToTryReading > 0);
51537
51538 framesJustRead = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading);
51539 totalFrameCount += framesJustRead;
51540
51541 if (framesJustRead < frameCountToTryReading) {
51542 break;
51543 }
51544 }
51545
51546
51547 if (pConfigOut != NULL) {
51548 pConfigOut->format = pDecoder->outputFormat;
51549 pConfigOut->channels = pDecoder->outputChannels;
51550 pConfigOut->sampleRate = pDecoder->outputSampleRate;
51551 ma_channel_map_copy(pConfigOut->channelMap, pDecoder->outputChannelMap, pDecoder->outputChannels);
51552 }
51553
51554 if (ppPCMFramesOut != NULL) {
51555 *ppPCMFramesOut = pPCMFramesOut;
51556 } else {
51557 ma__free_from_callbacks(pPCMFramesOut, &pDecoder->allocationCallbacks);
51558 }
51559
51560 if (pFrameCountOut != NULL) {
51561 *pFrameCountOut = totalFrameCount;
51562 }
51563
51564 ma_decoder_uninit(pDecoder);
51565 return MA_SUCCESS;
51566}
51567
51568MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
51569{
51570 ma_result result;
51571 ma_decoder_config config;
51572 ma_decoder decoder;
51573
51574 if (pFrameCountOut != NULL) {
51575 *pFrameCountOut = 0;
51576 }
51577 if (ppPCMFramesOut != NULL) {
51578 *ppPCMFramesOut = NULL;
51579 }
51580
51581 config = ma_decoder_config_init_copy(pConfig);
51582
51583 result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder);
51584 if (result != MA_SUCCESS) {
51585 return result;
51586 }
51587
51588 result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
51589
51590 return result;
51591}
51592
51593MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
51594{
51595 return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut);
51596}
51597
51598MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
51599{
51600 ma_decoder_config config;
51601 ma_decoder decoder;
51602 ma_result result;
51603
51604 if (pFrameCountOut != NULL) {
51605 *pFrameCountOut = 0;
51606 }
51607 if (ppPCMFramesOut != NULL) {
51608 *ppPCMFramesOut = NULL;
51609 }
51610
51611 if (pData == NULL || dataSize == 0) {
51612 return MA_INVALID_ARGS;
51613 }
51614
51615 config = ma_decoder_config_init_copy(pConfig);
51616
51617 result = ma_decoder_init_memory(pData, dataSize, &config, &decoder);
51618 if (result != MA_SUCCESS) {
51619 return result;
51620 }
51621
51622 return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
51623}
51624#endif /* MA_NO_DECODING */
51625
51626
51627#ifndef MA_NO_ENCODING
51628
51629#if defined(MA_HAS_WAV)
51630static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite)
51631{
51632 ma_encoder* pEncoder = (ma_encoder*)pUserData;
51633 MA_ASSERT(pEncoder != NULL);
51634
51635 return pEncoder->onWrite(pEncoder, pData, bytesToWrite);
51636}
51637
51638static drwav_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, drwav_seek_origin origin)
51639{
51640 ma_encoder* pEncoder = (ma_encoder*)pUserData;
51641 MA_ASSERT(pEncoder != NULL);
51642
51643 return pEncoder->onSeek(pEncoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
51644}
51645
51646static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder)
51647{
51648 drwav_data_format wavFormat;
51649 drwav_allocation_callbacks allocationCallbacks;
51650 drwav* pWav;
51651
51652 MA_ASSERT(pEncoder != NULL);
51653
51654 pWav = (drwav*)ma__malloc_from_callbacks(sizeof(*pWav), &pEncoder->config.allocationCallbacks);
51655 if (pWav == NULL) {
51656 return MA_OUT_OF_MEMORY;
51657 }
51658
51659 wavFormat.container = drwav_container_riff;
51660 wavFormat.channels = pEncoder->config.channels;
51661 wavFormat.sampleRate = pEncoder->config.sampleRate;
51662 wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8;
51663 if (pEncoder->config.format == ma_format_f32) {
51664 wavFormat.format = DR_WAVE_FORMAT_IEEE_FLOAT;
51665 } else {
51666 wavFormat.format = DR_WAVE_FORMAT_PCM;
51667 }
51668
51669 allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData;
51670 allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc;
51671 allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc;
51672 allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree;
51673
51674 if (!drwav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) {
51675 return MA_ERROR;
51676 }
51677
51678 pEncoder->pInternalEncoder = pWav;
51679
51680 return MA_SUCCESS;
51681}
51682
51683static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder)
51684{
51685 drwav* pWav;
51686
51687 MA_ASSERT(pEncoder != NULL);
51688
51689 pWav = (drwav*)pEncoder->pInternalEncoder;
51690 MA_ASSERT(pWav != NULL);
51691
51692 drwav_uninit(pWav);
51693 ma__free_from_callbacks(pWav, &pEncoder->config.allocationCallbacks);
51694}
51695
51696static ma_uint64 ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount)
51697{
51698 drwav* pWav;
51699
51700 MA_ASSERT(pEncoder != NULL);
51701
51702 pWav = (drwav*)pEncoder->pInternalEncoder;
51703 MA_ASSERT(pWav != NULL);
51704
51705 return drwav_write_pcm_frames(pWav, frameCount, pFramesIn);
51706}
51707#endif
51708
51709MA_API ma_encoder_config ma_encoder_config_init(ma_resource_format resourceFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
51710{
51711 ma_encoder_config config;
51712
51713 MA_ZERO_OBJECT(&config);
51714 config.resourceFormat = resourceFormat;
51715 config.format = format;
51716 config.channels = channels;
51717 config.sampleRate = sampleRate;
51718
51719 return config;
51720}
51721
51722MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder)
51723{
51724 ma_result result;
51725
51726 if (pEncoder == NULL) {
51727 return MA_INVALID_ARGS;
51728 }
51729
51730 MA_ZERO_OBJECT(pEncoder);
51731
51732 if (pConfig == NULL) {
51733 return MA_INVALID_ARGS;
51734 }
51735
51736 if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) {
51737 return MA_INVALID_ARGS;
51738 }
51739
51740 pEncoder->config = *pConfig;
51741
51742 result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks);
51743 if (result != MA_SUCCESS) {
51744 return result;
51745 }
51746
51747 return MA_SUCCESS;
51748}
51749
51750MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder)
51751{
51752 ma_result result = MA_SUCCESS;
51753
51754 /* This assumes ma_encoder_preinit() has been called prior. */
51755 MA_ASSERT(pEncoder != NULL);
51756
51757 if (onWrite == NULL || onSeek == NULL) {
51758 return MA_INVALID_ARGS;
51759 }
51760
51761 pEncoder->onWrite = onWrite;
51762 pEncoder->onSeek = onSeek;
51763 pEncoder->pUserData = pUserData;
51764
51765 switch (pEncoder->config.resourceFormat)
51766 {
51767 case ma_resource_format_wav:
51768 {
51769 #if defined(MA_HAS_WAV)
51770 pEncoder->onInit = ma_encoder__on_init_wav;
51771 pEncoder->onUninit = ma_encoder__on_uninit_wav;
51772 pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav;
51773 #else
51774 result = MA_NO_BACKEND;
51775 #endif
51776 } break;
51777
51778 default:
51779 {
51780 result = MA_INVALID_ARGS;
51781 } break;
51782 }
51783
51784 /* Getting here means we should have our backend callbacks set up. */
51785 if (result == MA_SUCCESS) {
51786 result = pEncoder->onInit(pEncoder);
51787 if (result != MA_SUCCESS) {
51788 return result;
51789 }
51790 }
51791
51792 return MA_SUCCESS;
51793}
51794
51795MA_API size_t ma_encoder__on_write_stdio(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite)
51796{
51797 return fwrite(pBufferIn, 1, bytesToWrite, (FILE*)pEncoder->pFile);
51798}
51799
51800MA_API ma_bool32 ma_encoder__on_seek_stdio(ma_encoder* pEncoder, int byteOffset, ma_seek_origin origin)
51801{
51802 return fseek((FILE*)pEncoder->pFile, byteOffset, (origin == ma_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
51803}
51804
51805MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
51806{
51807 ma_result result;
51808 FILE* pFile;
51809
51810 result = ma_encoder_preinit(pConfig, pEncoder);
51811 if (result != MA_SUCCESS) {
51812 return result;
51813 }
51814
51815 /* Now open the file. If this fails we don't need to uninitialize the encoder. */
51816 result = ma_fopen(&pFile, pFilePath, "wb");
51817 if (pFile == NULL) {
51818 return result;
51819 }
51820
51821 pEncoder->pFile = pFile;
51822
51823 return ma_encoder_init__internal(ma_encoder__on_write_stdio, ma_encoder__on_seek_stdio, NULL, pEncoder);
51824}
51825
51826MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
51827{
51828 ma_result result;
51829 FILE* pFile;
51830
51831 result = ma_encoder_preinit(pConfig, pEncoder);
51832 if (result != MA_SUCCESS) {
51833 return result;
51834 }
51835
51836 /* Now open the file. If this fails we don't need to uninitialize the encoder. */
51837 result = ma_wfopen(&pFile, pFilePath, L"wb", &pEncoder->config.allocationCallbacks);
51838 if (pFile == NULL) {
51839 return result;
51840 }
51841
51842 pEncoder->pFile = pFile;
51843
51844 return ma_encoder_init__internal(ma_encoder__on_write_stdio, ma_encoder__on_seek_stdio, NULL, pEncoder);
51845}
51846
51847MA_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)
51848{
51849 ma_result result;
51850
51851 result = ma_encoder_preinit(pConfig, pEncoder);
51852 if (result != MA_SUCCESS) {
51853 return result;
51854 }
51855
51856 return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder);
51857}
51858
51859
51860MA_API void ma_encoder_uninit(ma_encoder* pEncoder)
51861{
51862 if (pEncoder == NULL) {
51863 return;
51864 }
51865
51866 if (pEncoder->onUninit) {
51867 pEncoder->onUninit(pEncoder);
51868 }
51869
51870 /* If we have a file handle, close it. */
51871 if (pEncoder->onWrite == ma_encoder__on_write_stdio) {
51872 fclose((FILE*)pEncoder->pFile);
51873 }
51874}
51875
51876
51877MA_API ma_uint64 ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount)
51878{
51879 if (pEncoder == NULL || pFramesIn == NULL) {
51880 return 0;
51881 }
51882
51883 return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount);
51884}
51885#endif /* MA_NO_ENCODING */
51886
51887
51888
51889/**************************************************************************************************************************************************************
51890
51891Generation
51892
51893**************************************************************************************************************************************************************/
51894#ifndef MA_NO_GENERATION
51895MA_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)
51896{
51897 ma_waveform_config config;
51898
51899 MA_ZERO_OBJECT(&config);
51900 config.format = format;
51901 config.channels = channels;
51902 config.sampleRate = sampleRate;
51903 config.type = type;
51904 config.amplitude = amplitude;
51905 config.frequency = frequency;
51906
51907 return config;
51908}
51909
51910static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
51911{
51912 ma_uint64 framesRead = ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount);
51913
51914 if (pFramesRead != NULL) {
51915 *pFramesRead = framesRead;
51916 }
51917
51918 if (framesRead == 0) {
51919 return MA_AT_END;
51920 }
51921
51922 return MA_SUCCESS;
51923}
51924
51925static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
51926{
51927 return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex);
51928}
51929
51930static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
51931{
51932 ma_waveform* pWaveform = (ma_waveform*)pDataSource;
51933
51934 *pFormat = pWaveform->config.format;
51935 *pChannels = pWaveform->config.channels;
51936 *pSampleRate = pWaveform->config.sampleRate;
51937
51938 return MA_SUCCESS;
51939}
51940
51941static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
51942{
51943 ma_waveform* pWaveform = (ma_waveform*)pDataSource;
51944
51945 *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance);
51946
51947 return MA_SUCCESS;
51948}
51949
51950static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency)
51951{
51952 return (1.0 / (sampleRate / frequency));
51953}
51954
51955static void ma_waveform__update_advance(ma_waveform* pWaveform)
51956{
51957 pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency);
51958}
51959
51960static ma_data_source_vtable g_ma_waveform_data_source_vtable =
51961{
51962 ma_waveform__data_source_on_read,
51963 ma_waveform__data_source_on_seek,
51964 NULL, /* onMap */
51965 NULL, /* onUnmap */
51966 ma_waveform__data_source_on_get_data_format,
51967 ma_waveform__data_source_on_get_cursor,
51968 NULL /* onGetLength. There's no notion of a length in waveforms. */
51969};
51970
51971MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform)
51972{
51973 ma_result result;
51974 ma_data_source_config dataSourceConfig;
51975
51976 if (pWaveform == NULL) {
51977 return MA_INVALID_ARGS;
51978 }
51979
51980 MA_ZERO_OBJECT(pWaveform);
51981
51982 dataSourceConfig = ma_data_source_config_init();
51983 dataSourceConfig.vtable = &g_ma_waveform_data_source_vtable;
51984
51985 result = ma_data_source_init(&dataSourceConfig, &pWaveform->ds);
51986 if (result != MA_SUCCESS) {
51987 return result;
51988 }
51989
51990 pWaveform->config = *pConfig;
51991 pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency);
51992 pWaveform->time = 0;
51993
51994 return MA_SUCCESS;
51995}
51996
51997MA_API void ma_waveform_uninit(ma_waveform* pWaveform)
51998{
51999 if (pWaveform == NULL) {
52000 return;
52001 }
52002
52003 ma_data_source_uninit(&pWaveform->ds);
52004}
52005
52006MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude)
52007{
52008 if (pWaveform == NULL) {
52009 return MA_INVALID_ARGS;
52010 }
52011
52012 pWaveform->config.amplitude = amplitude;
52013 return MA_SUCCESS;
52014}
52015
52016MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency)
52017{
52018 if (pWaveform == NULL) {
52019 return MA_INVALID_ARGS;
52020 }
52021
52022 pWaveform->config.frequency = frequency;
52023 ma_waveform__update_advance(pWaveform);
52024
52025 return MA_SUCCESS;
52026}
52027
52028MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type)
52029{
52030 if (pWaveform == NULL) {
52031 return MA_INVALID_ARGS;
52032 }
52033
52034 pWaveform->config.type = type;
52035 return MA_SUCCESS;
52036}
52037
52038MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate)
52039{
52040 if (pWaveform == NULL) {
52041 return MA_INVALID_ARGS;
52042 }
52043
52044 pWaveform->config.sampleRate = sampleRate;
52045 ma_waveform__update_advance(pWaveform);
52046
52047 return MA_SUCCESS;
52048}
52049
52050static float ma_waveform_sine_f32(double time, double amplitude)
52051{
52052 return (float)(ma_sind(MA_TAU_D * time) * amplitude);
52053}
52054
52055static ma_int16 ma_waveform_sine_s16(double time, double amplitude)
52056{
52057 return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude));
52058}
52059
52060static float ma_waveform_square_f32(double time, double amplitude)
52061{
52062 double f = time - (ma_int64)time;
52063 double r;
52064
52065 if (f < 0.5) {
52066 r = amplitude;
52067 } else {
52068 r = -amplitude;
52069 }
52070
52071 return (float)r;
52072}
52073
52074static ma_int16 ma_waveform_square_s16(double time, double amplitude)
52075{
52076 return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, amplitude));
52077}
52078
52079static float ma_waveform_triangle_f32(double time, double amplitude)
52080{
52081 double f = time - (ma_int64)time;
52082 double r;
52083
52084 r = 2 * ma_abs(2 * (f - 0.5)) - 1;
52085
52086 return (float)(r * amplitude);
52087}
52088
52089static ma_int16 ma_waveform_triangle_s16(double time, double amplitude)
52090{
52091 return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude));
52092}
52093
52094static float ma_waveform_sawtooth_f32(double time, double amplitude)
52095{
52096 double f = time - (ma_int64)time;
52097 double r;
52098
52099 r = 2 * (f - 0.5);
52100
52101 return (float)(r * amplitude);
52102}
52103
52104static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude)
52105{
52106 return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude));
52107}
52108
52109static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
52110{
52111 ma_uint64 iFrame;
52112 ma_uint64 iChannel;
52113 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
52114 ma_uint32 bpf = bps * pWaveform->config.channels;
52115
52116 MA_ASSERT(pWaveform != NULL);
52117 MA_ASSERT(pFramesOut != NULL);
52118
52119 if (pWaveform->config.format == ma_format_f32) {
52120 float* pFramesOutF32 = (float*)pFramesOut;
52121 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52122 float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude);
52123 pWaveform->time += pWaveform->advance;
52124
52125 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
52126 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
52127 }
52128 }
52129 } else if (pWaveform->config.format == ma_format_s16) {
52130 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
52131 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52132 ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude);
52133 pWaveform->time += pWaveform->advance;
52134
52135 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
52136 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
52137 }
52138 }
52139 } else {
52140 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52141 float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude);
52142 pWaveform->time += pWaveform->advance;
52143
52144 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
52145 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
52146 }
52147 }
52148 }
52149}
52150
52151static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
52152{
52153 ma_uint64 iFrame;
52154 ma_uint64 iChannel;
52155 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
52156 ma_uint32 bpf = bps * pWaveform->config.channels;
52157
52158 MA_ASSERT(pWaveform != NULL);
52159 MA_ASSERT(pFramesOut != NULL);
52160
52161 if (pWaveform->config.format == ma_format_f32) {
52162 float* pFramesOutF32 = (float*)pFramesOut;
52163 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52164 float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude);
52165 pWaveform->time += pWaveform->advance;
52166
52167 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
52168 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
52169 }
52170 }
52171 } else if (pWaveform->config.format == ma_format_s16) {
52172 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
52173 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52174 ma_int16 s = ma_waveform_square_s16(pWaveform->time, pWaveform->config.amplitude);
52175 pWaveform->time += pWaveform->advance;
52176
52177 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
52178 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
52179 }
52180 }
52181 } else {
52182 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52183 float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude);
52184 pWaveform->time += pWaveform->advance;
52185
52186 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
52187 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
52188 }
52189 }
52190 }
52191}
52192
52193static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
52194{
52195 ma_uint64 iFrame;
52196 ma_uint64 iChannel;
52197 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
52198 ma_uint32 bpf = bps * pWaveform->config.channels;
52199
52200 MA_ASSERT(pWaveform != NULL);
52201 MA_ASSERT(pFramesOut != NULL);
52202
52203 if (pWaveform->config.format == ma_format_f32) {
52204 float* pFramesOutF32 = (float*)pFramesOut;
52205 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52206 float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude);
52207 pWaveform->time += pWaveform->advance;
52208
52209 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
52210 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
52211 }
52212 }
52213 } else if (pWaveform->config.format == ma_format_s16) {
52214 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
52215 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52216 ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude);
52217 pWaveform->time += pWaveform->advance;
52218
52219 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
52220 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
52221 }
52222 }
52223 } else {
52224 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52225 float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude);
52226 pWaveform->time += pWaveform->advance;
52227
52228 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
52229 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
52230 }
52231 }
52232 }
52233}
52234
52235static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
52236{
52237 ma_uint64 iFrame;
52238 ma_uint64 iChannel;
52239 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
52240 ma_uint32 bpf = bps * pWaveform->config.channels;
52241
52242 MA_ASSERT(pWaveform != NULL);
52243 MA_ASSERT(pFramesOut != NULL);
52244
52245 if (pWaveform->config.format == ma_format_f32) {
52246 float* pFramesOutF32 = (float*)pFramesOut;
52247 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52248 float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude);
52249 pWaveform->time += pWaveform->advance;
52250
52251 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
52252 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
52253 }
52254 }
52255 } else if (pWaveform->config.format == ma_format_s16) {
52256 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
52257 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52258 ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude);
52259 pWaveform->time += pWaveform->advance;
52260
52261 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
52262 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
52263 }
52264 }
52265 } else {
52266 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52267 float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude);
52268 pWaveform->time += pWaveform->advance;
52269
52270 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
52271 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
52272 }
52273 }
52274 }
52275}
52276
52277MA_API ma_uint64 ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
52278{
52279 if (pWaveform == NULL) {
52280 return 0;
52281 }
52282
52283 if (pFramesOut != NULL) {
52284 switch (pWaveform->config.type)
52285 {
52286 case ma_waveform_type_sine:
52287 {
52288 ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount);
52289 } break;
52290
52291 case ma_waveform_type_square:
52292 {
52293 ma_waveform_read_pcm_frames__square(pWaveform, pFramesOut, frameCount);
52294 } break;
52295
52296 case ma_waveform_type_triangle:
52297 {
52298 ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount);
52299 } break;
52300
52301 case ma_waveform_type_sawtooth:
52302 {
52303 ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount);
52304 } break;
52305
52306 default: return 0;
52307 }
52308 } else {
52309 pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */
52310 }
52311
52312 return frameCount;
52313}
52314
52315MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex)
52316{
52317 if (pWaveform == NULL) {
52318 return MA_INVALID_ARGS;
52319 }
52320
52321 pWaveform->time = pWaveform->advance * (ma_int64)frameIndex; /* Casting for VC6. Won't be an issue in practice. */
52322
52323 return MA_SUCCESS;
52324}
52325
52326
52327MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude)
52328{
52329 ma_noise_config config;
52330 MA_ZERO_OBJECT(&config);
52331
52332 config.format = format;
52333 config.channels = channels;
52334 config.type = type;
52335 config.seed = seed;
52336 config.amplitude = amplitude;
52337
52338 if (config.seed == 0) {
52339 config.seed = MA_DEFAULT_LCG_SEED;
52340 }
52341
52342 return config;
52343}
52344
52345
52346static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
52347{
52348 ma_uint64 framesRead = ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount);
52349
52350 if (pFramesRead != NULL) {
52351 *pFramesRead = framesRead;
52352 }
52353
52354 if (framesRead == 0) {
52355 return MA_AT_END;
52356 }
52357
52358 return MA_SUCCESS;
52359}
52360
52361static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
52362{
52363 /* No-op. Just pretend to be successful. */
52364 (void)pDataSource;
52365 (void)frameIndex;
52366 return MA_SUCCESS;
52367}
52368
52369static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
52370{
52371 ma_noise* pNoise = (ma_noise*)pDataSource;
52372
52373 *pFormat = pNoise->config.format;
52374 *pChannels = pNoise->config.channels;
52375 *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */
52376
52377 return MA_SUCCESS;
52378}
52379
52380static ma_data_source_vtable g_ma_noise_data_source_vtable =
52381{
52382 ma_noise__data_source_on_read,
52383 ma_noise__data_source_on_seek, /* No-op for noise. */
52384 NULL, /* onMap */
52385 NULL, /* onUnmap */
52386 ma_noise__data_source_on_get_data_format,
52387 NULL, /* onGetCursor. No notion of a cursor for noise. */
52388 NULL /* onGetLength. No notion of a length for noise. */
52389};
52390
52391MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, ma_noise* pNoise)
52392{
52393 ma_result result;
52394 ma_data_source_config dataSourceConfig;
52395
52396 if (pNoise == NULL) {
52397 return MA_INVALID_ARGS;
52398 }
52399
52400 MA_ZERO_OBJECT(pNoise);
52401
52402 if (pConfig == NULL) {
52403 return MA_INVALID_ARGS;
52404 }
52405
52406 if (pConfig->channels < MA_MIN_CHANNELS || pConfig->channels > MA_MAX_CHANNELS) {
52407 return MA_INVALID_ARGS;
52408 }
52409
52410 dataSourceConfig = ma_data_source_config_init();
52411 dataSourceConfig.vtable = &g_ma_noise_data_source_vtable;
52412
52413 result = ma_data_source_init(&dataSourceConfig, &pNoise->ds);
52414 if (result != MA_SUCCESS) {
52415 return result;
52416 }
52417
52418 pNoise->config = *pConfig;
52419 ma_lcg_seed(&pNoise->lcg, pConfig->seed);
52420
52421 if (pNoise->config.type == ma_noise_type_pink) {
52422 ma_uint32 iChannel;
52423 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
52424 pNoise->state.pink.accumulation[iChannel] = 0;
52425 pNoise->state.pink.counter[iChannel] = 1;
52426 }
52427 }
52428
52429 if (pNoise->config.type == ma_noise_type_brownian) {
52430 ma_uint32 iChannel;
52431 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
52432 pNoise->state.brownian.accumulation[iChannel] = 0;
52433 }
52434 }
52435
52436 return MA_SUCCESS;
52437}
52438
52439MA_API void ma_noise_uninit(ma_noise* pNoise)
52440{
52441 if (pNoise == NULL) {
52442 return;
52443 }
52444
52445 ma_data_source_uninit(&pNoise->ds);
52446}
52447
52448MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude)
52449{
52450 if (pNoise == NULL) {
52451 return MA_INVALID_ARGS;
52452 }
52453
52454 pNoise->config.amplitude = amplitude;
52455 return MA_SUCCESS;
52456}
52457
52458MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed)
52459{
52460 if (pNoise == NULL) {
52461 return MA_INVALID_ARGS;
52462 }
52463
52464 pNoise->lcg.state = seed;
52465 return MA_SUCCESS;
52466}
52467
52468
52469MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type)
52470{
52471 if (pNoise == NULL) {
52472 return MA_INVALID_ARGS;
52473 }
52474
52475 pNoise->config.type = type;
52476 return MA_SUCCESS;
52477}
52478
52479static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise)
52480{
52481 return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude);
52482}
52483
52484static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise)
52485{
52486 return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise));
52487}
52488
52489static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
52490{
52491 ma_uint64 iFrame;
52492 ma_uint32 iChannel;
52493 const ma_uint32 channels = pNoise->config.channels;
52494 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
52495
52496 if (pNoise->config.format == ma_format_f32) {
52497 float* pFramesOutF32 = (float*)pFramesOut;
52498 if (pNoise->config.duplicateChannels) {
52499 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52500 float s = ma_noise_f32_white(pNoise);
52501 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52502 pFramesOutF32[iFrame*channels + iChannel] = s;
52503 }
52504 }
52505 } else {
52506 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52507 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52508 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise);
52509 }
52510 }
52511 }
52512 } else if (pNoise->config.format == ma_format_s16) {
52513 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
52514 if (pNoise->config.duplicateChannels) {
52515 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52516 ma_int16 s = ma_noise_s16_white(pNoise);
52517 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52518 pFramesOutS16[iFrame*channels + iChannel] = s;
52519 }
52520 }
52521 } else {
52522 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52523 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52524 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise);
52525 }
52526 }
52527 }
52528 } else {
52529 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
52530 const ma_uint32 bpf = bps * channels;
52531
52532 if (pNoise->config.duplicateChannels) {
52533 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52534 float s = ma_noise_f32_white(pNoise);
52535 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52536 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
52537 }
52538 }
52539 } else {
52540 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52541 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52542 float s = ma_noise_f32_white(pNoise);
52543 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
52544 }
52545 }
52546 }
52547 }
52548
52549 return frameCount;
52550}
52551
52552
52553static MA_INLINE unsigned int ma_tzcnt32(unsigned int x)
52554{
52555 unsigned int n;
52556
52557 /* Special case for odd numbers since they should happen about half the time. */
52558 if (x & 0x1) {
52559 return 0;
52560 }
52561
52562 if (x == 0) {
52563 return sizeof(x) << 3;
52564 }
52565
52566 n = 1;
52567 if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; }
52568 if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; }
52569 if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; }
52570 if ((x & 0x00000003) == 0) { x >>= 2; n += 2; }
52571 n -= x & 0x00000001;
52572
52573 return n;
52574}
52575
52576/*
52577Pink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h
52578
52579This is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/
52580*/
52581static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel)
52582{
52583 double result;
52584 double binPrev;
52585 double binNext;
52586 unsigned int ibin;
52587
52588 ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (ma_countof(pNoise->state.pink.bin[0]) - 1);
52589
52590 binPrev = pNoise->state.pink.bin[iChannel][ibin];
52591 binNext = ma_lcg_rand_f64(&pNoise->lcg);
52592 pNoise->state.pink.bin[iChannel][ibin] = binNext;
52593
52594 pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev);
52595 pNoise->state.pink.counter[iChannel] += 1;
52596
52597 result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]);
52598 result /= 10;
52599
52600 return (float)(result * pNoise->config.amplitude);
52601}
52602
52603static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel)
52604{
52605 return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel));
52606}
52607
52608static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
52609{
52610 ma_uint64 iFrame;
52611 ma_uint32 iChannel;
52612 const ma_uint32 channels = pNoise->config.channels;
52613 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
52614
52615 if (pNoise->config.format == ma_format_f32) {
52616 float* pFramesOutF32 = (float*)pFramesOut;
52617 if (pNoise->config.duplicateChannels) {
52618 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52619 float s = ma_noise_f32_pink(pNoise, 0);
52620 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52621 pFramesOutF32[iFrame*channels + iChannel] = s;
52622 }
52623 }
52624 } else {
52625 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52626 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52627 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel);
52628 }
52629 }
52630 }
52631 } else if (pNoise->config.format == ma_format_s16) {
52632 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
52633 if (pNoise->config.duplicateChannels) {
52634 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52635 ma_int16 s = ma_noise_s16_pink(pNoise, 0);
52636 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52637 pFramesOutS16[iFrame*channels + iChannel] = s;
52638 }
52639 }
52640 } else {
52641 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52642 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52643 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel);
52644 }
52645 }
52646 }
52647 } else {
52648 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
52649 const ma_uint32 bpf = bps * channels;
52650
52651 if (pNoise->config.duplicateChannels) {
52652 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52653 float s = ma_noise_f32_pink(pNoise, 0);
52654 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52655 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
52656 }
52657 }
52658 } else {
52659 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52660 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52661 float s = ma_noise_f32_pink(pNoise, iChannel);
52662 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
52663 }
52664 }
52665 }
52666 }
52667
52668 return frameCount;
52669}
52670
52671
52672static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel)
52673{
52674 double result;
52675
52676 result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]);
52677 result /= 1.005; /* Don't escape the -1..1 range on average. */
52678
52679 pNoise->state.brownian.accumulation[iChannel] = result;
52680 result /= 20;
52681
52682 return (float)(result * pNoise->config.amplitude);
52683}
52684
52685static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel)
52686{
52687 return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel));
52688}
52689
52690static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
52691{
52692 ma_uint64 iFrame;
52693 ma_uint32 iChannel;
52694 const ma_uint32 channels = pNoise->config.channels;
52695 MA_ASSUME(channels >= MA_MIN_CHANNELS && channels <= MA_MAX_CHANNELS);
52696
52697 if (pNoise->config.format == ma_format_f32) {
52698 float* pFramesOutF32 = (float*)pFramesOut;
52699 if (pNoise->config.duplicateChannels) {
52700 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52701 float s = ma_noise_f32_brownian(pNoise, 0);
52702 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52703 pFramesOutF32[iFrame*channels + iChannel] = s;
52704 }
52705 }
52706 } else {
52707 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52708 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52709 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel);
52710 }
52711 }
52712 }
52713 } else if (pNoise->config.format == ma_format_s16) {
52714 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
52715 if (pNoise->config.duplicateChannels) {
52716 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52717 ma_int16 s = ma_noise_s16_brownian(pNoise, 0);
52718 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52719 pFramesOutS16[iFrame*channels + iChannel] = s;
52720 }
52721 }
52722 } else {
52723 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52724 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52725 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel);
52726 }
52727 }
52728 }
52729 } else {
52730 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
52731 const ma_uint32 bpf = bps * channels;
52732
52733 if (pNoise->config.duplicateChannels) {
52734 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52735 float s = ma_noise_f32_brownian(pNoise, 0);
52736 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52737 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
52738 }
52739 }
52740 } else {
52741 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52742 for (iChannel = 0; iChannel < channels; iChannel += 1) {
52743 float s = ma_noise_f32_brownian(pNoise, iChannel);
52744 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
52745 }
52746 }
52747 }
52748 }
52749
52750 return frameCount;
52751}
52752
52753MA_API ma_uint64 ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
52754{
52755 if (pNoise == NULL) {
52756 return 0;
52757 }
52758
52759 /* 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. */
52760 if (pFramesOut == NULL) {
52761 return frameCount;
52762 }
52763
52764 if (pNoise->config.type == ma_noise_type_white) {
52765 return ma_noise_read_pcm_frames__white(pNoise, pFramesOut, frameCount);
52766 }
52767
52768 if (pNoise->config.type == ma_noise_type_pink) {
52769 return ma_noise_read_pcm_frames__pink(pNoise, pFramesOut, frameCount);
52770 }
52771
52772 if (pNoise->config.type == ma_noise_type_brownian) {
52773 return ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount);
52774 }
52775
52776 /* Should never get here. */
52777 MA_ASSERT(MA_FALSE);
52778 return 0;
52779}
52780#endif /* MA_NO_GENERATION */
52781
52782
52783
52784/**************************************************************************************************************************************************************
52785***************************************************************************************************************************************************************
52786
52787Auto Generated
52788==============
52789All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as dr_wav, dr_flac, etc. If you find a bug in the
52790code below please report the bug to the respective repository for the relevant project (probably dr_libs).
52791
52792***************************************************************************************************************************************************************
52793**************************************************************************************************************************************************************/
52794#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
52795#if !defined(DR_WAV_IMPLEMENTATION) && !defined(DRWAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
52796/* dr_wav_c begin */
52797#ifndef dr_wav_c
52798#define dr_wav_c
52799#include <stdlib.h>
52800#include <string.h>
52801#include <limits.h>
52802#ifndef DR_WAV_NO_STDIO
52803#include <stdio.h>
52804#include <wchar.h>
52805#endif
52806#ifndef DRWAV_ASSERT
52807#include <assert.h>
52808#define DRWAV_ASSERT(expression) assert(expression)
52809#endif
52810#ifndef DRWAV_MALLOC
52811#define DRWAV_MALLOC(sz) malloc((sz))
52812#endif
52813#ifndef DRWAV_REALLOC
52814#define DRWAV_REALLOC(p, sz) realloc((p), (sz))
52815#endif
52816#ifndef DRWAV_FREE
52817#define DRWAV_FREE(p) free((p))
52818#endif
52819#ifndef DRWAV_COPY_MEMORY
52820#define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
52821#endif
52822#ifndef DRWAV_ZERO_MEMORY
52823#define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
52824#endif
52825#ifndef DRWAV_ZERO_OBJECT
52826#define DRWAV_ZERO_OBJECT(p) DRWAV_ZERO_MEMORY((p), sizeof(*p))
52827#endif
52828#define drwav_countof(x) (sizeof(x) / sizeof(x[0]))
52829#define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
52830#define drwav_min(a, b) (((a) < (b)) ? (a) : (b))
52831#define drwav_max(a, b) (((a) > (b)) ? (a) : (b))
52832#define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x))))
52833#define DRWAV_MAX_SIMD_VECTOR_SIZE 64
52834#if defined(__x86_64__) || defined(_M_X64)
52835 #define DRWAV_X64
52836#elif defined(__i386) || defined(_M_IX86)
52837 #define DRWAV_X86
52838#elif defined(__arm__) || defined(_M_ARM)
52839 #define DRWAV_ARM
52840#endif
52841#ifdef _MSC_VER
52842 #define DRWAV_INLINE __forceinline
52843#elif defined(__GNUC__)
52844 #if defined(__STRICT_ANSI__)
52845 #define DRWAV_INLINE __inline__ __attribute__((always_inline))
52846 #else
52847 #define DRWAV_INLINE inline __attribute__((always_inline))
52848 #endif
52849#elif defined(__WATCOMC__)
52850 #define DRWAV_INLINE __inline
52851#else
52852 #define DRWAV_INLINE
52853#endif
52854#if defined(SIZE_MAX)
52855 #define DRWAV_SIZE_MAX SIZE_MAX
52856#else
52857 #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
52858 #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF)
52859 #else
52860 #define DRWAV_SIZE_MAX 0xFFFFFFFF
52861 #endif
52862#endif
52863#if defined(_MSC_VER) && _MSC_VER >= 1400
52864 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
52865 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
52866 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
52867#elif defined(__clang__)
52868 #if defined(__has_builtin)
52869 #if __has_builtin(__builtin_bswap16)
52870 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
52871 #endif
52872 #if __has_builtin(__builtin_bswap32)
52873 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
52874 #endif
52875 #if __has_builtin(__builtin_bswap64)
52876 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
52877 #endif
52878 #endif
52879#elif defined(__GNUC__)
52880 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
52881 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
52882 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
52883 #endif
52884 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
52885 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
52886 #endif
52887#endif
52888DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision)
52889{
52890 if (pMajor) {
52891 *pMajor = DRWAV_VERSION_MAJOR;
52892 }
52893 if (pMinor) {
52894 *pMinor = DRWAV_VERSION_MINOR;
52895 }
52896 if (pRevision) {
52897 *pRevision = DRWAV_VERSION_REVISION;
52898 }
52899}
52900DRWAV_API const char* drwav_version_string(void)
52901{
52902 return DRWAV_VERSION_STRING;
52903}
52904#ifndef DRWAV_MAX_SAMPLE_RATE
52905#define DRWAV_MAX_SAMPLE_RATE 384000
52906#endif
52907#ifndef DRWAV_MAX_CHANNELS
52908#define DRWAV_MAX_CHANNELS 256
52909#endif
52910#ifndef DRWAV_MAX_BITS_PER_SAMPLE
52911#define DRWAV_MAX_BITS_PER_SAMPLE 64
52912#endif
52913static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00};
52914static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
52915static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
52916static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
52917static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
52918static DRWAV_INLINE int drwav__is_little_endian(void)
52919{
52920#if defined(DRWAV_X86) || defined(DRWAV_X64)
52921 return DRWAV_TRUE;
52922#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
52923 return DRWAV_TRUE;
52924#else
52925 int n = 1;
52926 return (*(char*)&n) == 1;
52927#endif
52928}
52929static DRWAV_INLINE void drwav_bytes_to_guid(const drwav_uint8* data, drwav_uint8* guid)
52930{
52931 int i;
52932 for (i = 0; i < 16; ++i) {
52933 guid[i] = data[i];
52934 }
52935}
52936static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n)
52937{
52938#ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC
52939 #if defined(_MSC_VER)
52940 return _byteswap_ushort(n);
52941 #elif defined(__GNUC__) || defined(__clang__)
52942 return __builtin_bswap16(n);
52943 #else
52944 #error "This compiler does not support the byte swap intrinsic."
52945 #endif
52946#else
52947 return ((n & 0xFF00) >> 8) |
52948 ((n & 0x00FF) << 8);
52949#endif
52950}
52951static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n)
52952{
52953#ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC
52954 #if defined(_MSC_VER)
52955 return _byteswap_ulong(n);
52956 #elif defined(__GNUC__) || defined(__clang__)
52957 #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT)
52958 drwav_uint32 r;
52959 __asm__ __volatile__ (
52960 #if defined(DRWAV_64BIT)
52961 "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n)
52962 #else
52963 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
52964 #endif
52965 );
52966 return r;
52967 #else
52968 return __builtin_bswap32(n);
52969 #endif
52970 #else
52971 #error "This compiler does not support the byte swap intrinsic."
52972 #endif
52973#else
52974 return ((n & 0xFF000000) >> 24) |
52975 ((n & 0x00FF0000) >> 8) |
52976 ((n & 0x0000FF00) << 8) |
52977 ((n & 0x000000FF) << 24);
52978#endif
52979}
52980static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n)
52981{
52982#ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC
52983 #if defined(_MSC_VER)
52984 return _byteswap_uint64(n);
52985 #elif defined(__GNUC__) || defined(__clang__)
52986 return __builtin_bswap64(n);
52987 #else
52988 #error "This compiler does not support the byte swap intrinsic."
52989 #endif
52990#else
52991 return ((n & ((drwav_uint64)0xFF000000 << 32)) >> 56) |
52992 ((n & ((drwav_uint64)0x00FF0000 << 32)) >> 40) |
52993 ((n & ((drwav_uint64)0x0000FF00 << 32)) >> 24) |
52994 ((n & ((drwav_uint64)0x000000FF << 32)) >> 8) |
52995 ((n & ((drwav_uint64)0xFF000000 )) << 8) |
52996 ((n & ((drwav_uint64)0x00FF0000 )) << 24) |
52997 ((n & ((drwav_uint64)0x0000FF00 )) << 40) |
52998 ((n & ((drwav_uint64)0x000000FF )) << 56);
52999#endif
53000}
53001static DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n)
53002{
53003 return (drwav_int16)drwav__bswap16((drwav_uint16)n);
53004}
53005static DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount)
53006{
53007 drwav_uint64 iSample;
53008 for (iSample = 0; iSample < sampleCount; iSample += 1) {
53009 pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]);
53010 }
53011}
53012static DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p)
53013{
53014 drwav_uint8 t;
53015 t = p[0];
53016 p[0] = p[2];
53017 p[2] = t;
53018}
53019static DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount)
53020{
53021 drwav_uint64 iSample;
53022 for (iSample = 0; iSample < sampleCount; iSample += 1) {
53023 drwav_uint8* pSample = pSamples + (iSample*3);
53024 drwav__bswap_s24(pSample);
53025 }
53026}
53027static DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n)
53028{
53029 return (drwav_int32)drwav__bswap32((drwav_uint32)n);
53030}
53031static DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount)
53032{
53033 drwav_uint64 iSample;
53034 for (iSample = 0; iSample < sampleCount; iSample += 1) {
53035 pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]);
53036 }
53037}
53038static DRWAV_INLINE float drwav__bswap_f32(float n)
53039{
53040 union {
53041 drwav_uint32 i;
53042 float f;
53043 } x;
53044 x.f = n;
53045 x.i = drwav__bswap32(x.i);
53046 return x.f;
53047}
53048static DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount)
53049{
53050 drwav_uint64 iSample;
53051 for (iSample = 0; iSample < sampleCount; iSample += 1) {
53052 pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]);
53053 }
53054}
53055static DRWAV_INLINE double drwav__bswap_f64(double n)
53056{
53057 union {
53058 drwav_uint64 i;
53059 double f;
53060 } x;
53061 x.f = n;
53062 x.i = drwav__bswap64(x.i);
53063 return x.f;
53064}
53065static DRWAV_INLINE void drwav__bswap_samples_f64(double* pSamples, drwav_uint64 sampleCount)
53066{
53067 drwav_uint64 iSample;
53068 for (iSample = 0; iSample < sampleCount; iSample += 1) {
53069 pSamples[iSample] = drwav__bswap_f64(pSamples[iSample]);
53070 }
53071}
53072static DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample)
53073{
53074 switch (bytesPerSample)
53075 {
53076 case 2:
53077 {
53078 drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount);
53079 } break;
53080 case 3:
53081 {
53082 drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount);
53083 } break;
53084 case 4:
53085 {
53086 drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount);
53087 } break;
53088 default:
53089 {
53090 DRWAV_ASSERT(DRWAV_FALSE);
53091 } break;
53092 }
53093}
53094static DRWAV_INLINE void drwav__bswap_samples_ieee(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample)
53095{
53096 switch (bytesPerSample)
53097 {
53098 #if 0
53099 case 2:
53100 {
53101 drwav__bswap_samples_f16((drwav_float16*)pSamples, sampleCount);
53102 } break;
53103 #endif
53104 case 4:
53105 {
53106 drwav__bswap_samples_f32((float*)pSamples, sampleCount);
53107 } break;
53108 case 8:
53109 {
53110 drwav__bswap_samples_f64((double*)pSamples, sampleCount);
53111 } break;
53112 default:
53113 {
53114 DRWAV_ASSERT(DRWAV_FALSE);
53115 } break;
53116 }
53117}
53118static DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample, drwav_uint16 format)
53119{
53120 switch (format)
53121 {
53122 case DR_WAVE_FORMAT_PCM:
53123 {
53124 drwav__bswap_samples_pcm(pSamples, sampleCount, bytesPerSample);
53125 } break;
53126 case DR_WAVE_FORMAT_IEEE_FLOAT:
53127 {
53128 drwav__bswap_samples_ieee(pSamples, sampleCount, bytesPerSample);
53129 } break;
53130 case DR_WAVE_FORMAT_ALAW:
53131 case DR_WAVE_FORMAT_MULAW:
53132 {
53133 drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount);
53134 } break;
53135 case DR_WAVE_FORMAT_ADPCM:
53136 case DR_WAVE_FORMAT_DVI_ADPCM:
53137 default:
53138 {
53139 DRWAV_ASSERT(DRWAV_FALSE);
53140 } break;
53141 }
53142}
53143DRWAV_PRIVATE void* drwav__malloc_default(size_t sz, void* pUserData)
53144{
53145 (void)pUserData;
53146 return DRWAV_MALLOC(sz);
53147}
53148DRWAV_PRIVATE void* drwav__realloc_default(void* p, size_t sz, void* pUserData)
53149{
53150 (void)pUserData;
53151 return DRWAV_REALLOC(p, sz);
53152}
53153DRWAV_PRIVATE void drwav__free_default(void* p, void* pUserData)
53154{
53155 (void)pUserData;
53156 DRWAV_FREE(p);
53157}
53158DRWAV_PRIVATE void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocation_callbacks* pAllocationCallbacks)
53159{
53160 if (pAllocationCallbacks == NULL) {
53161 return NULL;
53162 }
53163 if (pAllocationCallbacks->onMalloc != NULL) {
53164 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
53165 }
53166 if (pAllocationCallbacks->onRealloc != NULL) {
53167 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
53168 }
53169 return NULL;
53170}
53171DRWAV_PRIVATE void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_allocation_callbacks* pAllocationCallbacks)
53172{
53173 if (pAllocationCallbacks == NULL) {
53174 return NULL;
53175 }
53176 if (pAllocationCallbacks->onRealloc != NULL) {
53177 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
53178 }
53179 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
53180 void* p2;
53181 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
53182 if (p2 == NULL) {
53183 return NULL;
53184 }
53185 if (p != NULL) {
53186 DRWAV_COPY_MEMORY(p2, p, szOld);
53187 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
53188 }
53189 return p2;
53190 }
53191 return NULL;
53192}
53193DRWAV_PRIVATE void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks)
53194{
53195 if (p == NULL || pAllocationCallbacks == NULL) {
53196 return;
53197 }
53198 if (pAllocationCallbacks->onFree != NULL) {
53199 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
53200 }
53201}
53202DRWAV_PRIVATE drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks)
53203{
53204 if (pAllocationCallbacks != NULL) {
53205 return *pAllocationCallbacks;
53206 } else {
53207 drwav_allocation_callbacks allocationCallbacks;
53208 allocationCallbacks.pUserData = NULL;
53209 allocationCallbacks.onMalloc = drwav__malloc_default;
53210 allocationCallbacks.onRealloc = drwav__realloc_default;
53211 allocationCallbacks.onFree = drwav__free_default;
53212 return allocationCallbacks;
53213 }
53214}
53215static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag)
53216{
53217 return
53218 formatTag == DR_WAVE_FORMAT_ADPCM ||
53219 formatTag == DR_WAVE_FORMAT_DVI_ADPCM;
53220}
53221DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize)
53222{
53223 return (unsigned int)(chunkSize % 2);
53224}
53225DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize)
53226{
53227 return (unsigned int)(chunkSize % 8);
53228}
53229DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
53230DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
53231DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
53232DRWAV_PRIVATE drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut)
53233{
53234 if (container == drwav_container_riff || container == drwav_container_rf64) {
53235 drwav_uint8 sizeInBytes[4];
53236 if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) {
53237 return DRWAV_AT_END;
53238 }
53239 if (onRead(pUserData, sizeInBytes, 4) != 4) {
53240 return DRWAV_INVALID_FILE;
53241 }
53242 pHeaderOut->sizeInBytes = drwav_bytes_to_u32(sizeInBytes);
53243 pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes);
53244 *pRunningBytesReadOut += 8;
53245 } else {
53246 drwav_uint8 sizeInBytes[8];
53247 if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) {
53248 return DRWAV_AT_END;
53249 }
53250 if (onRead(pUserData, sizeInBytes, 8) != 8) {
53251 return DRWAV_INVALID_FILE;
53252 }
53253 pHeaderOut->sizeInBytes = drwav_bytes_to_u64(sizeInBytes) - 24;
53254 pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes);
53255 *pRunningBytesReadOut += 24;
53256 }
53257 return DRWAV_SUCCESS;
53258}
53259DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
53260{
53261 drwav_uint64 bytesRemainingToSeek = offset;
53262 while (bytesRemainingToSeek > 0) {
53263 if (bytesRemainingToSeek > 0x7FFFFFFF) {
53264 if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
53265 return DRWAV_FALSE;
53266 }
53267 bytesRemainingToSeek -= 0x7FFFFFFF;
53268 } else {
53269 if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) {
53270 return DRWAV_FALSE;
53271 }
53272 bytesRemainingToSeek = 0;
53273 }
53274 }
53275 return DRWAV_TRUE;
53276}
53277DRWAV_PRIVATE drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
53278{
53279 if (offset <= 0x7FFFFFFF) {
53280 return onSeek(pUserData, (int)offset, drwav_seek_origin_start);
53281 }
53282 if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) {
53283 return DRWAV_FALSE;
53284 }
53285 offset -= 0x7FFFFFFF;
53286 for (;;) {
53287 if (offset <= 0x7FFFFFFF) {
53288 return onSeek(pUserData, (int)offset, drwav_seek_origin_current);
53289 }
53290 if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
53291 return DRWAV_FALSE;
53292 }
53293 offset -= 0x7FFFFFFF;
53294 }
53295}
53296DRWAV_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)
53297{
53298 drwav_chunk_header header;
53299 drwav_uint8 fmt[16];
53300 if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) {
53301 return DRWAV_FALSE;
53302 }
53303 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))) {
53304 if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) {
53305 return DRWAV_FALSE;
53306 }
53307 *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize;
53308 if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) {
53309 return DRWAV_FALSE;
53310 }
53311 }
53312 if (container == drwav_container_riff || container == drwav_container_rf64) {
53313 if (!drwav_fourcc_equal(header.id.fourcc, "fmt ")) {
53314 return DRWAV_FALSE;
53315 }
53316 } else {
53317 if (!drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT)) {
53318 return DRWAV_FALSE;
53319 }
53320 }
53321 if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) {
53322 return DRWAV_FALSE;
53323 }
53324 *pRunningBytesReadOut += sizeof(fmt);
53325 fmtOut->formatTag = drwav_bytes_to_u16(fmt + 0);
53326 fmtOut->channels = drwav_bytes_to_u16(fmt + 2);
53327 fmtOut->sampleRate = drwav_bytes_to_u32(fmt + 4);
53328 fmtOut->avgBytesPerSec = drwav_bytes_to_u32(fmt + 8);
53329 fmtOut->blockAlign = drwav_bytes_to_u16(fmt + 12);
53330 fmtOut->bitsPerSample = drwav_bytes_to_u16(fmt + 14);
53331 fmtOut->extendedSize = 0;
53332 fmtOut->validBitsPerSample = 0;
53333 fmtOut->channelMask = 0;
53334 memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat));
53335 if (header.sizeInBytes > 16) {
53336 drwav_uint8 fmt_cbSize[2];
53337 int bytesReadSoFar = 0;
53338 if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) {
53339 return DRWAV_FALSE;
53340 }
53341 *pRunningBytesReadOut += sizeof(fmt_cbSize);
53342 bytesReadSoFar = 18;
53343 fmtOut->extendedSize = drwav_bytes_to_u16(fmt_cbSize);
53344 if (fmtOut->extendedSize > 0) {
53345 if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
53346 if (fmtOut->extendedSize != 22) {
53347 return DRWAV_FALSE;
53348 }
53349 }
53350 if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
53351 drwav_uint8 fmtext[22];
53352 if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) {
53353 return DRWAV_FALSE;
53354 }
53355 fmtOut->validBitsPerSample = drwav_bytes_to_u16(fmtext + 0);
53356 fmtOut->channelMask = drwav_bytes_to_u32(fmtext + 2);
53357 drwav_bytes_to_guid(fmtext + 6, fmtOut->subFormat);
53358 } else {
53359 if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) {
53360 return DRWAV_FALSE;
53361 }
53362 }
53363 *pRunningBytesReadOut += fmtOut->extendedSize;
53364 bytesReadSoFar += fmtOut->extendedSize;
53365 }
53366 if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) {
53367 return DRWAV_FALSE;
53368 }
53369 *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar);
53370 }
53371 if (header.paddingSize > 0) {
53372 if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) {
53373 return DRWAV_FALSE;
53374 }
53375 *pRunningBytesReadOut += header.paddingSize;
53376 }
53377 return DRWAV_TRUE;
53378}
53379DRWAV_PRIVATE size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor)
53380{
53381 size_t bytesRead;
53382 DRWAV_ASSERT(onRead != NULL);
53383 DRWAV_ASSERT(pCursor != NULL);
53384 bytesRead = onRead(pUserData, pBufferOut, bytesToRead);
53385 *pCursor += bytesRead;
53386 return bytesRead;
53387}
53388#if 0
53389DRWAV_PRIVATE drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor)
53390{
53391 DRWAV_ASSERT(onSeek != NULL);
53392 DRWAV_ASSERT(pCursor != NULL);
53393 if (!onSeek(pUserData, offset, origin)) {
53394 return DRWAV_FALSE;
53395 }
53396 if (origin == drwav_seek_origin_start) {
53397 *pCursor = offset;
53398 } else {
53399 *pCursor += offset;
53400 }
53401 return DRWAV_TRUE;
53402}
53403#endif
53404#define DRWAV_SMPL_BYTES 36
53405#define DRWAV_SMPL_LOOP_BYTES 24
53406#define DRWAV_INST_BYTES 7
53407#define DRWAV_ACID_BYTES 24
53408#define DRWAV_CUE_BYTES 4
53409#define DRWAV_BEXT_BYTES 602
53410#define DRWAV_BEXT_DESCRIPTION_BYTES 256
53411#define DRWAV_BEXT_ORIGINATOR_NAME_BYTES 32
53412#define DRWAV_BEXT_ORIGINATOR_REF_BYTES 32
53413#define DRWAV_BEXT_RESERVED_BYTES 180
53414#define DRWAV_BEXT_UMID_BYTES 64
53415#define DRWAV_CUE_POINT_BYTES 24
53416#define DRWAV_LIST_LABEL_OR_NOTE_BYTES 4
53417#define DRWAV_LIST_LABELLED_TEXT_BYTES 20
53418#define DRWAV_METADATA_ALIGNMENT 8
53419typedef enum
53420{
53421 drwav__metadata_parser_stage_count,
53422 drwav__metadata_parser_stage_read
53423} drwav__metadata_parser_stage;
53424typedef struct
53425{
53426 drwav_read_proc onRead;
53427 drwav_seek_proc onSeek;
53428 void *pReadSeekUserData;
53429 drwav__metadata_parser_stage stage;
53430 drwav_metadata *pMetadata;
53431 drwav_uint32 metadataCount;
53432 drwav_uint8 *pData;
53433 drwav_uint8 *pDataCursor;
53434 drwav_uint64 metadataCursor;
53435 drwav_uint64 extraCapacity;
53436} drwav__metadata_parser;
53437DRWAV_PRIVATE size_t drwav__metadata_memory_capacity(drwav__metadata_parser* pParser)
53438{
53439 drwav_uint64 cap = sizeof(drwav_metadata) * (drwav_uint64)pParser->metadataCount + pParser->extraCapacity;
53440 if (cap > DRWAV_SIZE_MAX) {
53441 return 0;
53442 }
53443 return (size_t)cap;
53444}
53445DRWAV_PRIVATE drwav_uint8* drwav__metadata_get_memory(drwav__metadata_parser* pParser, size_t size, size_t align)
53446{
53447 drwav_uint8* pResult;
53448 if (align) {
53449 drwav_uintptr modulo = (drwav_uintptr)pParser->pDataCursor % align;
53450 if (modulo != 0) {
53451 pParser->pDataCursor += align - modulo;
53452 }
53453 }
53454 pResult = pParser->pDataCursor;
53455 DRWAV_ASSERT((pResult + size) <= (pParser->pData + drwav__metadata_memory_capacity(pParser)));
53456 pParser->pDataCursor += size;
53457 return pResult;
53458}
53459DRWAV_PRIVATE void drwav__metadata_request_extra_memory_for_stage_2(drwav__metadata_parser* pParser, size_t bytes, size_t align)
53460{
53461 size_t extra = bytes + (align ? (align - 1) : 0);
53462 pParser->extraCapacity += extra;
53463}
53464DRWAV_PRIVATE drwav_result drwav__metadata_alloc(drwav__metadata_parser* pParser, drwav_allocation_callbacks* pAllocationCallbacks)
53465{
53466 if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) {
53467 free(pParser->pData);
53468 pParser->pData = (drwav_uint8*)pAllocationCallbacks->onMalloc(drwav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData);
53469 pParser->pDataCursor = pParser->pData;
53470 if (pParser->pData == NULL) {
53471 return DRWAV_OUT_OF_MEMORY;
53472 }
53473 pParser->pMetadata = (drwav_metadata*)drwav__metadata_get_memory(pParser, sizeof(drwav_metadata) * pParser->metadataCount, 1);
53474 pParser->metadataCursor = 0;
53475 }
53476 return DRWAV_SUCCESS;
53477}
53478DRWAV_PRIVATE size_t drwav__metadata_parser_read(drwav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor)
53479{
53480 if (pCursor != NULL) {
53481 return drwav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor);
53482 } else {
53483 return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead);
53484 }
53485}
53486DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata)
53487{
53488 drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES];
53489 drwav_uint64 totalBytesRead = 0;
53490 size_t bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead);
53491 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
53492 if (bytesJustRead == sizeof(smplHeaderData)) {
53493 drwav_uint32 iSampleLoop;
53494 pMetadata->type = drwav_metadata_type_smpl;
53495 pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0);
53496 pMetadata->data.smpl.productId = drwav_bytes_to_u32(smplHeaderData + 4);
53497 pMetadata->data.smpl.samplePeriodNanoseconds = drwav_bytes_to_u32(smplHeaderData + 8);
53498 pMetadata->data.smpl.midiUnityNote = drwav_bytes_to_u32(smplHeaderData + 12);
53499 pMetadata->data.smpl.midiPitchFraction = drwav_bytes_to_u32(smplHeaderData + 16);
53500 pMetadata->data.smpl.smpteFormat = drwav_bytes_to_u32(smplHeaderData + 20);
53501 pMetadata->data.smpl.smpteOffset = drwav_bytes_to_u32(smplHeaderData + 24);
53502 pMetadata->data.smpl.sampleLoopCount = drwav_bytes_to_u32(smplHeaderData + 28);
53503 pMetadata->data.smpl.samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(smplHeaderData + 32);
53504 pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT);
53505 for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) {
53506 drwav_uint8 smplLoopData[DRWAV_SMPL_LOOP_BYTES];
53507 bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead);
53508 if (bytesJustRead == sizeof(smplLoopData)) {
53509 pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0);
53510 pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4);
53511 pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 8);
53512 pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 12);
53513 pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16);
53514 pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20);
53515 } else {
53516 break;
53517 }
53518 }
53519 if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {
53520 pMetadata->data.smpl.pSamplerSpecificData = drwav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1);
53521 DRWAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL);
53522 bytesJustRead = drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead);
53523 }
53524 }
53525 return totalBytesRead;
53526}
53527DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata)
53528{
53529 drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES];
53530 drwav_uint64 totalBytesRead = 0;
53531 size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead);
53532 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
53533 if (bytesJustRead == sizeof(cueHeaderSectionData)) {
53534 pMetadata->type = drwav_metadata_type_cue;
53535 pMetadata->data.cue.cuePointCount = drwav_bytes_to_u32(cueHeaderSectionData);
53536 pMetadata->data.cue.pCuePoints = (drwav_cue_point*)drwav__metadata_get_memory(pParser, sizeof(drwav_cue_point) * pMetadata->data.cue.cuePointCount, DRWAV_METADATA_ALIGNMENT);
53537 DRWAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL);
53538 if (pMetadata->data.cue.cuePointCount > 0) {
53539 drwav_uint32 iCuePoint;
53540 for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {
53541 drwav_uint8 cuePointData[DRWAV_CUE_POINT_BYTES];
53542 bytesJustRead = drwav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead);
53543 if (bytesJustRead == sizeof(cuePointData)) {
53544 pMetadata->data.cue.pCuePoints[iCuePoint].id = drwav_bytes_to_u32(cuePointData + 0);
53545 pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = drwav_bytes_to_u32(cuePointData + 4);
53546 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8];
53547 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9];
53548 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10];
53549 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11];
53550 pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12);
53551 pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16);
53552 pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = drwav_bytes_to_u32(cuePointData + 20);
53553 } else {
53554 break;
53555 }
53556 }
53557 }
53558 }
53559 return totalBytesRead;
53560}
53561DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata)
53562{
53563 drwav_uint8 instData[DRWAV_INST_BYTES];
53564 drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL);
53565 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
53566 if (bytesRead == sizeof(instData)) {
53567 pMetadata->type = drwav_metadata_type_inst;
53568 pMetadata->data.inst.midiUnityNote = (drwav_int8)instData[0];
53569 pMetadata->data.inst.fineTuneCents = (drwav_int8)instData[1];
53570 pMetadata->data.inst.gainDecibels = (drwav_int8)instData[2];
53571 pMetadata->data.inst.lowNote = (drwav_int8)instData[3];
53572 pMetadata->data.inst.highNote = (drwav_int8)instData[4];
53573 pMetadata->data.inst.lowVelocity = (drwav_int8)instData[5];
53574 pMetadata->data.inst.highVelocity = (drwav_int8)instData[6];
53575 }
53576 return bytesRead;
53577}
53578DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata)
53579{
53580 drwav_uint8 acidData[DRWAV_ACID_BYTES];
53581 drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL);
53582 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
53583 if (bytesRead == sizeof(acidData)) {
53584 pMetadata->type = drwav_metadata_type_acid;
53585 pMetadata->data.acid.flags = drwav_bytes_to_u32(acidData + 0);
53586 pMetadata->data.acid.midiUnityNote = drwav_bytes_to_u16(acidData + 4);
53587 pMetadata->data.acid.reserved1 = drwav_bytes_to_u16(acidData + 6);
53588 pMetadata->data.acid.reserved2 = drwav_bytes_to_f32(acidData + 8);
53589 pMetadata->data.acid.numBeats = drwav_bytes_to_u32(acidData + 12);
53590 pMetadata->data.acid.meterDenominator = drwav_bytes_to_u16(acidData + 16);
53591 pMetadata->data.acid.meterNumerator = drwav_bytes_to_u16(acidData + 18);
53592 pMetadata->data.acid.tempo = drwav_bytes_to_f32(acidData + 20);
53593 }
53594 return bytesRead;
53595}
53596DRWAV_PRIVATE size_t drwav__strlen_clamped(char* str, size_t maxToRead)
53597{
53598 size_t result = 0;
53599 while (*str++ && result < maxToRead) {
53600 result += 1;
53601 }
53602 return result;
53603}
53604DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser, char* str, size_t maxToRead)
53605{
53606 size_t len = drwav__strlen_clamped(str, maxToRead);
53607 if (len) {
53608 char* result = (char*)drwav__metadata_get_memory(pParser, len + 1, 1);
53609 DRWAV_ASSERT(result != NULL);
53610 memcpy(result, str, len);
53611 result[len] = '\0';
53612 return result;
53613 } else {
53614 return NULL;
53615 }
53616}
53617DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize)
53618{
53619 drwav_uint8 bextData[DRWAV_BEXT_BYTES];
53620 drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL);
53621 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
53622 if (bytesRead == sizeof(bextData)) {
53623 drwav_uint8* pReadPointer;
53624 drwav_uint32 timeReferenceLow;
53625 drwav_uint32 timeReferenceHigh;
53626 size_t extraBytes;
53627 pMetadata->type = drwav_metadata_type_bext;
53628 pReadPointer = bextData;
53629 pMetadata->data.bext.pDescription = drwav__metadata_copy_string(pParser, (char*)(pReadPointer), DRWAV_BEXT_DESCRIPTION_BYTES);
53630 pReadPointer += DRWAV_BEXT_DESCRIPTION_BYTES;
53631 pMetadata->data.bext.pOriginatorName = drwav__metadata_copy_string(pParser, (char*)(pReadPointer), DRWAV_BEXT_ORIGINATOR_NAME_BYTES);
53632 pReadPointer += DRWAV_BEXT_ORIGINATOR_NAME_BYTES;
53633 pMetadata->data.bext.pOriginatorReference = drwav__metadata_copy_string(pParser, (char*)(pReadPointer), DRWAV_BEXT_ORIGINATOR_REF_BYTES);
53634 pReadPointer += DRWAV_BEXT_ORIGINATOR_REF_BYTES;
53635 memcpy(pReadPointer, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate));
53636 pReadPointer += sizeof(pMetadata->data.bext.pOriginationDate);
53637 memcpy(pReadPointer, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime));
53638 pReadPointer += sizeof(pMetadata->data.bext.pOriginationTime);
53639 timeReferenceLow = drwav_bytes_to_u32(pReadPointer);
53640 pReadPointer += sizeof(drwav_uint32);
53641 timeReferenceHigh = drwav_bytes_to_u32(pReadPointer);
53642 pReadPointer += sizeof(drwav_uint32);
53643 pMetadata->data.bext.timeReference = ((drwav_uint64)timeReferenceHigh << 32) + timeReferenceLow;
53644 pMetadata->data.bext.version = drwav_bytes_to_u16(pReadPointer);
53645 pReadPointer += sizeof(drwav_uint16);
53646 pMetadata->data.bext.pUMID = drwav__metadata_get_memory(pParser, DRWAV_BEXT_UMID_BYTES, 1);
53647 memcpy(pMetadata->data.bext.pUMID, pReadPointer, DRWAV_BEXT_UMID_BYTES);
53648 pReadPointer += DRWAV_BEXT_UMID_BYTES;
53649 pMetadata->data.bext.loudnessValue = drwav_bytes_to_u16(pReadPointer);
53650 pReadPointer += sizeof(drwav_uint16);
53651 pMetadata->data.bext.loudnessRange = drwav_bytes_to_u16(pReadPointer);
53652 pReadPointer += sizeof(drwav_uint16);
53653 pMetadata->data.bext.maxTruePeakLevel = drwav_bytes_to_u16(pReadPointer);
53654 pReadPointer += sizeof(drwav_uint16);
53655 pMetadata->data.bext.maxMomentaryLoudness = drwav_bytes_to_u16(pReadPointer);
53656 pReadPointer += sizeof(drwav_uint16);
53657 pMetadata->data.bext.maxShortTermLoudness = drwav_bytes_to_u16(pReadPointer);
53658 pReadPointer += sizeof(drwav_uint16);
53659 DRWAV_ASSERT((pReadPointer + DRWAV_BEXT_RESERVED_BYTES) == (bextData + DRWAV_BEXT_BYTES));
53660 extraBytes = (size_t)(chunkSize - DRWAV_BEXT_BYTES);
53661 if (extraBytes > 0) {
53662 pMetadata->data.bext.pCodingHistory = (char*)drwav__metadata_get_memory(pParser, extraBytes + 1, 1);
53663 DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL);
53664 bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL);
53665 pMetadata->data.bext.codingHistorySize = (drwav_uint32)strlen(pMetadata->data.bext.pCodingHistory);
53666 } else {
53667 pMetadata->data.bext.pCodingHistory = NULL;
53668 pMetadata->data.bext.codingHistorySize = 0;
53669 }
53670 }
53671 return bytesRead;
53672}
53673DRWAV_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)
53674{
53675 drwav_uint8 cueIDBuffer[DRWAV_LIST_LABEL_OR_NOTE_BYTES];
53676 drwav_uint64 totalBytesRead = 0;
53677 size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead);
53678 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
53679 if (bytesJustRead == sizeof(cueIDBuffer)) {
53680 drwav_uint32 sizeIncludingNullTerminator;
53681 pMetadata->type = type;
53682 pMetadata->data.labelOrNote.cuePointId = drwav_bytes_to_u32(cueIDBuffer);
53683 sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES;
53684 if (sizeIncludingNullTerminator > 0) {
53685 pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1;
53686 pMetadata->data.labelOrNote.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);
53687 DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);
53688 bytesJustRead = drwav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead);
53689 } else {
53690 pMetadata->data.labelOrNote.stringLength = 0;
53691 pMetadata->data.labelOrNote.pString = NULL;
53692 }
53693 }
53694 return totalBytesRead;
53695}
53696DRWAV_PRIVATE drwav_uint64 drwav__read_list_labelled_cue_region_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize)
53697{
53698 drwav_uint8 buffer[DRWAV_LIST_LABELLED_TEXT_BYTES];
53699 drwav_uint64 totalBytesRead = 0;
53700 size_t bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead);
53701 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
53702 if (bytesJustRead == sizeof(buffer)) {
53703 drwav_uint32 sizeIncludingNullTerminator;
53704 pMetadata->type = drwav_metadata_type_list_labelled_cue_region;
53705 pMetadata->data.labelledCueRegion.cuePointId = drwav_bytes_to_u32(buffer + 0);
53706 pMetadata->data.labelledCueRegion.sampleLength = drwav_bytes_to_u32(buffer + 4);
53707 pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8];
53708 pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9];
53709 pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10];
53710 pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11];
53711 pMetadata->data.labelledCueRegion.country = drwav_bytes_to_u16(buffer + 12);
53712 pMetadata->data.labelledCueRegion.language = drwav_bytes_to_u16(buffer + 14);
53713 pMetadata->data.labelledCueRegion.dialect = drwav_bytes_to_u16(buffer + 16);
53714 pMetadata->data.labelledCueRegion.codePage = drwav_bytes_to_u16(buffer + 18);
53715 sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABELLED_TEXT_BYTES;
53716 if (sizeIncludingNullTerminator > 0) {
53717 pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1;
53718 pMetadata->data.labelledCueRegion.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);
53719 DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);
53720 bytesJustRead = drwav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead);
53721 } else {
53722 pMetadata->data.labelledCueRegion.stringLength = 0;
53723 pMetadata->data.labelledCueRegion.pString = NULL;
53724 }
53725 }
53726 return totalBytesRead;
53727}
53728DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_info_text_chunk(drwav__metadata_parser* pParser, drwav_uint64 chunkSize, drwav_metadata_type type)
53729{
53730 drwav_uint64 bytesRead = 0;
53731 drwav_uint32 stringSizeWithNullTerminator = (drwav_uint32)chunkSize;
53732 if (pParser->stage == drwav__metadata_parser_stage_count) {
53733 pParser->metadataCount += 1;
53734 drwav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1);
53735 } else {
53736 drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];
53737 pMetadata->type = type;
53738 if (stringSizeWithNullTerminator > 0) {
53739 pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1;
53740 pMetadata->data.infoText.pString = (char*)drwav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1);
53741 DRWAV_ASSERT(pMetadata->data.infoText.pString != NULL);
53742 bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL);
53743 if (bytesRead == chunkSize) {
53744 pParser->metadataCursor += 1;
53745 } else {
53746 }
53747 } else {
53748 pMetadata->data.infoText.stringLength = 0;
53749 pMetadata->data.infoText.pString = NULL;
53750 pParser->metadataCursor += 1;
53751 }
53752 }
53753 return bytesRead;
53754}
53755DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_unknown_chunk(drwav__metadata_parser* pParser, const drwav_uint8* pChunkId, drwav_uint64 chunkSize, drwav_metadata_location location)
53756{
53757 drwav_uint64 bytesRead = 0;
53758 if (location == drwav_metadata_location_invalid) {
53759 return 0;
53760 }
53761 if (drwav_fourcc_equal(pChunkId, "data") || drwav_fourcc_equal(pChunkId, "fmt") || drwav_fourcc_equal(pChunkId, "fact")) {
53762 return 0;
53763 }
53764 if (pParser->stage == drwav__metadata_parser_stage_count) {
53765 pParser->metadataCount += 1;
53766 drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1);
53767 } else {
53768 drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];
53769 pMetadata->type = drwav_metadata_type_unknown;
53770 pMetadata->data.unknown.chunkLocation = location;
53771 pMetadata->data.unknown.id[0] = pChunkId[0];
53772 pMetadata->data.unknown.id[1] = pChunkId[1];
53773 pMetadata->data.unknown.id[2] = pChunkId[2];
53774 pMetadata->data.unknown.id[3] = pChunkId[3];
53775 pMetadata->data.unknown.dataSizeInBytes = (drwav_uint32)chunkSize;
53776 pMetadata->data.unknown.pData = (drwav_uint8 *)drwav__metadata_get_memory(pParser, (size_t)chunkSize, 1);
53777 DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL);
53778 bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL);
53779 if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) {
53780 pParser->metadataCursor += 1;
53781 } else {
53782 }
53783 }
53784 return bytesRead;
53785}
53786DRWAV_PRIVATE drwav_bool32 drwav__chunk_matches(drwav_uint64 allowedMetadataTypes, const drwav_uint8* pChunkID, drwav_metadata_type type, const char* pID)
53787{
53788 return (allowedMetadataTypes & type) && drwav_fourcc_equal(pChunkID, pID);
53789}
53790DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_uint64 allowedMetadataTypes)
53791{
53792 const drwav_uint8 *pChunkID = pChunkHeader->id.fourcc;
53793 drwav_uint64 bytesRead = 0;
53794 if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_smpl, "smpl")) {
53795 if (pChunkHeader->sizeInBytes >= DRWAV_SMPL_BYTES) {
53796 if (pParser->stage == drwav__metadata_parser_stage_count) {
53797 drwav_uint8 buffer[4];
53798 size_t bytesJustRead;
53799 if (!pParser->onSeek(pParser->pReadSeekUserData, 28, drwav_seek_origin_current)) {
53800 return bytesRead;
53801 }
53802 bytesRead += 28;
53803 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);
53804 if (bytesJustRead == sizeof(buffer)) {
53805 drwav_uint32 loopCount = drwav_bytes_to_u32(buffer);
53806 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);
53807 if (bytesJustRead == sizeof(buffer)) {
53808 drwav_uint32 samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(buffer);
53809 pParser->metadataCount += 1;
53810 drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_smpl_loop) * loopCount, DRWAV_METADATA_ALIGNMENT);
53811 drwav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1);
53812 }
53813 }
53814 } else {
53815 bytesRead = drwav__read_smpl_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
53816 if (bytesRead == pChunkHeader->sizeInBytes) {
53817 pParser->metadataCursor += 1;
53818 } else {
53819 }
53820 }
53821 } else {
53822 }
53823 } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_inst, "inst")) {
53824 if (pChunkHeader->sizeInBytes == DRWAV_INST_BYTES) {
53825 if (pParser->stage == drwav__metadata_parser_stage_count) {
53826 pParser->metadataCount += 1;
53827 } else {
53828 bytesRead = drwav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
53829 if (bytesRead == pChunkHeader->sizeInBytes) {
53830 pParser->metadataCursor += 1;
53831 } else {
53832 }
53833 }
53834 } else {
53835 }
53836 } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_acid, "acid")) {
53837 if (pChunkHeader->sizeInBytes == DRWAV_ACID_BYTES) {
53838 if (pParser->stage == drwav__metadata_parser_stage_count) {
53839 pParser->metadataCount += 1;
53840 } else {
53841 bytesRead = drwav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
53842 if (bytesRead == pChunkHeader->sizeInBytes) {
53843 pParser->metadataCursor += 1;
53844 } else {
53845 }
53846 }
53847 } else {
53848 }
53849 } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_cue, "cue ")) {
53850 if (pChunkHeader->sizeInBytes >= DRWAV_CUE_BYTES) {
53851 if (pParser->stage == drwav__metadata_parser_stage_count) {
53852 size_t cueCount;
53853 pParser->metadataCount += 1;
53854 cueCount = (size_t)(pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES;
53855 drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_cue_point) * cueCount, DRWAV_METADATA_ALIGNMENT);
53856 } else {
53857 bytesRead = drwav__read_cue_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
53858 if (bytesRead == pChunkHeader->sizeInBytes) {
53859 pParser->metadataCursor += 1;
53860 } else {
53861 }
53862 }
53863 } else {
53864 }
53865 } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_bext, "bext")) {
53866 if (pChunkHeader->sizeInBytes >= DRWAV_BEXT_BYTES) {
53867 if (pParser->stage == drwav__metadata_parser_stage_count) {
53868 char buffer[DRWAV_BEXT_DESCRIPTION_BYTES + 1];
53869 size_t allocSizeNeeded = DRWAV_BEXT_UMID_BYTES;
53870 size_t bytesJustRead;
53871 buffer[DRWAV_BEXT_DESCRIPTION_BYTES] = '\0';
53872 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_DESCRIPTION_BYTES, &bytesRead);
53873 if (bytesJustRead != DRWAV_BEXT_DESCRIPTION_BYTES) {
53874 return bytesRead;
53875 }
53876 allocSizeNeeded += strlen(buffer) + 1;
53877 buffer[DRWAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0';
53878 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead);
53879 if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_NAME_BYTES) {
53880 return bytesRead;
53881 }
53882 allocSizeNeeded += strlen(buffer) + 1;
53883 buffer[DRWAV_BEXT_ORIGINATOR_REF_BYTES] = '\0';
53884 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead);
53885 if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_REF_BYTES) {
53886 return bytesRead;
53887 }
53888 allocSizeNeeded += strlen(buffer) + 1;
53889 allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES;
53890 drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1);
53891 pParser->metadataCount += 1;
53892 } else {
53893 bytesRead = drwav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes);
53894 if (bytesRead == pChunkHeader->sizeInBytes) {
53895 pParser->metadataCursor += 1;
53896 } else {
53897 }
53898 }
53899 } else {
53900 }
53901 } else if (drwav_fourcc_equal(pChunkID, "LIST") || drwav_fourcc_equal(pChunkID, "list")) {
53902 drwav_metadata_location listType = drwav_metadata_location_invalid;
53903 while (bytesRead < pChunkHeader->sizeInBytes) {
53904 drwav_uint8 subchunkId[4];
53905 drwav_uint8 subchunkSizeBuffer[4];
53906 drwav_uint64 subchunkDataSize;
53907 drwav_uint64 subchunkBytesRead = 0;
53908 drwav_uint64 bytesJustRead = drwav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead);
53909 if (bytesJustRead != sizeof(subchunkId)) {
53910 break;
53911 }
53912 if (drwav_fourcc_equal(subchunkId, "adtl")) {
53913 listType = drwav_metadata_location_inside_adtl_list;
53914 continue;
53915 } else if (drwav_fourcc_equal(subchunkId, "INFO")) {
53916 listType = drwav_metadata_location_inside_info_list;
53917 continue;
53918 }
53919 bytesJustRead = drwav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead);
53920 if (bytesJustRead != sizeof(subchunkSizeBuffer)) {
53921 break;
53922 }
53923 subchunkDataSize = drwav_bytes_to_u32(subchunkSizeBuffer);
53924 if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_label, "labl") || drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_note, "note")) {
53925 if (subchunkDataSize >= DRWAV_LIST_LABEL_OR_NOTE_BYTES) {
53926 drwav_uint64 stringSizeWithNullTerm = subchunkDataSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES;
53927 if (pParser->stage == drwav__metadata_parser_stage_count) {
53928 pParser->metadataCount += 1;
53929 drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1);
53930 } else {
53931 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);
53932 if (subchunkBytesRead == subchunkDataSize) {
53933 pParser->metadataCursor += 1;
53934 } else {
53935 }
53936 }
53937 } else {
53938 }
53939 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_labelled_cue_region, "ltxt")) {
53940 if (subchunkDataSize >= DRWAV_LIST_LABELLED_TEXT_BYTES) {
53941 drwav_uint64 stringSizeWithNullTerminator = subchunkDataSize - DRWAV_LIST_LABELLED_TEXT_BYTES;
53942 if (pParser->stage == drwav__metadata_parser_stage_count) {
53943 pParser->metadataCount += 1;
53944 drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1);
53945 } else {
53946 subchunkBytesRead = drwav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize);
53947 if (subchunkBytesRead == subchunkDataSize) {
53948 pParser->metadataCursor += 1;
53949 } else {
53950 }
53951 }
53952 } else {
53953 }
53954 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_software, "ISFT")) {
53955 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_software);
53956 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_copyright, "ICOP")) {
53957 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_copyright);
53958 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_title, "INAM")) {
53959 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_title);
53960 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_artist, "IART")) {
53961 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_artist);
53962 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_comment, "ICMT")) {
53963 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_comment);
53964 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_date, "ICRD")) {
53965 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_date);
53966 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_genre, "IGNR")) {
53967 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_genre);
53968 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_album, "IPRD")) {
53969 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_album);
53970 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_tracknumber, "ITRK")) {
53971 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_tracknumber);
53972 } else if (allowedMetadataTypes & drwav_metadata_type_unknown) {
53973 subchunkBytesRead = drwav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType);
53974 }
53975 bytesRead += subchunkBytesRead;
53976 DRWAV_ASSERT(subchunkBytesRead <= subchunkDataSize);
53977 if (subchunkBytesRead < subchunkDataSize) {
53978 drwav_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead;
53979 if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, drwav_seek_origin_current)) {
53980 break;
53981 }
53982 bytesRead += bytesToSeek;
53983 }
53984 if ((subchunkDataSize % 2) == 1) {
53985 if (!pParser->onSeek(pParser->pReadSeekUserData, 1, drwav_seek_origin_current)) {
53986 break;
53987 }
53988 bytesRead += 1;
53989 }
53990 }
53991 } else if (allowedMetadataTypes & drwav_metadata_type_unknown) {
53992 bytesRead = drwav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, drwav_metadata_location_top_level);
53993 }
53994 return bytesRead;
53995}
53996DRWAV_PRIVATE drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav)
53997{
53998 if ((pWav->bitsPerSample & 0x7) == 0) {
53999 return (pWav->bitsPerSample * pWav->fmt.channels) >> 3;
54000 } else {
54001 return pWav->fmt.blockAlign;
54002 }
54003}
54004DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT)
54005{
54006 if (pFMT == NULL) {
54007 return 0;
54008 }
54009 if (pFMT->formatTag != DR_WAVE_FORMAT_EXTENSIBLE) {
54010 return pFMT->formatTag;
54011 } else {
54012 return drwav_bytes_to_u16(pFMT->subFormat);
54013 }
54014}
54015DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
54016{
54017 if (pWav == NULL || onRead == NULL || onSeek == NULL) {
54018 return DRWAV_FALSE;
54019 }
54020 DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));
54021 pWav->onRead = onRead;
54022 pWav->onSeek = onSeek;
54023 pWav->pUserData = pReadSeekUserData;
54024 pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
54025 if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
54026 return DRWAV_FALSE;
54027 }
54028 return DRWAV_TRUE;
54029}
54030DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags)
54031{
54032 drwav_uint64 cursor;
54033 drwav_bool32 sequential;
54034 drwav_uint8 riff[4];
54035 drwav_fmt fmt;
54036 unsigned short translatedFormatTag;
54037 drwav_bool32 foundDataChunk;
54038 drwav_uint64 dataChunkSize = 0;
54039 drwav_uint64 sampleCountFromFactChunk = 0;
54040 drwav_uint64 chunkSize;
54041 drwav__metadata_parser metadataParser;
54042 cursor = 0;
54043 sequential = (flags & DRWAV_SEQUENTIAL) != 0;
54044 if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) {
54045 return DRWAV_FALSE;
54046 }
54047 if (drwav_fourcc_equal(riff, "RIFF")) {
54048 pWav->container = drwav_container_riff;
54049 } else if (drwav_fourcc_equal(riff, "riff")) {
54050 int i;
54051 drwav_uint8 riff2[12];
54052 pWav->container = drwav_container_w64;
54053 if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) {
54054 return DRWAV_FALSE;
54055 }
54056 for (i = 0; i < 12; ++i) {
54057 if (riff2[i] != drwavGUID_W64_RIFF[i+4]) {
54058 return DRWAV_FALSE;
54059 }
54060 }
54061 } else if (drwav_fourcc_equal(riff, "RF64")) {
54062 pWav->container = drwav_container_rf64;
54063 } else {
54064 return DRWAV_FALSE;
54065 }
54066 if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) {
54067 drwav_uint8 chunkSizeBytes[4];
54068 drwav_uint8 wave[4];
54069 if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
54070 return DRWAV_FALSE;
54071 }
54072 if (pWav->container == drwav_container_riff) {
54073 if (drwav_bytes_to_u32(chunkSizeBytes) < 36) {
54074 return DRWAV_FALSE;
54075 }
54076 } else {
54077 if (drwav_bytes_to_u32(chunkSizeBytes) != 0xFFFFFFFF) {
54078 return DRWAV_FALSE;
54079 }
54080 }
54081 if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
54082 return DRWAV_FALSE;
54083 }
54084 if (!drwav_fourcc_equal(wave, "WAVE")) {
54085 return DRWAV_FALSE;
54086 }
54087 } else {
54088 drwav_uint8 chunkSizeBytes[8];
54089 drwav_uint8 wave[16];
54090 if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
54091 return DRWAV_FALSE;
54092 }
54093 if (drwav_bytes_to_u64(chunkSizeBytes) < 80) {
54094 return DRWAV_FALSE;
54095 }
54096 if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
54097 return DRWAV_FALSE;
54098 }
54099 if (!drwav_guid_equal(wave, drwavGUID_W64_WAVE)) {
54100 return DRWAV_FALSE;
54101 }
54102 }
54103 if (pWav->container == drwav_container_rf64) {
54104 drwav_uint8 sizeBytes[8];
54105 drwav_uint64 bytesRemainingInChunk;
54106 drwav_chunk_header header;
54107 drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
54108 if (result != DRWAV_SUCCESS) {
54109 return DRWAV_FALSE;
54110 }
54111 if (!drwav_fourcc_equal(header.id.fourcc, "ds64")) {
54112 return DRWAV_FALSE;
54113 }
54114 bytesRemainingInChunk = header.sizeInBytes + header.paddingSize;
54115 if (!drwav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) {
54116 return DRWAV_FALSE;
54117 }
54118 bytesRemainingInChunk -= 8;
54119 cursor += 8;
54120 if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
54121 return DRWAV_FALSE;
54122 }
54123 bytesRemainingInChunk -= 8;
54124 dataChunkSize = drwav_bytes_to_u64(sizeBytes);
54125 if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
54126 return DRWAV_FALSE;
54127 }
54128 bytesRemainingInChunk -= 8;
54129 sampleCountFromFactChunk = drwav_bytes_to_u64(sizeBytes);
54130 if (!drwav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) {
54131 return DRWAV_FALSE;
54132 }
54133 cursor += bytesRemainingInChunk;
54134 }
54135 if (!drwav__read_fmt(pWav->onRead, pWav->onSeek, pWav->pUserData, pWav->container, &cursor, &fmt)) {
54136 return DRWAV_FALSE;
54137 }
54138 if ((fmt.sampleRate == 0 || fmt.sampleRate > DRWAV_MAX_SAMPLE_RATE) ||
54139 (fmt.channels == 0 || fmt.channels > DRWAV_MAX_CHANNELS) ||
54140 (fmt.bitsPerSample == 0 || fmt.bitsPerSample > DRWAV_MAX_BITS_PER_SAMPLE) ||
54141 fmt.blockAlign == 0) {
54142 return DRWAV_FALSE;
54143 }
54144 translatedFormatTag = fmt.formatTag;
54145 if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
54146 translatedFormatTag = drwav_bytes_to_u16(fmt.subFormat + 0);
54147 }
54148 memset(&metadataParser, 0, sizeof(metadataParser));
54149 if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) {
54150 drwav_uint64 cursorForMetadata = cursor;
54151 metadataParser.onRead = pWav->onRead;
54152 metadataParser.onSeek = pWav->onSeek;
54153 metadataParser.pReadSeekUserData = pWav->pUserData;
54154 metadataParser.stage = drwav__metadata_parser_stage_count;
54155 for (;;) {
54156 drwav_result result;
54157 drwav_uint64 bytesRead;
54158 drwav_uint64 remainingBytes;
54159 drwav_chunk_header header;
54160 result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursorForMetadata, &header);
54161 if (result != DRWAV_SUCCESS) {
54162 break;
54163 }
54164 bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes);
54165 DRWAV_ASSERT(bytesRead <= header.sizeInBytes);
54166 remainingBytes = header.sizeInBytes - bytesRead + header.paddingSize;
54167 if (!drwav__seek_forward(pWav->onSeek, remainingBytes, pWav->pUserData)) {
54168 break;
54169 }
54170 cursorForMetadata += remainingBytes;
54171 }
54172 if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) {
54173 return DRWAV_FALSE;
54174 }
54175 drwav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks);
54176 metadataParser.stage = drwav__metadata_parser_stage_read;
54177 }
54178 foundDataChunk = DRWAV_FALSE;
54179 for (;;) {
54180 drwav_chunk_header header;
54181 drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
54182 if (result != DRWAV_SUCCESS) {
54183 if (!foundDataChunk) {
54184 return DRWAV_FALSE;
54185 } else {
54186 break;
54187 }
54188 }
54189 if (!sequential && onChunk != NULL) {
54190 drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt);
54191 if (callbackBytesRead > 0) {
54192 if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) {
54193 return DRWAV_FALSE;
54194 }
54195 }
54196 }
54197 if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) {
54198 drwav_uint64 bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes);
54199 if (bytesRead > 0) {
54200 if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) {
54201 return DRWAV_FALSE;
54202 }
54203 }
54204 }
54205 if (!foundDataChunk) {
54206 pWav->dataChunkDataPos = cursor;
54207 }
54208 chunkSize = header.sizeInBytes;
54209 if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) {
54210 if (drwav_fourcc_equal(header.id.fourcc, "data")) {
54211 foundDataChunk = DRWAV_TRUE;
54212 if (pWav->container != drwav_container_rf64) {
54213 dataChunkSize = chunkSize;
54214 }
54215 }
54216 } else {
54217 if (drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA)) {
54218 foundDataChunk = DRWAV_TRUE;
54219 dataChunkSize = chunkSize;
54220 }
54221 }
54222 if (foundDataChunk && sequential) {
54223 break;
54224 }
54225 if (pWav->container == drwav_container_riff) {
54226 if (drwav_fourcc_equal(header.id.fourcc, "fact")) {
54227 drwav_uint32 sampleCount;
54228 if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) {
54229 return DRWAV_FALSE;
54230 }
54231 chunkSize -= 4;
54232 if (!foundDataChunk) {
54233 pWav->dataChunkDataPos = cursor;
54234 }
54235 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
54236 sampleCountFromFactChunk = sampleCount;
54237 } else {
54238 sampleCountFromFactChunk = 0;
54239 }
54240 }
54241 } else if (pWav->container == drwav_container_w64) {
54242 if (drwav_guid_equal(header.id.guid, drwavGUID_W64_FACT)) {
54243 if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) {
54244 return DRWAV_FALSE;
54245 }
54246 chunkSize -= 8;
54247 if (!foundDataChunk) {
54248 pWav->dataChunkDataPos = cursor;
54249 }
54250 }
54251 } else if (pWav->container == drwav_container_rf64) {
54252 }
54253 chunkSize += header.paddingSize;
54254 if (!drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData)) {
54255 break;
54256 }
54257 cursor += chunkSize;
54258 if (!foundDataChunk) {
54259 pWav->dataChunkDataPos = cursor;
54260 }
54261 }
54262 pWav->pMetadata = metadataParser.pMetadata;
54263 pWav->metadataCount = metadataParser.metadataCount;
54264 if (!foundDataChunk) {
54265 return DRWAV_FALSE;
54266 }
54267 if (!sequential) {
54268 if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) {
54269 return DRWAV_FALSE;
54270 }
54271 cursor = pWav->dataChunkDataPos;
54272 }
54273 pWav->fmt = fmt;
54274 pWav->sampleRate = fmt.sampleRate;
54275 pWav->channels = fmt.channels;
54276 pWav->bitsPerSample = fmt.bitsPerSample;
54277 pWav->bytesRemaining = dataChunkSize;
54278 pWav->translatedFormatTag = translatedFormatTag;
54279 pWav->dataChunkDataSize = dataChunkSize;
54280 if (sampleCountFromFactChunk != 0) {
54281 pWav->totalPCMFrameCount = sampleCountFromFactChunk;
54282 } else {
54283 pWav->totalPCMFrameCount = dataChunkSize / drwav_get_bytes_per_pcm_frame(pWav);
54284 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
54285 drwav_uint64 totalBlockHeaderSizeInBytes;
54286 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
54287 if ((blockCount * fmt.blockAlign) < dataChunkSize) {
54288 blockCount += 1;
54289 }
54290 totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels);
54291 pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
54292 }
54293 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
54294 drwav_uint64 totalBlockHeaderSizeInBytes;
54295 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
54296 if ((blockCount * fmt.blockAlign) < dataChunkSize) {
54297 blockCount += 1;
54298 }
54299 totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels);
54300 pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
54301 pWav->totalPCMFrameCount += blockCount;
54302 }
54303 }
54304 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
54305 if (pWav->channels > 2) {
54306 return DRWAV_FALSE;
54307 }
54308 }
54309#ifdef DR_WAV_LIBSNDFILE_COMPAT
54310 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
54311 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
54312 pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels;
54313 }
54314 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
54315 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
54316 pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels;
54317 }
54318#endif
54319 return DRWAV_TRUE;
54320}
54321DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
54322{
54323 return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks);
54324}
54325DRWAV_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)
54326{
54327 if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) {
54328 return DRWAV_FALSE;
54329 }
54330 return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
54331}
54332DRWAV_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)
54333{
54334 if (!drwav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
54335 return DRWAV_FALSE;
54336 }
54337 pWav->allowedMetadataTypes = drwav_metadata_type_all_including_unknown;
54338 return drwav_init__internal(pWav, NULL, NULL, flags);
54339}
54340DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav)
54341{
54342 drwav_metadata *result = pWav->pMetadata;
54343 pWav->pMetadata = NULL;
54344 pWav->metadataCount = 0;
54345 return result;
54346}
54347DRWAV_PRIVATE size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize)
54348{
54349 DRWAV_ASSERT(pWav != NULL);
54350 DRWAV_ASSERT(pWav->onWrite != NULL);
54351 return pWav->onWrite(pWav->pUserData, pData, dataSize);
54352}
54353DRWAV_PRIVATE size_t drwav__write_byte(drwav* pWav, drwav_uint8 byte)
54354{
54355 DRWAV_ASSERT(pWav != NULL);
54356 DRWAV_ASSERT(pWav->onWrite != NULL);
54357 return pWav->onWrite(pWav->pUserData, &byte, 1);
54358}
54359DRWAV_PRIVATE size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value)
54360{
54361 DRWAV_ASSERT(pWav != NULL);
54362 DRWAV_ASSERT(pWav->onWrite != NULL);
54363 if (!drwav__is_little_endian()) {
54364 value = drwav__bswap16(value);
54365 }
54366 return drwav__write(pWav, &value, 2);
54367}
54368DRWAV_PRIVATE size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value)
54369{
54370 DRWAV_ASSERT(pWav != NULL);
54371 DRWAV_ASSERT(pWav->onWrite != NULL);
54372 if (!drwav__is_little_endian()) {
54373 value = drwav__bswap32(value);
54374 }
54375 return drwav__write(pWav, &value, 4);
54376}
54377DRWAV_PRIVATE size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value)
54378{
54379 DRWAV_ASSERT(pWav != NULL);
54380 DRWAV_ASSERT(pWav->onWrite != NULL);
54381 if (!drwav__is_little_endian()) {
54382 value = drwav__bswap64(value);
54383 }
54384 return drwav__write(pWav, &value, 8);
54385}
54386DRWAV_PRIVATE size_t drwav__write_f32ne_to_le(drwav* pWav, float value)
54387{
54388 union {
54389 drwav_uint32 u32;
54390 float f32;
54391 } u;
54392 DRWAV_ASSERT(pWav != NULL);
54393 DRWAV_ASSERT(pWav->onWrite != NULL);
54394 u.f32 = value;
54395 if (!drwav__is_little_endian()) {
54396 u.u32 = drwav__bswap32(u.u32);
54397 }
54398 return drwav__write(pWav, &u.u32, 4);
54399}
54400DRWAV_PRIVATE size_t drwav__write_or_count(drwav* pWav, const void* pData, size_t dataSize)
54401{
54402 if (pWav == NULL) {
54403 return dataSize;
54404 }
54405 return drwav__write(pWav, pData, dataSize);
54406}
54407DRWAV_PRIVATE size_t drwav__write_or_count_byte(drwav* pWav, drwav_uint8 byte)
54408{
54409 if (pWav == NULL) {
54410 return 1;
54411 }
54412 return drwav__write_byte(pWav, byte);
54413}
54414DRWAV_PRIVATE size_t drwav__write_or_count_u16ne_to_le(drwav* pWav, drwav_uint16 value)
54415{
54416 if (pWav == NULL) {
54417 return 2;
54418 }
54419 return drwav__write_u16ne_to_le(pWav, value);
54420}
54421DRWAV_PRIVATE size_t drwav__write_or_count_u32ne_to_le(drwav* pWav, drwav_uint32 value)
54422{
54423 if (pWav == NULL) {
54424 return 4;
54425 }
54426 return drwav__write_u32ne_to_le(pWav, value);
54427}
54428#if 0
54429DRWAV_PRIVATE size_t drwav__write_or_count_u64ne_to_le(drwav* pWav, drwav_uint64 value)
54430{
54431 if (pWav == NULL) {
54432 return 8;
54433 }
54434 return drwav__write_u64ne_to_le(pWav, value);
54435}
54436#endif
54437DRWAV_PRIVATE size_t drwav__write_or_count_f32ne_to_le(drwav* pWav, float value)
54438{
54439 if (pWav == NULL) {
54440 return 4;
54441 }
54442 return drwav__write_f32ne_to_le(pWav, value);
54443}
54444DRWAV_PRIVATE size_t drwav__write_or_count_string_to_fixed_size_buf(drwav* pWav, char* str, size_t bufFixedSize)
54445{
54446 size_t len;
54447 if (pWav == NULL) {
54448 return bufFixedSize;
54449 }
54450 len = drwav__strlen_clamped(str, bufFixedSize);
54451 drwav__write_or_count(pWav, str, len);
54452 if (len < bufFixedSize) {
54453 size_t i;
54454 for (i = 0; i < bufFixedSize - len; ++i) {
54455 drwav__write_byte(pWav, 0);
54456 }
54457 }
54458 return bufFixedSize;
54459}
54460DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* pMetadatas, drwav_uint32 metadataCount)
54461{
54462 size_t bytesWritten = 0;
54463 drwav_bool32 hasListAdtl = DRWAV_FALSE;
54464 drwav_bool32 hasListInfo = DRWAV_FALSE;
54465 drwav_uint32 iMetadata;
54466 if (pMetadatas == NULL || metadataCount == 0) {
54467 return 0;
54468 }
54469 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
54470 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
54471 drwav_uint32 chunkSize = 0;
54472 if ((pMetadata->type & drwav_metadata_type_list_all_info_strings) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list)) {
54473 hasListInfo = DRWAV_TRUE;
54474 }
54475 if ((pMetadata->type & drwav_metadata_type_list_all_adtl) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list)) {
54476 hasListAdtl = DRWAV_TRUE;
54477 }
54478 switch (pMetadata->type) {
54479 case drwav_metadata_type_smpl:
54480 {
54481 drwav_uint32 iLoop;
54482 chunkSize = DRWAV_SMPL_BYTES + DRWAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes;
54483 bytesWritten += drwav__write_or_count(pWav, "smpl", 4);
54484 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
54485 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId);
54486 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId);
54487 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds);
54488 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote);
54489 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction);
54490 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat);
54491 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset);
54492 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount);
54493 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);
54494 for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) {
54495 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId);
54496 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type);
54497 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset);
54498 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset);
54499 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction);
54500 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount);
54501 }
54502 if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {
54503 bytesWritten += drwav__write(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);
54504 }
54505 } break;
54506 case drwav_metadata_type_inst:
54507 {
54508 chunkSize = DRWAV_INST_BYTES;
54509 bytesWritten += drwav__write_or_count(pWav, "inst", 4);
54510 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
54511 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1);
54512 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1);
54513 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1);
54514 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1);
54515 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1);
54516 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1);
54517 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1);
54518 } break;
54519 case drwav_metadata_type_cue:
54520 {
54521 drwav_uint32 iCuePoint;
54522 chunkSize = DRWAV_CUE_BYTES + DRWAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount;
54523 bytesWritten += drwav__write_or_count(pWav, "cue ", 4);
54524 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
54525 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount);
54526 for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {
54527 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id);
54528 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition);
54529 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4);
54530 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart);
54531 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart);
54532 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset);
54533 }
54534 } break;
54535 case drwav_metadata_type_acid:
54536 {
54537 chunkSize = DRWAV_ACID_BYTES;
54538 bytesWritten += drwav__write_or_count(pWav, "acid", 4);
54539 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
54540 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags);
54541 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote);
54542 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1);
54543 bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2);
54544 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats);
54545 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator);
54546 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator);
54547 bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo);
54548 } break;
54549 case drwav_metadata_type_bext:
54550 {
54551 char reservedBuf[DRWAV_BEXT_RESERVED_BYTES];
54552 drwav_uint32 timeReferenceLow;
54553 drwav_uint32 timeReferenceHigh;
54554 chunkSize = DRWAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize;
54555 bytesWritten += drwav__write_or_count(pWav, "bext", 4);
54556 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
54557 bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, DRWAV_BEXT_DESCRIPTION_BYTES);
54558 bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, DRWAV_BEXT_ORIGINATOR_NAME_BYTES);
54559 bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, DRWAV_BEXT_ORIGINATOR_REF_BYTES);
54560 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate));
54561 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime));
54562 timeReferenceLow = (drwav_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF);
54563 timeReferenceHigh = (drwav_uint32)(pMetadata->data.bext.timeReference >> 32);
54564 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceLow);
54565 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh);
54566 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version);
54567 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES);
54568 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue);
54569 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange);
54570 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel);
54571 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness);
54572 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness);
54573 memset(reservedBuf, 0, sizeof(reservedBuf));
54574 bytesWritten += drwav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf));
54575 if (pMetadata->data.bext.codingHistorySize > 0) {
54576 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize);
54577 }
54578 } break;
54579 case drwav_metadata_type_unknown:
54580 {
54581 if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_top_level) {
54582 chunkSize = pMetadata->data.unknown.dataSizeInBytes;
54583 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
54584 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
54585 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes);
54586 }
54587 } break;
54588 default: break;
54589 }
54590 if ((chunkSize % 2) != 0) {
54591 bytesWritten += drwav__write_or_count_byte(pWav, 0);
54592 }
54593 }
54594 if (hasListInfo) {
54595 drwav_uint32 chunkSize = 4;
54596 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
54597 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
54598 if ((pMetadata->type & drwav_metadata_type_list_all_info_strings)) {
54599 chunkSize += 8;
54600 chunkSize += pMetadata->data.infoText.stringLength + 1;
54601 } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) {
54602 chunkSize += 8;
54603 chunkSize += pMetadata->data.unknown.dataSizeInBytes;
54604 }
54605 if ((chunkSize % 2) != 0) {
54606 chunkSize += 1;
54607 }
54608 }
54609 bytesWritten += drwav__write_or_count(pWav, "LIST", 4);
54610 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
54611 bytesWritten += drwav__write_or_count(pWav, "INFO", 4);
54612 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
54613 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
54614 drwav_uint32 subchunkSize = 0;
54615 if (pMetadata->type & drwav_metadata_type_list_all_info_strings) {
54616 const char* pID = NULL;
54617 switch (pMetadata->type) {
54618 case drwav_metadata_type_list_info_software: pID = "ISFT"; break;
54619 case drwav_metadata_type_list_info_copyright: pID = "ICOP"; break;
54620 case drwav_metadata_type_list_info_title: pID = "INAM"; break;
54621 case drwav_metadata_type_list_info_artist: pID = "IART"; break;
54622 case drwav_metadata_type_list_info_comment: pID = "ICMT"; break;
54623 case drwav_metadata_type_list_info_date: pID = "ICRD"; break;
54624 case drwav_metadata_type_list_info_genre: pID = "IGNR"; break;
54625 case drwav_metadata_type_list_info_album: pID = "IPRD"; break;
54626 case drwav_metadata_type_list_info_tracknumber: pID = "ITRK"; break;
54627 default: break;
54628 }
54629 DRWAV_ASSERT(pID != NULL);
54630 if (pMetadata->data.infoText.stringLength) {
54631 subchunkSize = pMetadata->data.infoText.stringLength + 1;
54632 bytesWritten += drwav__write_or_count(pWav, pID, 4);
54633 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
54634 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength);
54635 bytesWritten += drwav__write_or_count_byte(pWav, '\0');
54636 }
54637 } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) {
54638 if (pMetadata->data.unknown.dataSizeInBytes) {
54639 subchunkSize = pMetadata->data.unknown.dataSizeInBytes;
54640 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
54641 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes);
54642 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);
54643 }
54644 }
54645 if ((subchunkSize % 2) != 0) {
54646 bytesWritten += drwav__write_or_count_byte(pWav, 0);
54647 }
54648 }
54649 }
54650 if (hasListAdtl) {
54651 drwav_uint32 chunkSize = 4;
54652 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
54653 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
54654 switch (pMetadata->type)
54655 {
54656 case drwav_metadata_type_list_label:
54657 case drwav_metadata_type_list_note:
54658 {
54659 chunkSize += 8;
54660 chunkSize += DRWAV_LIST_LABEL_OR_NOTE_BYTES;
54661 if (pMetadata->data.labelOrNote.stringLength > 0) {
54662 chunkSize += pMetadata->data.labelOrNote.stringLength + 1;
54663 }
54664 } break;
54665 case drwav_metadata_type_list_labelled_cue_region:
54666 {
54667 chunkSize += 8;
54668 chunkSize += DRWAV_LIST_LABELLED_TEXT_BYTES;
54669 if (pMetadata->data.labelledCueRegion.stringLength > 0) {
54670 chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;
54671 }
54672 } break;
54673 case drwav_metadata_type_unknown:
54674 {
54675 if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) {
54676 chunkSize += 8;
54677 chunkSize += pMetadata->data.unknown.dataSizeInBytes;
54678 }
54679 } break;
54680 default: break;
54681 }
54682 if ((chunkSize % 2) != 0) {
54683 chunkSize += 1;
54684 }
54685 }
54686 bytesWritten += drwav__write_or_count(pWav, "LIST", 4);
54687 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
54688 bytesWritten += drwav__write_or_count(pWav, "adtl", 4);
54689 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
54690 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
54691 drwav_uint32 subchunkSize = 0;
54692 switch (pMetadata->type)
54693 {
54694 case drwav_metadata_type_list_label:
54695 case drwav_metadata_type_list_note:
54696 {
54697 if (pMetadata->data.labelOrNote.stringLength > 0) {
54698 const char *pID = NULL;
54699 if (pMetadata->type == drwav_metadata_type_list_label) {
54700 pID = "labl";
54701 }
54702 else if (pMetadata->type == drwav_metadata_type_list_note) {
54703 pID = "note";
54704 }
54705 DRWAV_ASSERT(pID != NULL);
54706 DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);
54707 subchunkSize = DRWAV_LIST_LABEL_OR_NOTE_BYTES;
54708 bytesWritten += drwav__write_or_count(pWav, pID, 4);
54709 subchunkSize += pMetadata->data.labelOrNote.stringLength + 1;
54710 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
54711 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId);
54712 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength);
54713 bytesWritten += drwav__write_or_count_byte(pWav, '\0');
54714 }
54715 } break;
54716 case drwav_metadata_type_list_labelled_cue_region:
54717 {
54718 subchunkSize = DRWAV_LIST_LABELLED_TEXT_BYTES;
54719 bytesWritten += drwav__write_or_count(pWav, "ltxt", 4);
54720 if (pMetadata->data.labelledCueRegion.stringLength > 0) {
54721 subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;
54722 }
54723 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
54724 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId);
54725 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength);
54726 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4);
54727 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country);
54728 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language);
54729 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect);
54730 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage);
54731 if (pMetadata->data.labelledCueRegion.stringLength > 0) {
54732 DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);
54733 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength);
54734 bytesWritten += drwav__write_or_count_byte(pWav, '\0');
54735 }
54736 } break;
54737 case drwav_metadata_type_unknown:
54738 {
54739 if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) {
54740 subchunkSize = pMetadata->data.unknown.dataSizeInBytes;
54741 DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL);
54742 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
54743 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
54744 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);
54745 }
54746 } break;
54747 default: break;
54748 }
54749 if ((subchunkSize % 2) != 0) {
54750 bytesWritten += drwav__write_or_count_byte(pWav, 0);
54751 }
54752 }
54753 }
54754 DRWAV_ASSERT((bytesWritten % 2) == 0);
54755 return bytesWritten;
54756}
54757DRWAV_PRIVATE drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize, drwav_metadata* pMetadata, drwav_uint32 metadataCount)
54758{
54759 drwav_uint64 chunkSize = 4 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize);
54760 if (chunkSize > 0xFFFFFFFFUL) {
54761 chunkSize = 0xFFFFFFFFUL;
54762 }
54763 return (drwav_uint32)chunkSize;
54764}
54765DRWAV_PRIVATE drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize)
54766{
54767 if (dataChunkSize <= 0xFFFFFFFFUL) {
54768 return (drwav_uint32)dataChunkSize;
54769 } else {
54770 return 0xFFFFFFFFUL;
54771 }
54772}
54773DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize)
54774{
54775 drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize);
54776 return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize;
54777}
54778DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize)
54779{
54780 return 24 + dataChunkSize;
54781}
54782DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_rf64(drwav_uint64 dataChunkSize, drwav_metadata *metadata, drwav_uint32 numMetadata)
54783{
54784 drwav_uint64 chunkSize = 4 + 36 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize);
54785 if (chunkSize > 0xFFFFFFFFUL) {
54786 chunkSize = 0xFFFFFFFFUL;
54787 }
54788 return chunkSize;
54789}
54790DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_rf64(drwav_uint64 dataChunkSize)
54791{
54792 return dataChunkSize;
54793}
54794DRWAV_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)
54795{
54796 if (pWav == NULL || onWrite == NULL) {
54797 return DRWAV_FALSE;
54798 }
54799 if (!isSequential && onSeek == NULL) {
54800 return DRWAV_FALSE;
54801 }
54802 if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) {
54803 return DRWAV_FALSE;
54804 }
54805 if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) {
54806 return DRWAV_FALSE;
54807 }
54808 DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));
54809 pWav->onWrite = onWrite;
54810 pWav->onSeek = onSeek;
54811 pWav->pUserData = pUserData;
54812 pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
54813 if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
54814 return DRWAV_FALSE;
54815 }
54816 pWav->fmt.formatTag = (drwav_uint16)pFormat->format;
54817 pWav->fmt.channels = (drwav_uint16)pFormat->channels;
54818 pWav->fmt.sampleRate = pFormat->sampleRate;
54819 pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8);
54820 pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8);
54821 pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
54822 pWav->fmt.extendedSize = 0;
54823 pWav->isSequentialWrite = isSequential;
54824 return DRWAV_TRUE;
54825}
54826DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)
54827{
54828 size_t runningPos = 0;
54829 drwav_uint64 initialDataChunkSize = 0;
54830 drwav_uint64 chunkSizeFMT;
54831 if (pWav->isSequentialWrite) {
54832 initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8;
54833 if (pFormat->container == drwav_container_riff) {
54834 if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) {
54835 return DRWAV_FALSE;
54836 }
54837 }
54838 }
54839 pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize;
54840 if (pFormat->container == drwav_container_riff) {
54841 drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize;
54842 runningPos += drwav__write(pWav, "RIFF", 4);
54843 runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF);
54844 runningPos += drwav__write(pWav, "WAVE", 4);
54845 } else if (pFormat->container == drwav_container_w64) {
54846 drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize;
54847 runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16);
54848 runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF);
54849 runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16);
54850 } else if (pFormat->container == drwav_container_rf64) {
54851 runningPos += drwav__write(pWav, "RF64", 4);
54852 runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF);
54853 runningPos += drwav__write(pWav, "WAVE", 4);
54854 }
54855 if (pFormat->container == drwav_container_rf64) {
54856 drwav_uint32 initialds64ChunkSize = 28;
54857 drwav_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize;
54858 runningPos += drwav__write(pWav, "ds64", 4);
54859 runningPos += drwav__write_u32ne_to_le(pWav, initialds64ChunkSize);
54860 runningPos += drwav__write_u64ne_to_le(pWav, initialRiffChunkSize);
54861 runningPos += drwav__write_u64ne_to_le(pWav, initialDataChunkSize);
54862 runningPos += drwav__write_u64ne_to_le(pWav, totalSampleCount);
54863 runningPos += drwav__write_u32ne_to_le(pWav, 0);
54864 }
54865 if (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64) {
54866 chunkSizeFMT = 16;
54867 runningPos += drwav__write(pWav, "fmt ", 4);
54868 runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT);
54869 } else if (pFormat->container == drwav_container_w64) {
54870 chunkSizeFMT = 40;
54871 runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16);
54872 runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT);
54873 }
54874 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag);
54875 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels);
54876 runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate);
54877 runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec);
54878 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign);
54879 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample);
54880 if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64)) {
54881 runningPos += drwav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount);
54882 }
54883 pWav->dataChunkDataPos = runningPos;
54884 if (pFormat->container == drwav_container_riff) {
54885 drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize;
54886 runningPos += drwav__write(pWav, "data", 4);
54887 runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA);
54888 } else if (pFormat->container == drwav_container_w64) {
54889 drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize;
54890 runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16);
54891 runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA);
54892 } else if (pFormat->container == drwav_container_rf64) {
54893 runningPos += drwav__write(pWav, "data", 4);
54894 runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF);
54895 }
54896 pWav->container = pFormat->container;
54897 pWav->channels = (drwav_uint16)pFormat->channels;
54898 pWav->sampleRate = pFormat->sampleRate;
54899 pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
54900 pWav->translatedFormatTag = (drwav_uint16)pFormat->format;
54901 pWav->dataChunkDataPos = runningPos;
54902 return DRWAV_TRUE;
54903}
54904DRWAV_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)
54905{
54906 if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
54907 return DRWAV_FALSE;
54908 }
54909 return drwav_init_write__internal(pWav, pFormat, 0);
54910}
54911DRWAV_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)
54912{
54913 if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) {
54914 return DRWAV_FALSE;
54915 }
54916 return drwav_init_write__internal(pWav, pFormat, totalSampleCount);
54917}
54918DRWAV_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)
54919{
54920 if (pFormat == NULL) {
54921 return DRWAV_FALSE;
54922 }
54923 return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks);
54924}
54925DRWAV_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)
54926{
54927 if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
54928 return DRWAV_FALSE;
54929 }
54930 pWav->pMetadata = pMetadata;
54931 pWav->metadataCount = metadataCount;
54932 return drwav_init_write__internal(pWav, pFormat, 0);
54933}
54934DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount)
54935{
54936 drwav_uint64 targetDataSizeBytes = (drwav_uint64)((drwav_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0);
54937 drwav_uint64 riffChunkSizeBytes;
54938 drwav_uint64 fileSizeBytes = 0;
54939 if (pFormat->container == drwav_container_riff) {
54940 riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount);
54941 fileSizeBytes = (8 + riffChunkSizeBytes);
54942 } else if (pFormat->container == drwav_container_w64) {
54943 riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes);
54944 fileSizeBytes = riffChunkSizeBytes;
54945 } else if (pFormat->container == drwav_container_rf64) {
54946 riffChunkSizeBytes = drwav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount);
54947 fileSizeBytes = (8 + riffChunkSizeBytes);
54948 }
54949 return fileSizeBytes;
54950}
54951#ifndef DR_WAV_NO_STDIO
54952#include <errno.h>
54953DRWAV_PRIVATE drwav_result drwav_result_from_errno(int e)
54954{
54955 switch (e)
54956 {
54957 case 0: return DRWAV_SUCCESS;
54958 #ifdef EPERM
54959 case EPERM: return DRWAV_INVALID_OPERATION;
54960 #endif
54961 #ifdef ENOENT
54962 case ENOENT: return DRWAV_DOES_NOT_EXIST;
54963 #endif
54964 #ifdef ESRCH
54965 case ESRCH: return DRWAV_DOES_NOT_EXIST;
54966 #endif
54967 #ifdef EINTR
54968 case EINTR: return DRWAV_INTERRUPT;
54969 #endif
54970 #ifdef EIO
54971 case EIO: return DRWAV_IO_ERROR;
54972 #endif
54973 #ifdef ENXIO
54974 case ENXIO: return DRWAV_DOES_NOT_EXIST;
54975 #endif
54976 #ifdef E2BIG
54977 case E2BIG: return DRWAV_INVALID_ARGS;
54978 #endif
54979 #ifdef ENOEXEC
54980 case ENOEXEC: return DRWAV_INVALID_FILE;
54981 #endif
54982 #ifdef EBADF
54983 case EBADF: return DRWAV_INVALID_FILE;
54984 #endif
54985 #ifdef ECHILD
54986 case ECHILD: return DRWAV_ERROR;
54987 #endif
54988 #ifdef EAGAIN
54989 case EAGAIN: return DRWAV_UNAVAILABLE;
54990 #endif
54991 #ifdef ENOMEM
54992 case ENOMEM: return DRWAV_OUT_OF_MEMORY;
54993 #endif
54994 #ifdef EACCES
54995 case EACCES: return DRWAV_ACCESS_DENIED;
54996 #endif
54997 #ifdef EFAULT
54998 case EFAULT: return DRWAV_BAD_ADDRESS;
54999 #endif
55000 #ifdef ENOTBLK
55001 case ENOTBLK: return DRWAV_ERROR;
55002 #endif
55003 #ifdef EBUSY
55004 case EBUSY: return DRWAV_BUSY;
55005 #endif
55006 #ifdef EEXIST
55007 case EEXIST: return DRWAV_ALREADY_EXISTS;
55008 #endif
55009 #ifdef EXDEV
55010 case EXDEV: return DRWAV_ERROR;
55011 #endif
55012 #ifdef ENODEV
55013 case ENODEV: return DRWAV_DOES_NOT_EXIST;
55014 #endif
55015 #ifdef ENOTDIR
55016 case ENOTDIR: return DRWAV_NOT_DIRECTORY;
55017 #endif
55018 #ifdef EISDIR
55019 case EISDIR: return DRWAV_IS_DIRECTORY;
55020 #endif
55021 #ifdef EINVAL
55022 case EINVAL: return DRWAV_INVALID_ARGS;
55023 #endif
55024 #ifdef ENFILE
55025 case ENFILE: return DRWAV_TOO_MANY_OPEN_FILES;
55026 #endif
55027 #ifdef EMFILE
55028 case EMFILE: return DRWAV_TOO_MANY_OPEN_FILES;
55029 #endif
55030 #ifdef ENOTTY
55031 case ENOTTY: return DRWAV_INVALID_OPERATION;
55032 #endif
55033 #ifdef ETXTBSY
55034 case ETXTBSY: return DRWAV_BUSY;
55035 #endif
55036 #ifdef EFBIG
55037 case EFBIG: return DRWAV_TOO_BIG;
55038 #endif
55039 #ifdef ENOSPC
55040 case ENOSPC: return DRWAV_NO_SPACE;
55041 #endif
55042 #ifdef ESPIPE
55043 case ESPIPE: return DRWAV_BAD_SEEK;
55044 #endif
55045 #ifdef EROFS
55046 case EROFS: return DRWAV_ACCESS_DENIED;
55047 #endif
55048 #ifdef EMLINK
55049 case EMLINK: return DRWAV_TOO_MANY_LINKS;
55050 #endif
55051 #ifdef EPIPE
55052 case EPIPE: return DRWAV_BAD_PIPE;
55053 #endif
55054 #ifdef EDOM
55055 case EDOM: return DRWAV_OUT_OF_RANGE;
55056 #endif
55057 #ifdef ERANGE
55058 case ERANGE: return DRWAV_OUT_OF_RANGE;
55059 #endif
55060 #ifdef EDEADLK
55061 case EDEADLK: return DRWAV_DEADLOCK;
55062 #endif
55063 #ifdef ENAMETOOLONG
55064 case ENAMETOOLONG: return DRWAV_PATH_TOO_LONG;
55065 #endif
55066 #ifdef ENOLCK
55067 case ENOLCK: return DRWAV_ERROR;
55068 #endif
55069 #ifdef ENOSYS
55070 case ENOSYS: return DRWAV_NOT_IMPLEMENTED;
55071 #endif
55072 #ifdef ENOTEMPTY
55073 case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY;
55074 #endif
55075 #ifdef ELOOP
55076 case ELOOP: return DRWAV_TOO_MANY_LINKS;
55077 #endif
55078 #ifdef ENOMSG
55079 case ENOMSG: return DRWAV_NO_MESSAGE;
55080 #endif
55081 #ifdef EIDRM
55082 case EIDRM: return DRWAV_ERROR;
55083 #endif
55084 #ifdef ECHRNG
55085 case ECHRNG: return DRWAV_ERROR;
55086 #endif
55087 #ifdef EL2NSYNC
55088 case EL2NSYNC: return DRWAV_ERROR;
55089 #endif
55090 #ifdef EL3HLT
55091 case EL3HLT: return DRWAV_ERROR;
55092 #endif
55093 #ifdef EL3RST
55094 case EL3RST: return DRWAV_ERROR;
55095 #endif
55096 #ifdef ELNRNG
55097 case ELNRNG: return DRWAV_OUT_OF_RANGE;
55098 #endif
55099 #ifdef EUNATCH
55100 case EUNATCH: return DRWAV_ERROR;
55101 #endif
55102 #ifdef ENOCSI
55103 case ENOCSI: return DRWAV_ERROR;
55104 #endif
55105 #ifdef EL2HLT
55106 case EL2HLT: return DRWAV_ERROR;
55107 #endif
55108 #ifdef EBADE
55109 case EBADE: return DRWAV_ERROR;
55110 #endif
55111 #ifdef EBADR
55112 case EBADR: return DRWAV_ERROR;
55113 #endif
55114 #ifdef EXFULL
55115 case EXFULL: return DRWAV_ERROR;
55116 #endif
55117 #ifdef ENOANO
55118 case ENOANO: return DRWAV_ERROR;
55119 #endif
55120 #ifdef EBADRQC
55121 case EBADRQC: return DRWAV_ERROR;
55122 #endif
55123 #ifdef EBADSLT
55124 case EBADSLT: return DRWAV_ERROR;
55125 #endif
55126 #ifdef EBFONT
55127 case EBFONT: return DRWAV_INVALID_FILE;
55128 #endif
55129 #ifdef ENOSTR
55130 case ENOSTR: return DRWAV_ERROR;
55131 #endif
55132 #ifdef ENODATA
55133 case ENODATA: return DRWAV_NO_DATA_AVAILABLE;
55134 #endif
55135 #ifdef ETIME
55136 case ETIME: return DRWAV_TIMEOUT;
55137 #endif
55138 #ifdef ENOSR
55139 case ENOSR: return DRWAV_NO_DATA_AVAILABLE;
55140 #endif
55141 #ifdef ENONET
55142 case ENONET: return DRWAV_NO_NETWORK;
55143 #endif
55144 #ifdef ENOPKG
55145 case ENOPKG: return DRWAV_ERROR;
55146 #endif
55147 #ifdef EREMOTE
55148 case EREMOTE: return DRWAV_ERROR;
55149 #endif
55150 #ifdef ENOLINK
55151 case ENOLINK: return DRWAV_ERROR;
55152 #endif
55153 #ifdef EADV
55154 case EADV: return DRWAV_ERROR;
55155 #endif
55156 #ifdef ESRMNT
55157 case ESRMNT: return DRWAV_ERROR;
55158 #endif
55159 #ifdef ECOMM
55160 case ECOMM: return DRWAV_ERROR;
55161 #endif
55162 #ifdef EPROTO
55163 case EPROTO: return DRWAV_ERROR;
55164 #endif
55165 #ifdef EMULTIHOP
55166 case EMULTIHOP: return DRWAV_ERROR;
55167 #endif
55168 #ifdef EDOTDOT
55169 case EDOTDOT: return DRWAV_ERROR;
55170 #endif
55171 #ifdef EBADMSG
55172 case EBADMSG: return DRWAV_BAD_MESSAGE;
55173 #endif
55174 #ifdef EOVERFLOW
55175 case EOVERFLOW: return DRWAV_TOO_BIG;
55176 #endif
55177 #ifdef ENOTUNIQ
55178 case ENOTUNIQ: return DRWAV_NOT_UNIQUE;
55179 #endif
55180 #ifdef EBADFD
55181 case EBADFD: return DRWAV_ERROR;
55182 #endif
55183 #ifdef EREMCHG
55184 case EREMCHG: return DRWAV_ERROR;
55185 #endif
55186 #ifdef ELIBACC
55187 case ELIBACC: return DRWAV_ACCESS_DENIED;
55188 #endif
55189 #ifdef ELIBBAD
55190 case ELIBBAD: return DRWAV_INVALID_FILE;
55191 #endif
55192 #ifdef ELIBSCN
55193 case ELIBSCN: return DRWAV_INVALID_FILE;
55194 #endif
55195 #ifdef ELIBMAX
55196 case ELIBMAX: return DRWAV_ERROR;
55197 #endif
55198 #ifdef ELIBEXEC
55199 case ELIBEXEC: return DRWAV_ERROR;
55200 #endif
55201 #ifdef EILSEQ
55202 case EILSEQ: return DRWAV_INVALID_DATA;
55203 #endif
55204 #ifdef ERESTART
55205 case ERESTART: return DRWAV_ERROR;
55206 #endif
55207 #ifdef ESTRPIPE
55208 case ESTRPIPE: return DRWAV_ERROR;
55209 #endif
55210 #ifdef EUSERS
55211 case EUSERS: return DRWAV_ERROR;
55212 #endif
55213 #ifdef ENOTSOCK
55214 case ENOTSOCK: return DRWAV_NOT_SOCKET;
55215 #endif
55216 #ifdef EDESTADDRREQ
55217 case EDESTADDRREQ: return DRWAV_NO_ADDRESS;
55218 #endif
55219 #ifdef EMSGSIZE
55220 case EMSGSIZE: return DRWAV_TOO_BIG;
55221 #endif
55222 #ifdef EPROTOTYPE
55223 case EPROTOTYPE: return DRWAV_BAD_PROTOCOL;
55224 #endif
55225 #ifdef ENOPROTOOPT
55226 case ENOPROTOOPT: return DRWAV_PROTOCOL_UNAVAILABLE;
55227 #endif
55228 #ifdef EPROTONOSUPPORT
55229 case EPROTONOSUPPORT: return DRWAV_PROTOCOL_NOT_SUPPORTED;
55230 #endif
55231 #ifdef ESOCKTNOSUPPORT
55232 case ESOCKTNOSUPPORT: return DRWAV_SOCKET_NOT_SUPPORTED;
55233 #endif
55234 #ifdef EOPNOTSUPP
55235 case EOPNOTSUPP: return DRWAV_INVALID_OPERATION;
55236 #endif
55237 #ifdef EPFNOSUPPORT
55238 case EPFNOSUPPORT: return DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED;
55239 #endif
55240 #ifdef EAFNOSUPPORT
55241 case EAFNOSUPPORT: return DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED;
55242 #endif
55243 #ifdef EADDRINUSE
55244 case EADDRINUSE: return DRWAV_ALREADY_IN_USE;
55245 #endif
55246 #ifdef EADDRNOTAVAIL
55247 case EADDRNOTAVAIL: return DRWAV_ERROR;
55248 #endif
55249 #ifdef ENETDOWN
55250 case ENETDOWN: return DRWAV_NO_NETWORK;
55251 #endif
55252 #ifdef ENETUNREACH
55253 case ENETUNREACH: return DRWAV_NO_NETWORK;
55254 #endif
55255 #ifdef ENETRESET
55256 case ENETRESET: return DRWAV_NO_NETWORK;
55257 #endif
55258 #ifdef ECONNABORTED
55259 case ECONNABORTED: return DRWAV_NO_NETWORK;
55260 #endif
55261 #ifdef ECONNRESET
55262 case ECONNRESET: return DRWAV_CONNECTION_RESET;
55263 #endif
55264 #ifdef ENOBUFS
55265 case ENOBUFS: return DRWAV_NO_SPACE;
55266 #endif
55267 #ifdef EISCONN
55268 case EISCONN: return DRWAV_ALREADY_CONNECTED;
55269 #endif
55270 #ifdef ENOTCONN
55271 case ENOTCONN: return DRWAV_NOT_CONNECTED;
55272 #endif
55273 #ifdef ESHUTDOWN
55274 case ESHUTDOWN: return DRWAV_ERROR;
55275 #endif
55276 #ifdef ETOOMANYREFS
55277 case ETOOMANYREFS: return DRWAV_ERROR;
55278 #endif
55279 #ifdef ETIMEDOUT
55280 case ETIMEDOUT: return DRWAV_TIMEOUT;
55281 #endif
55282 #ifdef ECONNREFUSED
55283 case ECONNREFUSED: return DRWAV_CONNECTION_REFUSED;
55284 #endif
55285 #ifdef EHOSTDOWN
55286 case EHOSTDOWN: return DRWAV_NO_HOST;
55287 #endif
55288 #ifdef EHOSTUNREACH
55289 case EHOSTUNREACH: return DRWAV_NO_HOST;
55290 #endif
55291 #ifdef EALREADY
55292 case EALREADY: return DRWAV_IN_PROGRESS;
55293 #endif
55294 #ifdef EINPROGRESS
55295 case EINPROGRESS: return DRWAV_IN_PROGRESS;
55296 #endif
55297 #ifdef ESTALE
55298 case ESTALE: return DRWAV_INVALID_FILE;
55299 #endif
55300 #ifdef EUCLEAN
55301 case EUCLEAN: return DRWAV_ERROR;
55302 #endif
55303 #ifdef ENOTNAM
55304 case ENOTNAM: return DRWAV_ERROR;
55305 #endif
55306 #ifdef ENAVAIL
55307 case ENAVAIL: return DRWAV_ERROR;
55308 #endif
55309 #ifdef EISNAM
55310 case EISNAM: return DRWAV_ERROR;
55311 #endif
55312 #ifdef EREMOTEIO
55313 case EREMOTEIO: return DRWAV_IO_ERROR;
55314 #endif
55315 #ifdef EDQUOT
55316 case EDQUOT: return DRWAV_NO_SPACE;
55317 #endif
55318 #ifdef ENOMEDIUM
55319 case ENOMEDIUM: return DRWAV_DOES_NOT_EXIST;
55320 #endif
55321 #ifdef EMEDIUMTYPE
55322 case EMEDIUMTYPE: return DRWAV_ERROR;
55323 #endif
55324 #ifdef ECANCELED
55325 case ECANCELED: return DRWAV_CANCELLED;
55326 #endif
55327 #ifdef ENOKEY
55328 case ENOKEY: return DRWAV_ERROR;
55329 #endif
55330 #ifdef EKEYEXPIRED
55331 case EKEYEXPIRED: return DRWAV_ERROR;
55332 #endif
55333 #ifdef EKEYREVOKED
55334 case EKEYREVOKED: return DRWAV_ERROR;
55335 #endif
55336 #ifdef EKEYREJECTED
55337 case EKEYREJECTED: return DRWAV_ERROR;
55338 #endif
55339 #ifdef EOWNERDEAD
55340 case EOWNERDEAD: return DRWAV_ERROR;
55341 #endif
55342 #ifdef ENOTRECOVERABLE
55343 case ENOTRECOVERABLE: return DRWAV_ERROR;
55344 #endif
55345 #ifdef ERFKILL
55346 case ERFKILL: return DRWAV_ERROR;
55347 #endif
55348 #ifdef EHWPOISON
55349 case EHWPOISON: return DRWAV_ERROR;
55350 #endif
55351 default: return DRWAV_ERROR;
55352 }
55353}
55354DRWAV_PRIVATE drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
55355{
55356#if defined(_MSC_VER) && _MSC_VER >= 1400
55357 errno_t err;
55358#endif
55359 if (ppFile != NULL) {
55360 *ppFile = NULL;
55361 }
55362 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
55363 return DRWAV_INVALID_ARGS;
55364 }
55365#if defined(_MSC_VER) && _MSC_VER >= 1400
55366 err = fopen_s(ppFile, pFilePath, pOpenMode);
55367 if (err != 0) {
55368 return drwav_result_from_errno(err);
55369 }
55370#else
55371#if defined(_WIN32) || defined(__APPLE__)
55372 *ppFile = fopen(pFilePath, pOpenMode);
55373#else
55374 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
55375 *ppFile = fopen64(pFilePath, pOpenMode);
55376 #else
55377 *ppFile = fopen(pFilePath, pOpenMode);
55378 #endif
55379#endif
55380 if (*ppFile == NULL) {
55381 drwav_result result = drwav_result_from_errno(errno);
55382 if (result == DRWAV_SUCCESS) {
55383 result = DRWAV_ERROR;
55384 }
55385 return result;
55386 }
55387#endif
55388 return DRWAV_SUCCESS;
55389}
55390#if defined(_WIN32)
55391 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
55392 #define DRWAV_HAS_WFOPEN
55393 #endif
55394#endif
55395DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks)
55396{
55397 if (ppFile != NULL) {
55398 *ppFile = NULL;
55399 }
55400 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
55401 return DRWAV_INVALID_ARGS;
55402 }
55403#if defined(DRWAV_HAS_WFOPEN)
55404 {
55405 #if defined(_MSC_VER) && _MSC_VER >= 1400
55406 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
55407 if (err != 0) {
55408 return drwav_result_from_errno(err);
55409 }
55410 #else
55411 *ppFile = _wfopen(pFilePath, pOpenMode);
55412 if (*ppFile == NULL) {
55413 return drwav_result_from_errno(errno);
55414 }
55415 #endif
55416 (void)pAllocationCallbacks;
55417 }
55418#else
55419 {
55420 mbstate_t mbs;
55421 size_t lenMB;
55422 const wchar_t* pFilePathTemp = pFilePath;
55423 char* pFilePathMB = NULL;
55424 char pOpenModeMB[32] = {0};
55425 DRWAV_ZERO_OBJECT(&mbs);
55426 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
55427 if (lenMB == (size_t)-1) {
55428 return drwav_result_from_errno(errno);
55429 }
55430 pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
55431 if (pFilePathMB == NULL) {
55432 return DRWAV_OUT_OF_MEMORY;
55433 }
55434 pFilePathTemp = pFilePath;
55435 DRWAV_ZERO_OBJECT(&mbs);
55436 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
55437 {
55438 size_t i = 0;
55439 for (;;) {
55440 if (pOpenMode[i] == 0) {
55441 pOpenModeMB[i] = '\0';
55442 break;
55443 }
55444 pOpenModeMB[i] = (char)pOpenMode[i];
55445 i += 1;
55446 }
55447 }
55448 *ppFile = fopen(pFilePathMB, pOpenModeMB);
55449 drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
55450 }
55451 if (*ppFile == NULL) {
55452 return DRWAV_ERROR;
55453 }
55454#endif
55455 return DRWAV_SUCCESS;
55456}
55457DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
55458{
55459 return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
55460}
55461DRWAV_PRIVATE size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite)
55462{
55463 return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData);
55464}
55465DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin)
55466{
55467 return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
55468}
55469DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
55470{
55471 return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
55472}
55473DRWAV_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)
55474{
55475 drwav_bool32 result;
55476 result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
55477 if (result != DRWAV_TRUE) {
55478 fclose(pFile);
55479 return result;
55480 }
55481 pWav->allowedMetadataTypes = allowedMetadataTypes;
55482 result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
55483 if (result != DRWAV_TRUE) {
55484 fclose(pFile);
55485 return result;
55486 }
55487 return DRWAV_TRUE;
55488}
55489DRWAV_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)
55490{
55491 FILE* pFile;
55492 if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) {
55493 return DRWAV_FALSE;
55494 }
55495 return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks);
55496}
55497DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
55498{
55499 return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
55500}
55501DRWAV_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)
55502{
55503 FILE* pFile;
55504 if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) {
55505 return DRWAV_FALSE;
55506 }
55507 return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks);
55508}
55509DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
55510{
55511 FILE* pFile;
55512 if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) {
55513 return DRWAV_FALSE;
55514 }
55515 return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks);
55516}
55517DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
55518{
55519 FILE* pFile;
55520 if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) {
55521 return DRWAV_FALSE;
55522 }
55523 return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks);
55524}
55525DRWAV_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)
55526{
55527 drwav_bool32 result;
55528 result = drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
55529 if (result != DRWAV_TRUE) {
55530 fclose(pFile);
55531 return result;
55532 }
55533 result = drwav_init_write__internal(pWav, pFormat, totalSampleCount);
55534 if (result != DRWAV_TRUE) {
55535 fclose(pFile);
55536 return result;
55537 }
55538 return DRWAV_TRUE;
55539}
55540DRWAV_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)
55541{
55542 FILE* pFile;
55543 if (drwav_fopen(&pFile, filename, "wb") != DRWAV_SUCCESS) {
55544 return DRWAV_FALSE;
55545 }
55546 return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
55547}
55548DRWAV_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)
55549{
55550 FILE* pFile;
55551 if (drwav_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != DRWAV_SUCCESS) {
55552 return DRWAV_FALSE;
55553 }
55554 return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
55555}
55556DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
55557{
55558 return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
55559}
55560DRWAV_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)
55561{
55562 return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
55563}
55564DRWAV_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)
55565{
55566 if (pFormat == NULL) {
55567 return DRWAV_FALSE;
55568 }
55569 return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
55570}
55571DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
55572{
55573 return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
55574}
55575DRWAV_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)
55576{
55577 return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
55578}
55579DRWAV_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)
55580{
55581 if (pFormat == NULL) {
55582 return DRWAV_FALSE;
55583 }
55584 return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
55585}
55586#endif
55587DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
55588{
55589 drwav* pWav = (drwav*)pUserData;
55590 size_t bytesRemaining;
55591 DRWAV_ASSERT(pWav != NULL);
55592 DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos);
55593 bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos;
55594 if (bytesToRead > bytesRemaining) {
55595 bytesToRead = bytesRemaining;
55596 }
55597 if (bytesToRead > 0) {
55598 DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead);
55599 pWav->memoryStream.currentReadPos += bytesToRead;
55600 }
55601 return bytesToRead;
55602}
55603DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin)
55604{
55605 drwav* pWav = (drwav*)pUserData;
55606 DRWAV_ASSERT(pWav != NULL);
55607 if (origin == drwav_seek_origin_current) {
55608 if (offset > 0) {
55609 if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) {
55610 return DRWAV_FALSE;
55611 }
55612 } else {
55613 if (pWav->memoryStream.currentReadPos < (size_t)-offset) {
55614 return DRWAV_FALSE;
55615 }
55616 }
55617 pWav->memoryStream.currentReadPos += offset;
55618 } else {
55619 if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) {
55620 pWav->memoryStream.currentReadPos = offset;
55621 } else {
55622 return DRWAV_FALSE;
55623 }
55624 }
55625 return DRWAV_TRUE;
55626}
55627DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)
55628{
55629 drwav* pWav = (drwav*)pUserData;
55630 size_t bytesRemaining;
55631 DRWAV_ASSERT(pWav != NULL);
55632 DRWAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos);
55633 bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos;
55634 if (bytesRemaining < bytesToWrite) {
55635 void* pNewData;
55636 size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2;
55637 if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) {
55638 newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite;
55639 }
55640 pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks);
55641 if (pNewData == NULL) {
55642 return 0;
55643 }
55644 *pWav->memoryStreamWrite.ppData = pNewData;
55645 pWav->memoryStreamWrite.dataCapacity = newDataCapacity;
55646 }
55647 DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite);
55648 pWav->memoryStreamWrite.currentWritePos += bytesToWrite;
55649 if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) {
55650 pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos;
55651 }
55652 *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize;
55653 return bytesToWrite;
55654}
55655DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin)
55656{
55657 drwav* pWav = (drwav*)pUserData;
55658 DRWAV_ASSERT(pWav != NULL);
55659 if (origin == drwav_seek_origin_current) {
55660 if (offset > 0) {
55661 if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) {
55662 offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos);
55663 }
55664 } else {
55665 if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) {
55666 offset = -(int)pWav->memoryStreamWrite.currentWritePos;
55667 }
55668 }
55669 pWav->memoryStreamWrite.currentWritePos += offset;
55670 } else {
55671 if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) {
55672 pWav->memoryStreamWrite.currentWritePos = offset;
55673 } else {
55674 pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize;
55675 }
55676 }
55677 return DRWAV_TRUE;
55678}
55679DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks)
55680{
55681 return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks);
55682}
55683DRWAV_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)
55684{
55685 if (data == NULL || dataSize == 0) {
55686 return DRWAV_FALSE;
55687 }
55688 if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) {
55689 return DRWAV_FALSE;
55690 }
55691 pWav->memoryStream.data = (const drwav_uint8*)data;
55692 pWav->memoryStream.dataSize = dataSize;
55693 pWav->memoryStream.currentReadPos = 0;
55694 return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
55695}
55696DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
55697{
55698 if (data == NULL || dataSize == 0) {
55699 return DRWAV_FALSE;
55700 }
55701 if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) {
55702 return DRWAV_FALSE;
55703 }
55704 pWav->memoryStream.data = (const drwav_uint8*)data;
55705 pWav->memoryStream.dataSize = dataSize;
55706 pWav->memoryStream.currentReadPos = 0;
55707 pWav->allowedMetadataTypes = drwav_metadata_type_all_including_unknown;
55708 return drwav_init__internal(pWav, NULL, NULL, flags);
55709}
55710DRWAV_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)
55711{
55712 if (ppData == NULL || pDataSize == NULL) {
55713 return DRWAV_FALSE;
55714 }
55715 *ppData = NULL;
55716 *pDataSize = 0;
55717 if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) {
55718 return DRWAV_FALSE;
55719 }
55720 pWav->memoryStreamWrite.ppData = ppData;
55721 pWav->memoryStreamWrite.pDataSize = pDataSize;
55722 pWav->memoryStreamWrite.dataSize = 0;
55723 pWav->memoryStreamWrite.dataCapacity = 0;
55724 pWav->memoryStreamWrite.currentWritePos = 0;
55725 return drwav_init_write__internal(pWav, pFormat, totalSampleCount);
55726}
55727DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
55728{
55729 return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
55730}
55731DRWAV_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)
55732{
55733 return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
55734}
55735DRWAV_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)
55736{
55737 if (pFormat == NULL) {
55738 return DRWAV_FALSE;
55739 }
55740 return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
55741}
55742DRWAV_API drwav_result drwav_uninit(drwav* pWav)
55743{
55744 drwav_result result = DRWAV_SUCCESS;
55745 if (pWav == NULL) {
55746 return DRWAV_INVALID_ARGS;
55747 }
55748 if (pWav->onWrite != NULL) {
55749 drwav_uint32 paddingSize = 0;
55750 if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) {
55751 paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize);
55752 } else {
55753 paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize);
55754 }
55755 if (paddingSize > 0) {
55756 drwav_uint64 paddingData = 0;
55757 drwav__write(pWav, &paddingData, paddingSize);
55758 }
55759 if (pWav->onSeek && !pWav->isSequentialWrite) {
55760 if (pWav->container == drwav_container_riff) {
55761 if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) {
55762 drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
55763 drwav__write_u32ne_to_le(pWav, riffChunkSize);
55764 }
55765 if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, drwav_seek_origin_start)) {
55766 drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize);
55767 drwav__write_u32ne_to_le(pWav, dataChunkSize);
55768 }
55769 } else if (pWav->container == drwav_container_w64) {
55770 if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) {
55771 drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize);
55772 drwav__write_u64ne_to_le(pWav, riffChunkSize);
55773 }
55774 if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, drwav_seek_origin_start)) {
55775 drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize);
55776 drwav__write_u64ne_to_le(pWav, dataChunkSize);
55777 }
55778 } else if (pWav->container == drwav_container_rf64) {
55779 int ds64BodyPos = 12 + 8;
55780 if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) {
55781 drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
55782 drwav__write_u64ne_to_le(pWav, riffChunkSize);
55783 }
55784 if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) {
55785 drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize);
55786 drwav__write_u64ne_to_le(pWav, dataChunkSize);
55787 }
55788 }
55789 }
55790 if (pWav->isSequentialWrite) {
55791 if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) {
55792 result = DRWAV_INVALID_FILE;
55793 }
55794 }
55795 } else {
55796 if (pWav->pMetadata != NULL) {
55797 pWav->allocationCallbacks.onFree(pWav->pMetadata, pWav->allocationCallbacks.pUserData);
55798 }
55799 }
55800#ifndef DR_WAV_NO_STDIO
55801 if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) {
55802 fclose((FILE*)pWav->pUserData);
55803 }
55804#endif
55805 return result;
55806}
55807DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut)
55808{
55809 size_t bytesRead;
55810 if (pWav == NULL || bytesToRead == 0) {
55811 return 0;
55812 }
55813 if (bytesToRead > pWav->bytesRemaining) {
55814 bytesToRead = (size_t)pWav->bytesRemaining;
55815 }
55816 if (bytesToRead == 0) {
55817 return 0;
55818 }
55819 if (pBufferOut != NULL) {
55820 bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);
55821 } else {
55822 bytesRead = 0;
55823 while (bytesRead < bytesToRead) {
55824 size_t bytesToSeek = (bytesToRead - bytesRead);
55825 if (bytesToSeek > 0x7FFFFFFF) {
55826 bytesToSeek = 0x7FFFFFFF;
55827 }
55828 if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) {
55829 break;
55830 }
55831 bytesRead += bytesToSeek;
55832 }
55833 while (bytesRead < bytesToRead) {
55834 drwav_uint8 buffer[4096];
55835 size_t bytesSeeked;
55836 size_t bytesToSeek = (bytesToRead - bytesRead);
55837 if (bytesToSeek > sizeof(buffer)) {
55838 bytesToSeek = sizeof(buffer);
55839 }
55840 bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek);
55841 bytesRead += bytesSeeked;
55842 if (bytesSeeked < bytesToSeek) {
55843 break;
55844 }
55845 }
55846 }
55847 pWav->readCursorInPCMFrames += bytesRead / drwav_get_bytes_per_pcm_frame(pWav);
55848 pWav->bytesRemaining -= bytesRead;
55849 return bytesRead;
55850}
55851DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
55852{
55853 drwav_uint32 bytesPerFrame;
55854 drwav_uint64 bytesToRead;
55855 if (pWav == NULL || framesToRead == 0) {
55856 return 0;
55857 }
55858 if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
55859 return 0;
55860 }
55861 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
55862 if (bytesPerFrame == 0) {
55863 return 0;
55864 }
55865 bytesToRead = framesToRead * bytesPerFrame;
55866 if (bytesToRead > DRWAV_SIZE_MAX) {
55867 bytesToRead = (DRWAV_SIZE_MAX / bytesPerFrame) * bytesPerFrame;
55868 }
55869 if (bytesToRead == 0) {
55870 return 0;
55871 }
55872 return drwav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame;
55873}
55874DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
55875{
55876 drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
55877 if (pBufferOut != NULL) {
55878 drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, drwav_get_bytes_per_pcm_frame(pWav)/pWav->channels, pWav->translatedFormatTag);
55879 }
55880 return framesRead;
55881}
55882DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
55883{
55884 if (drwav__is_little_endian()) {
55885 return drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
55886 } else {
55887 return drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);
55888 }
55889}
55890DRWAV_PRIVATE drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav)
55891{
55892 if (pWav->onWrite != NULL) {
55893 return DRWAV_FALSE;
55894 }
55895 if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) {
55896 return DRWAV_FALSE;
55897 }
55898 if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
55899 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
55900 DRWAV_ZERO_OBJECT(&pWav->msadpcm);
55901 } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
55902 DRWAV_ZERO_OBJECT(&pWav->ima);
55903 } else {
55904 DRWAV_ASSERT(DRWAV_FALSE);
55905 }
55906 }
55907 pWav->readCursorInPCMFrames = 0;
55908 pWav->bytesRemaining = pWav->dataChunkDataSize;
55909 return DRWAV_TRUE;
55910}
55911DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex)
55912{
55913 if (pWav == NULL || pWav->onSeek == NULL) {
55914 return DRWAV_FALSE;
55915 }
55916 if (pWav->onWrite != NULL) {
55917 return DRWAV_FALSE;
55918 }
55919 if (pWav->totalPCMFrameCount == 0) {
55920 return DRWAV_TRUE;
55921 }
55922 if (targetFrameIndex >= pWav->totalPCMFrameCount) {
55923 targetFrameIndex = pWav->totalPCMFrameCount - 1;
55924 }
55925 if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
55926 if (targetFrameIndex < pWav->readCursorInPCMFrames) {
55927 if (!drwav_seek_to_first_pcm_frame(pWav)) {
55928 return DRWAV_FALSE;
55929 }
55930 }
55931 if (targetFrameIndex > pWav->readCursorInPCMFrames) {
55932 drwav_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames;
55933 drwav_int16 devnull[2048];
55934 while (offsetInFrames > 0) {
55935 drwav_uint64 framesRead = 0;
55936 drwav_uint64 framesToRead = offsetInFrames;
55937 if (framesToRead > drwav_countof(devnull)/pWav->channels) {
55938 framesToRead = drwav_countof(devnull)/pWav->channels;
55939 }
55940 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
55941 framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull);
55942 } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
55943 framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull);
55944 } else {
55945 DRWAV_ASSERT(DRWAV_FALSE);
55946 }
55947 if (framesRead != framesToRead) {
55948 return DRWAV_FALSE;
55949 }
55950 offsetInFrames -= framesRead;
55951 }
55952 }
55953 } else {
55954 drwav_uint64 totalSizeInBytes;
55955 drwav_uint64 currentBytePos;
55956 drwav_uint64 targetBytePos;
55957 drwav_uint64 offset;
55958 totalSizeInBytes = pWav->totalPCMFrameCount * drwav_get_bytes_per_pcm_frame(pWav);
55959 DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining);
55960 currentBytePos = totalSizeInBytes - pWav->bytesRemaining;
55961 targetBytePos = targetFrameIndex * drwav_get_bytes_per_pcm_frame(pWav);
55962 if (currentBytePos < targetBytePos) {
55963 offset = (targetBytePos - currentBytePos);
55964 } else {
55965 if (!drwav_seek_to_first_pcm_frame(pWav)) {
55966 return DRWAV_FALSE;
55967 }
55968 offset = targetBytePos;
55969 }
55970 while (offset > 0) {
55971 int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);
55972 if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) {
55973 return DRWAV_FALSE;
55974 }
55975 pWav->readCursorInPCMFrames += offset32 / drwav_get_bytes_per_pcm_frame(pWav);
55976 pWav->bytesRemaining -= offset32;
55977 offset -= offset32;
55978 }
55979 }
55980 return DRWAV_TRUE;
55981}
55982DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor)
55983{
55984 if (pCursor == NULL) {
55985 return DRWAV_INVALID_ARGS;
55986 }
55987 *pCursor = 0;
55988 if (pWav == NULL) {
55989 return DRWAV_INVALID_ARGS;
55990 }
55991 *pCursor = pWav->readCursorInPCMFrames;
55992 return DRWAV_SUCCESS;
55993}
55994DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength)
55995{
55996 if (pLength == NULL) {
55997 return DRWAV_INVALID_ARGS;
55998 }
55999 *pLength = 0;
56000 if (pWav == NULL) {
56001 return DRWAV_INVALID_ARGS;
56002 }
56003 *pLength = pWav->totalPCMFrameCount;
56004 return DRWAV_SUCCESS;
56005}
56006DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData)
56007{
56008 size_t bytesWritten;
56009 if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {
56010 return 0;
56011 }
56012 bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);
56013 pWav->dataChunkDataSize += bytesWritten;
56014 return bytesWritten;
56015}
56016DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
56017{
56018 drwav_uint64 bytesToWrite;
56019 drwav_uint64 bytesWritten;
56020 const drwav_uint8* pRunningData;
56021 if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
56022 return 0;
56023 }
56024 bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
56025 if (bytesToWrite > DRWAV_SIZE_MAX) {
56026 return 0;
56027 }
56028 bytesWritten = 0;
56029 pRunningData = (const drwav_uint8*)pData;
56030 while (bytesToWrite > 0) {
56031 size_t bytesJustWritten;
56032 drwav_uint64 bytesToWriteThisIteration;
56033 bytesToWriteThisIteration = bytesToWrite;
56034 DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX);
56035 bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData);
56036 if (bytesJustWritten == 0) {
56037 break;
56038 }
56039 bytesToWrite -= bytesJustWritten;
56040 bytesWritten += bytesJustWritten;
56041 pRunningData += bytesJustWritten;
56042 }
56043 return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
56044}
56045DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
56046{
56047 drwav_uint64 bytesToWrite;
56048 drwav_uint64 bytesWritten;
56049 drwav_uint32 bytesPerSample;
56050 const drwav_uint8* pRunningData;
56051 if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
56052 return 0;
56053 }
56054 bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
56055 if (bytesToWrite > DRWAV_SIZE_MAX) {
56056 return 0;
56057 }
56058 bytesWritten = 0;
56059 pRunningData = (const drwav_uint8*)pData;
56060 bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels;
56061 while (bytesToWrite > 0) {
56062 drwav_uint8 temp[4096];
56063 drwav_uint32 sampleCount;
56064 size_t bytesJustWritten;
56065 drwav_uint64 bytesToWriteThisIteration;
56066 bytesToWriteThisIteration = bytesToWrite;
56067 DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX);
56068 sampleCount = sizeof(temp)/bytesPerSample;
56069 if (bytesToWriteThisIteration > ((drwav_uint64)sampleCount)*bytesPerSample) {
56070 bytesToWriteThisIteration = ((drwav_uint64)sampleCount)*bytesPerSample;
56071 }
56072 DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration);
56073 drwav__bswap_samples(temp, sampleCount, bytesPerSample, pWav->translatedFormatTag);
56074 bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp);
56075 if (bytesJustWritten == 0) {
56076 break;
56077 }
56078 bytesToWrite -= bytesJustWritten;
56079 bytesWritten += bytesJustWritten;
56080 pRunningData += bytesJustWritten;
56081 }
56082 return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
56083}
56084DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
56085{
56086 if (drwav__is_little_endian()) {
56087 return drwav_write_pcm_frames_le(pWav, framesToWrite, pData);
56088 } else {
56089 return drwav_write_pcm_frames_be(pWav, framesToWrite, pData);
56090 }
56091}
56092DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
56093{
56094 drwav_uint64 totalFramesRead = 0;
56095 DRWAV_ASSERT(pWav != NULL);
56096 DRWAV_ASSERT(framesToRead > 0);
56097 while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
56098 DRWAV_ASSERT(framesToRead > 0);
56099 if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) {
56100 if (pWav->channels == 1) {
56101 drwav_uint8 header[7];
56102 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
56103 return totalFramesRead;
56104 }
56105 pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
56106 pWav->msadpcm.predictor[0] = header[0];
56107 pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 1);
56108 pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 3);
56109 pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 5);
56110 pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0];
56111 pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1];
56112 pWav->msadpcm.cachedFrameCount = 2;
56113 } else {
56114 drwav_uint8 header[14];
56115 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
56116 return totalFramesRead;
56117 }
56118 pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
56119 pWav->msadpcm.predictor[0] = header[0];
56120 pWav->msadpcm.predictor[1] = header[1];
56121 pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 2);
56122 pWav->msadpcm.delta[1] = drwav_bytes_to_s16(header + 4);
56123 pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 6);
56124 pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav_bytes_to_s16(header + 8);
56125 pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 10);
56126 pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav_bytes_to_s16(header + 12);
56127 pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0];
56128 pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0];
56129 pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1];
56130 pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1];
56131 pWav->msadpcm.cachedFrameCount = 2;
56132 }
56133 }
56134 while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
56135 if (pBufferOut != NULL) {
56136 drwav_uint32 iSample = 0;
56137 for (iSample = 0; iSample < pWav->channels; iSample += 1) {
56138 pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample];
56139 }
56140 pBufferOut += pWav->channels;
56141 }
56142 framesToRead -= 1;
56143 totalFramesRead += 1;
56144 pWav->readCursorInPCMFrames += 1;
56145 pWav->msadpcm.cachedFrameCount -= 1;
56146 }
56147 if (framesToRead == 0) {
56148 break;
56149 }
56150 if (pWav->msadpcm.cachedFrameCount == 0) {
56151 if (pWav->msadpcm.bytesRemainingInBlock == 0) {
56152 continue;
56153 } else {
56154 static drwav_int32 adaptationTable[] = {
56155 230, 230, 230, 230, 307, 409, 512, 614,
56156 768, 614, 512, 409, 307, 230, 230, 230
56157 };
56158 static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
56159 static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
56160 drwav_uint8 nibbles;
56161 drwav_int32 nibble0;
56162 drwav_int32 nibble1;
56163 if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {
56164 return totalFramesRead;
56165 }
56166 pWav->msadpcm.bytesRemainingInBlock -= 1;
56167 nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }
56168 nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }
56169 if (pWav->channels == 1) {
56170 drwav_int32 newSample0;
56171 drwav_int32 newSample1;
56172 newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
56173 newSample0 += nibble0 * pWav->msadpcm.delta[0];
56174 newSample0 = drwav_clamp(newSample0, -32768, 32767);
56175 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
56176 if (pWav->msadpcm.delta[0] < 16) {
56177 pWav->msadpcm.delta[0] = 16;
56178 }
56179 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
56180 pWav->msadpcm.prevFrames[0][1] = newSample0;
56181 newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
56182 newSample1 += nibble1 * pWav->msadpcm.delta[0];
56183 newSample1 = drwav_clamp(newSample1, -32768, 32767);
56184 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;
56185 if (pWav->msadpcm.delta[0] < 16) {
56186 pWav->msadpcm.delta[0] = 16;
56187 }
56188 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
56189 pWav->msadpcm.prevFrames[0][1] = newSample1;
56190 pWav->msadpcm.cachedFrames[2] = newSample0;
56191 pWav->msadpcm.cachedFrames[3] = newSample1;
56192 pWav->msadpcm.cachedFrameCount = 2;
56193 } else {
56194 drwav_int32 newSample0;
56195 drwav_int32 newSample1;
56196 newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
56197 newSample0 += nibble0 * pWav->msadpcm.delta[0];
56198 newSample0 = drwav_clamp(newSample0, -32768, 32767);
56199 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
56200 if (pWav->msadpcm.delta[0] < 16) {
56201 pWav->msadpcm.delta[0] = 16;
56202 }
56203 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
56204 pWav->msadpcm.prevFrames[0][1] = newSample0;
56205 newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
56206 newSample1 += nibble1 * pWav->msadpcm.delta[1];
56207 newSample1 = drwav_clamp(newSample1, -32768, 32767);
56208 pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;
56209 if (pWav->msadpcm.delta[1] < 16) {
56210 pWav->msadpcm.delta[1] = 16;
56211 }
56212 pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1];
56213 pWav->msadpcm.prevFrames[1][1] = newSample1;
56214 pWav->msadpcm.cachedFrames[2] = newSample0;
56215 pWav->msadpcm.cachedFrames[3] = newSample1;
56216 pWav->msadpcm.cachedFrameCount = 1;
56217 }
56218 }
56219 }
56220 }
56221 return totalFramesRead;
56222}
56223DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
56224{
56225 drwav_uint64 totalFramesRead = 0;
56226 drwav_uint32 iChannel;
56227 static drwav_int32 indexTable[16] = {
56228 -1, -1, -1, -1, 2, 4, 6, 8,
56229 -1, -1, -1, -1, 2, 4, 6, 8
56230 };
56231 static drwav_int32 stepTable[89] = {
56232 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
56233 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
56234 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
56235 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
56236 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
56237 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
56238 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
56239 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
56240 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
56241 };
56242 DRWAV_ASSERT(pWav != NULL);
56243 DRWAV_ASSERT(framesToRead > 0);
56244 while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
56245 DRWAV_ASSERT(framesToRead > 0);
56246 if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) {
56247 if (pWav->channels == 1) {
56248 drwav_uint8 header[4];
56249 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
56250 return totalFramesRead;
56251 }
56252 pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
56253 if (header[2] >= drwav_countof(stepTable)) {
56254 pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current);
56255 pWav->ima.bytesRemainingInBlock = 0;
56256 return totalFramesRead;
56257 }
56258 pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0);
56259 pWav->ima.stepIndex[0] = header[2];
56260 pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0];
56261 pWav->ima.cachedFrameCount = 1;
56262 } else {
56263 drwav_uint8 header[8];
56264 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
56265 return totalFramesRead;
56266 }
56267 pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
56268 if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) {
56269 pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current);
56270 pWav->ima.bytesRemainingInBlock = 0;
56271 return totalFramesRead;
56272 }
56273 pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0);
56274 pWav->ima.stepIndex[0] = header[2];
56275 pWav->ima.predictor[1] = drwav_bytes_to_s16(header + 4);
56276 pWav->ima.stepIndex[1] = header[6];
56277 pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0];
56278 pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1];
56279 pWav->ima.cachedFrameCount = 1;
56280 }
56281 }
56282 while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
56283 if (pBufferOut != NULL) {
56284 drwav_uint32 iSample;
56285 for (iSample = 0; iSample < pWav->channels; iSample += 1) {
56286 pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample];
56287 }
56288 pBufferOut += pWav->channels;
56289 }
56290 framesToRead -= 1;
56291 totalFramesRead += 1;
56292 pWav->readCursorInPCMFrames += 1;
56293 pWav->ima.cachedFrameCount -= 1;
56294 }
56295 if (framesToRead == 0) {
56296 break;
56297 }
56298 if (pWav->ima.cachedFrameCount == 0) {
56299 if (pWav->ima.bytesRemainingInBlock == 0) {
56300 continue;
56301 } else {
56302 pWav->ima.cachedFrameCount = 8;
56303 for (iChannel = 0; iChannel < pWav->channels; ++iChannel) {
56304 drwav_uint32 iByte;
56305 drwav_uint8 nibbles[4];
56306 if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) {
56307 pWav->ima.cachedFrameCount = 0;
56308 return totalFramesRead;
56309 }
56310 pWav->ima.bytesRemainingInBlock -= 4;
56311 for (iByte = 0; iByte < 4; ++iByte) {
56312 drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0);
56313 drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4);
56314 drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]];
56315 drwav_int32 predictor = pWav->ima.predictor[iChannel];
56316 drwav_int32 diff = step >> 3;
56317 if (nibble0 & 1) diff += step >> 2;
56318 if (nibble0 & 2) diff += step >> 1;
56319 if (nibble0 & 4) diff += step;
56320 if (nibble0 & 8) diff = -diff;
56321 predictor = drwav_clamp(predictor + diff, -32768, 32767);
56322 pWav->ima.predictor[iChannel] = predictor;
56323 pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1);
56324 pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor;
56325 step = stepTable[pWav->ima.stepIndex[iChannel]];
56326 predictor = pWav->ima.predictor[iChannel];
56327 diff = step >> 3;
56328 if (nibble1 & 1) diff += step >> 2;
56329 if (nibble1 & 2) diff += step >> 1;
56330 if (nibble1 & 4) diff += step;
56331 if (nibble1 & 8) diff = -diff;
56332 predictor = drwav_clamp(predictor + diff, -32768, 32767);
56333 pWav->ima.predictor[iChannel] = predictor;
56334 pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1);
56335 pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor;
56336 }
56337 }
56338 }
56339 }
56340 }
56341 return totalFramesRead;
56342}
56343#ifndef DR_WAV_NO_CONVERSION_API
56344static unsigned short g_drwavAlawTable[256] = {
56345 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
56346 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
56347 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
56348 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,
56349 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,
56350 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,
56351 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,
56352 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,
56353 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,
56354 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,
56355 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,
56356 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,
56357 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,
56358 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,
56359 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,
56360 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
56361};
56362static unsigned short g_drwavMulawTable[256] = {
56363 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
56364 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
56365 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
56366 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,
56367 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,
56368 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,
56369 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,
56370 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,
56371 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,
56372 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,
56373 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,
56374 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,
56375 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,
56376 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,
56377 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,
56378 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
56379};
56380static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn)
56381{
56382 return (short)g_drwavAlawTable[sampleIn];
56383}
56384static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn)
56385{
56386 return (short)g_drwavMulawTable[sampleIn];
56387}
56388DRWAV_PRIVATE void drwav__pcm_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
56389{
56390 unsigned int i;
56391 if (bytesPerSample == 1) {
56392 drwav_u8_to_s16(pOut, pIn, totalSampleCount);
56393 return;
56394 }
56395 if (bytesPerSample == 2) {
56396 for (i = 0; i < totalSampleCount; ++i) {
56397 *pOut++ = ((const drwav_int16*)pIn)[i];
56398 }
56399 return;
56400 }
56401 if (bytesPerSample == 3) {
56402 drwav_s24_to_s16(pOut, pIn, totalSampleCount);
56403 return;
56404 }
56405 if (bytesPerSample == 4) {
56406 drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount);
56407 return;
56408 }
56409 if (bytesPerSample > 8) {
56410 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
56411 return;
56412 }
56413 for (i = 0; i < totalSampleCount; ++i) {
56414 drwav_uint64 sample = 0;
56415 unsigned int shift = (8 - bytesPerSample) * 8;
56416 unsigned int j;
56417 for (j = 0; j < bytesPerSample; j += 1) {
56418 DRWAV_ASSERT(j < 8);
56419 sample |= (drwav_uint64)(pIn[j]) << shift;
56420 shift += 8;
56421 }
56422 pIn += j;
56423 *pOut++ = (drwav_int16)((drwav_int64)sample >> 48);
56424 }
56425}
56426DRWAV_PRIVATE void drwav__ieee_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
56427{
56428 if (bytesPerSample == 4) {
56429 drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount);
56430 return;
56431 } else if (bytesPerSample == 8) {
56432 drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount);
56433 return;
56434 } else {
56435 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
56436 return;
56437 }
56438}
56439DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
56440{
56441 drwav_uint64 totalFramesRead;
56442 drwav_uint8 sampleData[4096];
56443 drwav_uint32 bytesPerFrame;
56444 if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) {
56445 return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
56446 }
56447 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
56448 if (bytesPerFrame == 0) {
56449 return 0;
56450 }
56451 totalFramesRead = 0;
56452 while (framesToRead > 0) {
56453 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
56454 if (framesRead == 0) {
56455 break;
56456 }
56457 drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);
56458 pBufferOut += framesRead*pWav->channels;
56459 framesToRead -= framesRead;
56460 totalFramesRead += framesRead;
56461 }
56462 return totalFramesRead;
56463}
56464DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
56465{
56466 drwav_uint64 totalFramesRead;
56467 drwav_uint8 sampleData[4096];
56468 drwav_uint32 bytesPerFrame;
56469 if (pBufferOut == NULL) {
56470 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
56471 }
56472 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
56473 if (bytesPerFrame == 0) {
56474 return 0;
56475 }
56476 totalFramesRead = 0;
56477 while (framesToRead > 0) {
56478 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
56479 if (framesRead == 0) {
56480 break;
56481 }
56482 drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);
56483 pBufferOut += framesRead*pWav->channels;
56484 framesToRead -= framesRead;
56485 totalFramesRead += framesRead;
56486 }
56487 return totalFramesRead;
56488}
56489DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
56490{
56491 drwav_uint64 totalFramesRead;
56492 drwav_uint8 sampleData[4096];
56493 drwav_uint32 bytesPerFrame;
56494 if (pBufferOut == NULL) {
56495 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
56496 }
56497 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
56498 if (bytesPerFrame == 0) {
56499 return 0;
56500 }
56501 totalFramesRead = 0;
56502 while (framesToRead > 0) {
56503 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
56504 if (framesRead == 0) {
56505 break;
56506 }
56507 drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
56508 pBufferOut += framesRead*pWav->channels;
56509 framesToRead -= framesRead;
56510 totalFramesRead += framesRead;
56511 }
56512 return totalFramesRead;
56513}
56514DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
56515{
56516 drwav_uint64 totalFramesRead;
56517 drwav_uint8 sampleData[4096];
56518 drwav_uint32 bytesPerFrame;
56519 if (pBufferOut == NULL) {
56520 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
56521 }
56522 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
56523 if (bytesPerFrame == 0) {
56524 return 0;
56525 }
56526 totalFramesRead = 0;
56527 while (framesToRead > 0) {
56528 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
56529 if (framesRead == 0) {
56530 break;
56531 }
56532 drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
56533 pBufferOut += framesRead*pWav->channels;
56534 framesToRead -= framesRead;
56535 totalFramesRead += framesRead;
56536 }
56537 return totalFramesRead;
56538}
56539DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
56540{
56541 if (pWav == NULL || framesToRead == 0) {
56542 return 0;
56543 }
56544 if (pBufferOut == NULL) {
56545 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
56546 }
56547 if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) {
56548 framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels;
56549 }
56550 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
56551 return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut);
56552 }
56553 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
56554 return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut);
56555 }
56556 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
56557 return drwav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut);
56558 }
56559 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
56560 return drwav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut);
56561 }
56562 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
56563 return drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut);
56564 }
56565 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
56566 return drwav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut);
56567 }
56568 return 0;
56569}
56570DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
56571{
56572 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
56573 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
56574 drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
56575 }
56576 return framesRead;
56577}
56578DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
56579{
56580 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
56581 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
56582 drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
56583 }
56584 return framesRead;
56585}
56586DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
56587{
56588 int r;
56589 size_t i;
56590 for (i = 0; i < sampleCount; ++i) {
56591 int x = pIn[i];
56592 r = x << 8;
56593 r = r - 32768;
56594 pOut[i] = (short)r;
56595 }
56596}
56597DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
56598{
56599 int r;
56600 size_t i;
56601 for (i = 0; i < sampleCount; ++i) {
56602 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;
56603 r = x >> 8;
56604 pOut[i] = (short)r;
56605 }
56606}
56607DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount)
56608{
56609 int r;
56610 size_t i;
56611 for (i = 0; i < sampleCount; ++i) {
56612 int x = pIn[i];
56613 r = x >> 16;
56614 pOut[i] = (short)r;
56615 }
56616}
56617DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount)
56618{
56619 int r;
56620 size_t i;
56621 for (i = 0; i < sampleCount; ++i) {
56622 float x = pIn[i];
56623 float c;
56624 c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
56625 c = c + 1;
56626 r = (int)(c * 32767.5f);
56627 r = r - 32768;
56628 pOut[i] = (short)r;
56629 }
56630}
56631DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount)
56632{
56633 int r;
56634 size_t i;
56635 for (i = 0; i < sampleCount; ++i) {
56636 double x = pIn[i];
56637 double c;
56638 c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
56639 c = c + 1;
56640 r = (int)(c * 32767.5);
56641 r = r - 32768;
56642 pOut[i] = (short)r;
56643 }
56644}
56645DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
56646{
56647 size_t i;
56648 for (i = 0; i < sampleCount; ++i) {
56649 pOut[i] = drwav__alaw_to_s16(pIn[i]);
56650 }
56651}
56652DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
56653{
56654 size_t i;
56655 for (i = 0; i < sampleCount; ++i) {
56656 pOut[i] = drwav__mulaw_to_s16(pIn[i]);
56657 }
56658}
56659DRWAV_PRIVATE void drwav__pcm_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
56660{
56661 unsigned int i;
56662 if (bytesPerSample == 1) {
56663 drwav_u8_to_f32(pOut, pIn, sampleCount);
56664 return;
56665 }
56666 if (bytesPerSample == 2) {
56667 drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount);
56668 return;
56669 }
56670 if (bytesPerSample == 3) {
56671 drwav_s24_to_f32(pOut, pIn, sampleCount);
56672 return;
56673 }
56674 if (bytesPerSample == 4) {
56675 drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount);
56676 return;
56677 }
56678 if (bytesPerSample > 8) {
56679 DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
56680 return;
56681 }
56682 for (i = 0; i < sampleCount; ++i) {
56683 drwav_uint64 sample = 0;
56684 unsigned int shift = (8 - bytesPerSample) * 8;
56685 unsigned int j;
56686 for (j = 0; j < bytesPerSample; j += 1) {
56687 DRWAV_ASSERT(j < 8);
56688 sample |= (drwav_uint64)(pIn[j]) << shift;
56689 shift += 8;
56690 }
56691 pIn += j;
56692 *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0);
56693 }
56694}
56695DRWAV_PRIVATE void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
56696{
56697 if (bytesPerSample == 4) {
56698 unsigned int i;
56699 for (i = 0; i < sampleCount; ++i) {
56700 *pOut++ = ((const float*)pIn)[i];
56701 }
56702 return;
56703 } else if (bytesPerSample == 8) {
56704 drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount);
56705 return;
56706 } else {
56707 DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
56708 return;
56709 }
56710}
56711DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
56712{
56713 drwav_uint64 totalFramesRead;
56714 drwav_uint8 sampleData[4096];
56715 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
56716 if (bytesPerFrame == 0) {
56717 return 0;
56718 }
56719 totalFramesRead = 0;
56720 while (framesToRead > 0) {
56721 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
56722 if (framesRead == 0) {
56723 break;
56724 }
56725 drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)framesRead*pWav->channels, bytesPerFrame/pWav->channels);
56726 pBufferOut += framesRead*pWav->channels;
56727 framesToRead -= framesRead;
56728 totalFramesRead += framesRead;
56729 }
56730 return totalFramesRead;
56731}
56732DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__msadpcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
56733{
56734 drwav_uint64 totalFramesRead = 0;
56735 drwav_int16 samples16[2048];
56736 while (framesToRead > 0) {
56737 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16);
56738 if (framesRead == 0) {
56739 break;
56740 }
56741 drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
56742 pBufferOut += framesRead*pWav->channels;
56743 framesToRead -= framesRead;
56744 totalFramesRead += framesRead;
56745 }
56746 return totalFramesRead;
56747}
56748DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
56749{
56750 drwav_uint64 totalFramesRead = 0;
56751 drwav_int16 samples16[2048];
56752 while (framesToRead > 0) {
56753 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16);
56754 if (framesRead == 0) {
56755 break;
56756 }
56757 drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
56758 pBufferOut += framesRead*pWav->channels;
56759 framesToRead -= framesRead;
56760 totalFramesRead += framesRead;
56761 }
56762 return totalFramesRead;
56763}
56764DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
56765{
56766 drwav_uint64 totalFramesRead;
56767 drwav_uint8 sampleData[4096];
56768 drwav_uint32 bytesPerFrame;
56769 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) {
56770 return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
56771 }
56772 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
56773 if (bytesPerFrame == 0) {
56774 return 0;
56775 }
56776 totalFramesRead = 0;
56777 while (framesToRead > 0) {
56778 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
56779 if (framesRead == 0) {
56780 break;
56781 }
56782 drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);
56783 pBufferOut += framesRead*pWav->channels;
56784 framesToRead -= framesRead;
56785 totalFramesRead += framesRead;
56786 }
56787 return totalFramesRead;
56788}
56789DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
56790{
56791 drwav_uint64 totalFramesRead;
56792 drwav_uint8 sampleData[4096];
56793 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
56794 if (bytesPerFrame == 0) {
56795 return 0;
56796 }
56797 totalFramesRead = 0;
56798 while (framesToRead > 0) {
56799 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
56800 if (framesRead == 0) {
56801 break;
56802 }
56803 drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
56804 pBufferOut += framesRead*pWav->channels;
56805 framesToRead -= framesRead;
56806 totalFramesRead += framesRead;
56807 }
56808 return totalFramesRead;
56809}
56810DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
56811{
56812 drwav_uint64 totalFramesRead;
56813 drwav_uint8 sampleData[4096];
56814 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
56815 if (bytesPerFrame == 0) {
56816 return 0;
56817 }
56818 totalFramesRead = 0;
56819 while (framesToRead > 0) {
56820 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
56821 if (framesRead == 0) {
56822 break;
56823 }
56824 drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
56825 pBufferOut += framesRead*pWav->channels;
56826 framesToRead -= framesRead;
56827 totalFramesRead += framesRead;
56828 }
56829 return totalFramesRead;
56830}
56831DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
56832{
56833 if (pWav == NULL || framesToRead == 0) {
56834 return 0;
56835 }
56836 if (pBufferOut == NULL) {
56837 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
56838 }
56839 if (framesToRead * pWav->channels * sizeof(float) > DRWAV_SIZE_MAX) {
56840 framesToRead = DRWAV_SIZE_MAX / sizeof(float) / pWav->channels;
56841 }
56842 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
56843 return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut);
56844 }
56845 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
56846 return drwav_read_pcm_frames_f32__msadpcm(pWav, framesToRead, pBufferOut);
56847 }
56848 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
56849 return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut);
56850 }
56851 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
56852 return drwav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut);
56853 }
56854 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
56855 return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut);
56856 }
56857 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
56858 return drwav_read_pcm_frames_f32__ima(pWav, framesToRead, pBufferOut);
56859 }
56860 return 0;
56861}
56862DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
56863{
56864 drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
56865 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
56866 drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
56867 }
56868 return framesRead;
56869}
56870DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
56871{
56872 drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
56873 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
56874 drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
56875 }
56876 return framesRead;
56877}
56878DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
56879{
56880 size_t i;
56881 if (pOut == NULL || pIn == NULL) {
56882 return;
56883 }
56884#ifdef DR_WAV_LIBSNDFILE_COMPAT
56885 for (i = 0; i < sampleCount; ++i) {
56886 *pOut++ = (pIn[i] / 256.0f) * 2 - 1;
56887 }
56888#else
56889 for (i = 0; i < sampleCount; ++i) {
56890 float x = pIn[i];
56891 x = x * 0.00784313725490196078f;
56892 x = x - 1;
56893 *pOut++ = x;
56894 }
56895#endif
56896}
56897DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount)
56898{
56899 size_t i;
56900 if (pOut == NULL || pIn == NULL) {
56901 return;
56902 }
56903 for (i = 0; i < sampleCount; ++i) {
56904 *pOut++ = pIn[i] * 0.000030517578125f;
56905 }
56906}
56907DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
56908{
56909 size_t i;
56910 if (pOut == NULL || pIn == NULL) {
56911 return;
56912 }
56913 for (i = 0; i < sampleCount; ++i) {
56914 double x;
56915 drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) << 8);
56916 drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16);
56917 drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24);
56918 x = (double)((drwav_int32)(a | b | c) >> 8);
56919 *pOut++ = (float)(x * 0.00000011920928955078125);
56920 }
56921}
56922DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount)
56923{
56924 size_t i;
56925 if (pOut == NULL || pIn == NULL) {
56926 return;
56927 }
56928 for (i = 0; i < sampleCount; ++i) {
56929 *pOut++ = (float)(pIn[i] / 2147483648.0);
56930 }
56931}
56932DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount)
56933{
56934 size_t i;
56935 if (pOut == NULL || pIn == NULL) {
56936 return;
56937 }
56938 for (i = 0; i < sampleCount; ++i) {
56939 *pOut++ = (float)pIn[i];
56940 }
56941}
56942DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
56943{
56944 size_t i;
56945 if (pOut == NULL || pIn == NULL) {
56946 return;
56947 }
56948 for (i = 0; i < sampleCount; ++i) {
56949 *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f;
56950 }
56951}
56952DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
56953{
56954 size_t i;
56955 if (pOut == NULL || pIn == NULL) {
56956 return;
56957 }
56958 for (i = 0; i < sampleCount; ++i) {
56959 *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f;
56960 }
56961}
56962DRWAV_PRIVATE void drwav__pcm_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
56963{
56964 unsigned int i;
56965 if (bytesPerSample == 1) {
56966 drwav_u8_to_s32(pOut, pIn, totalSampleCount);
56967 return;
56968 }
56969 if (bytesPerSample == 2) {
56970 drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount);
56971 return;
56972 }
56973 if (bytesPerSample == 3) {
56974 drwav_s24_to_s32(pOut, pIn, totalSampleCount);
56975 return;
56976 }
56977 if (bytesPerSample == 4) {
56978 for (i = 0; i < totalSampleCount; ++i) {
56979 *pOut++ = ((const drwav_int32*)pIn)[i];
56980 }
56981 return;
56982 }
56983 if (bytesPerSample > 8) {
56984 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
56985 return;
56986 }
56987 for (i = 0; i < totalSampleCount; ++i) {
56988 drwav_uint64 sample = 0;
56989 unsigned int shift = (8 - bytesPerSample) * 8;
56990 unsigned int j;
56991 for (j = 0; j < bytesPerSample; j += 1) {
56992 DRWAV_ASSERT(j < 8);
56993 sample |= (drwav_uint64)(pIn[j]) << shift;
56994 shift += 8;
56995 }
56996 pIn += j;
56997 *pOut++ = (drwav_int32)((drwav_int64)sample >> 32);
56998 }
56999}
57000DRWAV_PRIVATE void drwav__ieee_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
57001{
57002 if (bytesPerSample == 4) {
57003 drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount);
57004 return;
57005 } else if (bytesPerSample == 8) {
57006 drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount);
57007 return;
57008 } else {
57009 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
57010 return;
57011 }
57012}
57013DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
57014{
57015 drwav_uint64 totalFramesRead;
57016 drwav_uint8 sampleData[4096];
57017 drwav_uint32 bytesPerFrame;
57018 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) {
57019 return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
57020 }
57021 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
57022 if (bytesPerFrame == 0) {
57023 return 0;
57024 }
57025 totalFramesRead = 0;
57026 while (framesToRead > 0) {
57027 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
57028 if (framesRead == 0) {
57029 break;
57030 }
57031 drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);
57032 pBufferOut += framesRead*pWav->channels;
57033 framesToRead -= framesRead;
57034 totalFramesRead += framesRead;
57035 }
57036 return totalFramesRead;
57037}
57038DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
57039{
57040 drwav_uint64 totalFramesRead = 0;
57041 drwav_int16 samples16[2048];
57042 while (framesToRead > 0) {
57043 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16);
57044 if (framesRead == 0) {
57045 break;
57046 }
57047 drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
57048 pBufferOut += framesRead*pWav->channels;
57049 framesToRead -= framesRead;
57050 totalFramesRead += framesRead;
57051 }
57052 return totalFramesRead;
57053}
57054DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
57055{
57056 drwav_uint64 totalFramesRead = 0;
57057 drwav_int16 samples16[2048];
57058 while (framesToRead > 0) {
57059 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16);
57060 if (framesRead == 0) {
57061 break;
57062 }
57063 drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
57064 pBufferOut += framesRead*pWav->channels;
57065 framesToRead -= framesRead;
57066 totalFramesRead += framesRead;
57067 }
57068 return totalFramesRead;
57069}
57070DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
57071{
57072 drwav_uint64 totalFramesRead;
57073 drwav_uint8 sampleData[4096];
57074 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
57075 if (bytesPerFrame == 0) {
57076 return 0;
57077 }
57078 totalFramesRead = 0;
57079 while (framesToRead > 0) {
57080 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
57081 if (framesRead == 0) {
57082 break;
57083 }
57084 drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);
57085 pBufferOut += framesRead*pWav->channels;
57086 framesToRead -= framesRead;
57087 totalFramesRead += framesRead;
57088 }
57089 return totalFramesRead;
57090}
57091DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
57092{
57093 drwav_uint64 totalFramesRead;
57094 drwav_uint8 sampleData[4096];
57095 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
57096 if (bytesPerFrame == 0) {
57097 return 0;
57098 }
57099 totalFramesRead = 0;
57100 while (framesToRead > 0) {
57101 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
57102 if (framesRead == 0) {
57103 break;
57104 }
57105 drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
57106 pBufferOut += framesRead*pWav->channels;
57107 framesToRead -= framesRead;
57108 totalFramesRead += framesRead;
57109 }
57110 return totalFramesRead;
57111}
57112DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
57113{
57114 drwav_uint64 totalFramesRead;
57115 drwav_uint8 sampleData[4096];
57116 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
57117 if (bytesPerFrame == 0) {
57118 return 0;
57119 }
57120 totalFramesRead = 0;
57121 while (framesToRead > 0) {
57122 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
57123 if (framesRead == 0) {
57124 break;
57125 }
57126 drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
57127 pBufferOut += framesRead*pWav->channels;
57128 framesToRead -= framesRead;
57129 totalFramesRead += framesRead;
57130 }
57131 return totalFramesRead;
57132}
57133DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
57134{
57135 if (pWav == NULL || framesToRead == 0) {
57136 return 0;
57137 }
57138 if (pBufferOut == NULL) {
57139 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
57140 }
57141 if (framesToRead * pWav->channels * sizeof(drwav_int32) > DRWAV_SIZE_MAX) {
57142 framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32) / pWav->channels;
57143 }
57144 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
57145 return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut);
57146 }
57147 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
57148 return drwav_read_pcm_frames_s32__msadpcm(pWav, framesToRead, pBufferOut);
57149 }
57150 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
57151 return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut);
57152 }
57153 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
57154 return drwav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut);
57155 }
57156 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
57157 return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut);
57158 }
57159 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
57160 return drwav_read_pcm_frames_s32__ima(pWav, framesToRead, pBufferOut);
57161 }
57162 return 0;
57163}
57164DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
57165{
57166 drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
57167 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
57168 drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
57169 }
57170 return framesRead;
57171}
57172DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
57173{
57174 drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
57175 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
57176 drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
57177 }
57178 return framesRead;
57179}
57180DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
57181{
57182 size_t i;
57183 if (pOut == NULL || pIn == NULL) {
57184 return;
57185 }
57186 for (i = 0; i < sampleCount; ++i) {
57187 *pOut++ = ((int)pIn[i] - 128) << 24;
57188 }
57189}
57190DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount)
57191{
57192 size_t i;
57193 if (pOut == NULL || pIn == NULL) {
57194 return;
57195 }
57196 for (i = 0; i < sampleCount; ++i) {
57197 *pOut++ = pIn[i] << 16;
57198 }
57199}
57200DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
57201{
57202 size_t i;
57203 if (pOut == NULL || pIn == NULL) {
57204 return;
57205 }
57206 for (i = 0; i < sampleCount; ++i) {
57207 unsigned int s0 = pIn[i*3 + 0];
57208 unsigned int s1 = pIn[i*3 + 1];
57209 unsigned int s2 = pIn[i*3 + 2];
57210 drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));
57211 *pOut++ = sample32;
57212 }
57213}
57214DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount)
57215{
57216 size_t i;
57217 if (pOut == NULL || pIn == NULL) {
57218 return;
57219 }
57220 for (i = 0; i < sampleCount; ++i) {
57221 *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
57222 }
57223}
57224DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount)
57225{
57226 size_t i;
57227 if (pOut == NULL || pIn == NULL) {
57228 return;
57229 }
57230 for (i = 0; i < sampleCount; ++i) {
57231 *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
57232 }
57233}
57234DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
57235{
57236 size_t i;
57237 if (pOut == NULL || pIn == NULL) {
57238 return;
57239 }
57240 for (i = 0; i < sampleCount; ++i) {
57241 *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16;
57242 }
57243}
57244DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
57245{
57246 size_t i;
57247 if (pOut == NULL || pIn == NULL) {
57248 return;
57249 }
57250 for (i= 0; i < sampleCount; ++i) {
57251 *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16;
57252 }
57253}
57254DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
57255{
57256 drwav_uint64 sampleDataSize;
57257 drwav_int16* pSampleData;
57258 drwav_uint64 framesRead;
57259 DRWAV_ASSERT(pWav != NULL);
57260 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16);
57261 if (sampleDataSize > DRWAV_SIZE_MAX) {
57262 drwav_uninit(pWav);
57263 return NULL;
57264 }
57265 pSampleData = (drwav_int16*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
57266 if (pSampleData == NULL) {
57267 drwav_uninit(pWav);
57268 return NULL;
57269 }
57270 framesRead = drwav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
57271 if (framesRead != pWav->totalPCMFrameCount) {
57272 drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
57273 drwav_uninit(pWav);
57274 return NULL;
57275 }
57276 drwav_uninit(pWav);
57277 if (sampleRate) {
57278 *sampleRate = pWav->sampleRate;
57279 }
57280 if (channels) {
57281 *channels = pWav->channels;
57282 }
57283 if (totalFrameCount) {
57284 *totalFrameCount = pWav->totalPCMFrameCount;
57285 }
57286 return pSampleData;
57287}
57288DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
57289{
57290 drwav_uint64 sampleDataSize;
57291 float* pSampleData;
57292 drwav_uint64 framesRead;
57293 DRWAV_ASSERT(pWav != NULL);
57294 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float);
57295 if (sampleDataSize > DRWAV_SIZE_MAX) {
57296 drwav_uninit(pWav);
57297 return NULL;
57298 }
57299 pSampleData = (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
57300 if (pSampleData == NULL) {
57301 drwav_uninit(pWav);
57302 return NULL;
57303 }
57304 framesRead = drwav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
57305 if (framesRead != pWav->totalPCMFrameCount) {
57306 drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
57307 drwav_uninit(pWav);
57308 return NULL;
57309 }
57310 drwav_uninit(pWav);
57311 if (sampleRate) {
57312 *sampleRate = pWav->sampleRate;
57313 }
57314 if (channels) {
57315 *channels = pWav->channels;
57316 }
57317 if (totalFrameCount) {
57318 *totalFrameCount = pWav->totalPCMFrameCount;
57319 }
57320 return pSampleData;
57321}
57322DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
57323{
57324 drwav_uint64 sampleDataSize;
57325 drwav_int32* pSampleData;
57326 drwav_uint64 framesRead;
57327 DRWAV_ASSERT(pWav != NULL);
57328 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32);
57329 if (sampleDataSize > DRWAV_SIZE_MAX) {
57330 drwav_uninit(pWav);
57331 return NULL;
57332 }
57333 pSampleData = (drwav_int32*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
57334 if (pSampleData == NULL) {
57335 drwav_uninit(pWav);
57336 return NULL;
57337 }
57338 framesRead = drwav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
57339 if (framesRead != pWav->totalPCMFrameCount) {
57340 drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
57341 drwav_uninit(pWav);
57342 return NULL;
57343 }
57344 drwav_uninit(pWav);
57345 if (sampleRate) {
57346 *sampleRate = pWav->sampleRate;
57347 }
57348 if (channels) {
57349 *channels = pWav->channels;
57350 }
57351 if (totalFrameCount) {
57352 *totalFrameCount = pWav->totalPCMFrameCount;
57353 }
57354 return pSampleData;
57355}
57356DRWAV_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)
57357{
57358 drwav wav;
57359 if (channelsOut) {
57360 *channelsOut = 0;
57361 }
57362 if (sampleRateOut) {
57363 *sampleRateOut = 0;
57364 }
57365 if (totalFrameCountOut) {
57366 *totalFrameCountOut = 0;
57367 }
57368 if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
57369 return NULL;
57370 }
57371 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
57372}
57373DRWAV_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)
57374{
57375 drwav wav;
57376 if (channelsOut) {
57377 *channelsOut = 0;
57378 }
57379 if (sampleRateOut) {
57380 *sampleRateOut = 0;
57381 }
57382 if (totalFrameCountOut) {
57383 *totalFrameCountOut = 0;
57384 }
57385 if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
57386 return NULL;
57387 }
57388 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
57389}
57390DRWAV_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)
57391{
57392 drwav wav;
57393 if (channelsOut) {
57394 *channelsOut = 0;
57395 }
57396 if (sampleRateOut) {
57397 *sampleRateOut = 0;
57398 }
57399 if (totalFrameCountOut) {
57400 *totalFrameCountOut = 0;
57401 }
57402 if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
57403 return NULL;
57404 }
57405 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
57406}
57407#ifndef DR_WAV_NO_STDIO
57408DRWAV_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)
57409{
57410 drwav wav;
57411 if (channelsOut) {
57412 *channelsOut = 0;
57413 }
57414 if (sampleRateOut) {
57415 *sampleRateOut = 0;
57416 }
57417 if (totalFrameCountOut) {
57418 *totalFrameCountOut = 0;
57419 }
57420 if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
57421 return NULL;
57422 }
57423 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
57424}
57425DRWAV_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)
57426{
57427 drwav wav;
57428 if (channelsOut) {
57429 *channelsOut = 0;
57430 }
57431 if (sampleRateOut) {
57432 *sampleRateOut = 0;
57433 }
57434 if (totalFrameCountOut) {
57435 *totalFrameCountOut = 0;
57436 }
57437 if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
57438 return NULL;
57439 }
57440 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
57441}
57442DRWAV_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)
57443{
57444 drwav wav;
57445 if (channelsOut) {
57446 *channelsOut = 0;
57447 }
57448 if (sampleRateOut) {
57449 *sampleRateOut = 0;
57450 }
57451 if (totalFrameCountOut) {
57452 *totalFrameCountOut = 0;
57453 }
57454 if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
57455 return NULL;
57456 }
57457 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
57458}
57459DRWAV_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)
57460{
57461 drwav wav;
57462 if (sampleRateOut) {
57463 *sampleRateOut = 0;
57464 }
57465 if (channelsOut) {
57466 *channelsOut = 0;
57467 }
57468 if (totalFrameCountOut) {
57469 *totalFrameCountOut = 0;
57470 }
57471 if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
57472 return NULL;
57473 }
57474 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
57475}
57476DRWAV_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)
57477{
57478 drwav wav;
57479 if (sampleRateOut) {
57480 *sampleRateOut = 0;
57481 }
57482 if (channelsOut) {
57483 *channelsOut = 0;
57484 }
57485 if (totalFrameCountOut) {
57486 *totalFrameCountOut = 0;
57487 }
57488 if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
57489 return NULL;
57490 }
57491 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
57492}
57493DRWAV_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)
57494{
57495 drwav wav;
57496 if (sampleRateOut) {
57497 *sampleRateOut = 0;
57498 }
57499 if (channelsOut) {
57500 *channelsOut = 0;
57501 }
57502 if (totalFrameCountOut) {
57503 *totalFrameCountOut = 0;
57504 }
57505 if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
57506 return NULL;
57507 }
57508 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
57509}
57510#endif
57511DRWAV_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)
57512{
57513 drwav wav;
57514 if (channelsOut) {
57515 *channelsOut = 0;
57516 }
57517 if (sampleRateOut) {
57518 *sampleRateOut = 0;
57519 }
57520 if (totalFrameCountOut) {
57521 *totalFrameCountOut = 0;
57522 }
57523 if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
57524 return NULL;
57525 }
57526 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
57527}
57528DRWAV_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)
57529{
57530 drwav wav;
57531 if (channelsOut) {
57532 *channelsOut = 0;
57533 }
57534 if (sampleRateOut) {
57535 *sampleRateOut = 0;
57536 }
57537 if (totalFrameCountOut) {
57538 *totalFrameCountOut = 0;
57539 }
57540 if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
57541 return NULL;
57542 }
57543 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
57544}
57545DRWAV_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)
57546{
57547 drwav wav;
57548 if (channelsOut) {
57549 *channelsOut = 0;
57550 }
57551 if (sampleRateOut) {
57552 *sampleRateOut = 0;
57553 }
57554 if (totalFrameCountOut) {
57555 *totalFrameCountOut = 0;
57556 }
57557 if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
57558 return NULL;
57559 }
57560 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
57561}
57562#endif
57563DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks)
57564{
57565 if (pAllocationCallbacks != NULL) {
57566 drwav__free_from_callbacks(p, pAllocationCallbacks);
57567 } else {
57568 drwav__free_default(p, NULL);
57569 }
57570}
57571DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data)
57572{
57573 return ((drwav_uint16)data[0] << 0) | ((drwav_uint16)data[1] << 8);
57574}
57575DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data)
57576{
57577 return (drwav_int16)drwav_bytes_to_u16(data);
57578}
57579DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data)
57580{
57581 return ((drwav_uint32)data[0] << 0) | ((drwav_uint32)data[1] << 8) | ((drwav_uint32)data[2] << 16) | ((drwav_uint32)data[3] << 24);
57582}
57583DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data)
57584{
57585 union {
57586 drwav_uint32 u32;
57587 float f32;
57588 } value;
57589 value.u32 = drwav_bytes_to_u32(data);
57590 return value.f32;
57591}
57592DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data)
57593{
57594 return (drwav_int32)drwav_bytes_to_u32(data);
57595}
57596DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data)
57597{
57598 return
57599 ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) |
57600 ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56);
57601}
57602DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data)
57603{
57604 return (drwav_int64)drwav_bytes_to_u64(data);
57605}
57606DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16])
57607{
57608 int i;
57609 for (i = 0; i < 16; i += 1) {
57610 if (a[i] != b[i]) {
57611 return DRWAV_FALSE;
57612 }
57613 }
57614 return DRWAV_TRUE;
57615}
57616DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b)
57617{
57618 return
57619 a[0] == b[0] &&
57620 a[1] == b[1] &&
57621 a[2] == b[2] &&
57622 a[3] == b[3];
57623}
57624#endif
57625/* dr_wav_c end */
57626#endif /* DRWAV_IMPLEMENTATION */
57627#endif /* MA_NO_WAV */
57628
57629#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
57630#if !defined(DR_FLAC_IMPLEMENTATION) && !defined(DRFLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
57631/* dr_flac_c begin */
57632#ifndef dr_flac_c
57633#define dr_flac_c
57634#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
57635 #pragma GCC diagnostic push
57636 #if __GNUC__ >= 7
57637 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
57638 #endif
57639#endif
57640#ifdef __linux__
57641 #ifndef _BSD_SOURCE
57642 #define _BSD_SOURCE
57643 #endif
57644 #ifndef _DEFAULT_SOURCE
57645 #define _DEFAULT_SOURCE
57646 #endif
57647 #ifndef __USE_BSD
57648 #define __USE_BSD
57649 #endif
57650 #include <endian.h>
57651#endif
57652#include <stdlib.h>
57653#include <string.h>
57654#ifdef _MSC_VER
57655 #define DRFLAC_INLINE __forceinline
57656#elif defined(__GNUC__)
57657 #if defined(__STRICT_ANSI__)
57658 #define DRFLAC_INLINE __inline__ __attribute__((always_inline))
57659 #else
57660 #define DRFLAC_INLINE inline __attribute__((always_inline))
57661 #endif
57662#elif defined(__WATCOMC__)
57663 #define DRFLAC_INLINE __inline
57664#else
57665 #define DRFLAC_INLINE
57666#endif
57667#if defined(__x86_64__) || defined(_M_X64)
57668 #define DRFLAC_X64
57669#elif defined(__i386) || defined(_M_IX86)
57670 #define DRFLAC_X86
57671#elif defined(__arm__) || defined(_M_ARM) || defined(_M_ARM64)
57672 #define DRFLAC_ARM
57673#endif
57674#if !defined(DR_FLAC_NO_SIMD)
57675 #if defined(DRFLAC_X64) || defined(DRFLAC_X86)
57676 #if defined(_MSC_VER) && !defined(__clang__)
57677 #if _MSC_VER >= 1400 && !defined(DRFLAC_NO_SSE2)
57678 #define DRFLAC_SUPPORT_SSE2
57679 #endif
57680 #if _MSC_VER >= 1600 && !defined(DRFLAC_NO_SSE41)
57681 #define DRFLAC_SUPPORT_SSE41
57682 #endif
57683 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
57684 #if defined(__SSE2__) && !defined(DRFLAC_NO_SSE2)
57685 #define DRFLAC_SUPPORT_SSE2
57686 #endif
57687 #if defined(__SSE4_1__) && !defined(DRFLAC_NO_SSE41)
57688 #define DRFLAC_SUPPORT_SSE41
57689 #endif
57690 #endif
57691 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
57692 #if !defined(DRFLAC_SUPPORT_SSE2) && !defined(DRFLAC_NO_SSE2) && __has_include(<emmintrin.h>)
57693 #define DRFLAC_SUPPORT_SSE2
57694 #endif
57695 #if !defined(DRFLAC_SUPPORT_SSE41) && !defined(DRFLAC_NO_SSE41) && __has_include(<smmintrin.h>)
57696 #define DRFLAC_SUPPORT_SSE41
57697 #endif
57698 #endif
57699 #if defined(DRFLAC_SUPPORT_SSE41)
57700 #include <smmintrin.h>
57701 #elif defined(DRFLAC_SUPPORT_SSE2)
57702 #include <emmintrin.h>
57703 #endif
57704 #endif
57705 #if defined(DRFLAC_ARM)
57706 #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
57707 #define DRFLAC_SUPPORT_NEON
57708 #endif
57709 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
57710 #if !defined(DRFLAC_SUPPORT_NEON) && !defined(DRFLAC_NO_NEON) && __has_include(<arm_neon.h>)
57711 #define DRFLAC_SUPPORT_NEON
57712 #endif
57713 #endif
57714 #if defined(DRFLAC_SUPPORT_NEON)
57715 #include <arm_neon.h>
57716 #endif
57717 #endif
57718#endif
57719#if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64))
57720 #if defined(_MSC_VER) && !defined(__clang__)
57721 #if _MSC_VER >= 1400
57722 #include <intrin.h>
57723 static void drflac__cpuid(int info[4], int fid)
57724 {
57725 __cpuid(info, fid);
57726 }
57727 #else
57728 #define DRFLAC_NO_CPUID
57729 #endif
57730 #else
57731 #if defined(__GNUC__) || defined(__clang__)
57732 static void drflac__cpuid(int info[4], int fid)
57733 {
57734 #if defined(DRFLAC_X86) && defined(__PIC__)
57735 __asm__ __volatile__ (
57736 "xchg{l} {%%}ebx, %k1;"
57737 "cpuid;"
57738 "xchg{l} {%%}ebx, %k1;"
57739 : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
57740 );
57741 #else
57742 __asm__ __volatile__ (
57743 "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
57744 );
57745 #endif
57746 }
57747 #else
57748 #define DRFLAC_NO_CPUID
57749 #endif
57750 #endif
57751#else
57752 #define DRFLAC_NO_CPUID
57753#endif
57754static DRFLAC_INLINE drflac_bool32 drflac_has_sse2(void)
57755{
57756#if defined(DRFLAC_SUPPORT_SSE2)
57757 #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2)
57758 #if defined(DRFLAC_X64)
57759 return DRFLAC_TRUE;
57760 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
57761 return DRFLAC_TRUE;
57762 #else
57763 #if defined(DRFLAC_NO_CPUID)
57764 return DRFLAC_FALSE;
57765 #else
57766 int info[4];
57767 drflac__cpuid(info, 1);
57768 return (info[3] & (1 << 26)) != 0;
57769 #endif
57770 #endif
57771 #else
57772 return DRFLAC_FALSE;
57773 #endif
57774#else
57775 return DRFLAC_FALSE;
57776#endif
57777}
57778static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void)
57779{
57780#if defined(DRFLAC_SUPPORT_SSE41)
57781 #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41)
57782 #if defined(DRFLAC_X64)
57783 return DRFLAC_TRUE;
57784 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE4_1__)
57785 return DRFLAC_TRUE;
57786 #else
57787 #if defined(DRFLAC_NO_CPUID)
57788 return DRFLAC_FALSE;
57789 #else
57790 int info[4];
57791 drflac__cpuid(info, 1);
57792 return (info[2] & (1 << 19)) != 0;
57793 #endif
57794 #endif
57795 #else
57796 return DRFLAC_FALSE;
57797 #endif
57798#else
57799 return DRFLAC_FALSE;
57800#endif
57801}
57802#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) && !defined(__clang__)
57803 #define DRFLAC_HAS_LZCNT_INTRINSIC
57804#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
57805 #define DRFLAC_HAS_LZCNT_INTRINSIC
57806#elif defined(__clang__)
57807 #if defined(__has_builtin)
57808 #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl)
57809 #define DRFLAC_HAS_LZCNT_INTRINSIC
57810 #endif
57811 #endif
57812#endif
57813#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__)
57814 #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
57815 #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
57816 #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
57817#elif defined(__clang__)
57818 #if defined(__has_builtin)
57819 #if __has_builtin(__builtin_bswap16)
57820 #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
57821 #endif
57822 #if __has_builtin(__builtin_bswap32)
57823 #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
57824 #endif
57825 #if __has_builtin(__builtin_bswap64)
57826 #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
57827 #endif
57828 #endif
57829#elif defined(__GNUC__)
57830 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
57831 #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
57832 #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
57833 #endif
57834 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
57835 #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
57836 #endif
57837#elif defined(__WATCOMC__) && defined(__386__)
57838 #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
57839 #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
57840 #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
57841 extern __inline drflac_uint16 _watcom_bswap16(drflac_uint16);
57842 extern __inline drflac_uint32 _watcom_bswap32(drflac_uint32);
57843 extern __inline drflac_uint64 _watcom_bswap64(drflac_uint64);
57844#pragma aux _watcom_bswap16 = \
57845 "xchg al, ah" \
57846 parm [ax] \
57847 modify [ax];
57848#pragma aux _watcom_bswap32 = \
57849 "bswap eax" \
57850 parm [eax] \
57851 modify [eax];
57852#pragma aux _watcom_bswap64 = \
57853 "bswap eax" \
57854 "bswap edx" \
57855 "xchg eax,edx" \
57856 parm [eax edx] \
57857 modify [eax edx];
57858#endif
57859#ifndef DRFLAC_ASSERT
57860#include <assert.h>
57861#define DRFLAC_ASSERT(expression) assert(expression)
57862#endif
57863#ifndef DRFLAC_MALLOC
57864#define DRFLAC_MALLOC(sz) malloc((sz))
57865#endif
57866#ifndef DRFLAC_REALLOC
57867#define DRFLAC_REALLOC(p, sz) realloc((p), (sz))
57868#endif
57869#ifndef DRFLAC_FREE
57870#define DRFLAC_FREE(p) free((p))
57871#endif
57872#ifndef DRFLAC_COPY_MEMORY
57873#define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
57874#endif
57875#ifndef DRFLAC_ZERO_MEMORY
57876#define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
57877#endif
57878#ifndef DRFLAC_ZERO_OBJECT
57879#define DRFLAC_ZERO_OBJECT(p) DRFLAC_ZERO_MEMORY((p), sizeof(*(p)))
57880#endif
57881#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64
57882typedef drflac_int32 drflac_result;
57883#define DRFLAC_SUCCESS 0
57884#define DRFLAC_ERROR -1
57885#define DRFLAC_INVALID_ARGS -2
57886#define DRFLAC_INVALID_OPERATION -3
57887#define DRFLAC_OUT_OF_MEMORY -4
57888#define DRFLAC_OUT_OF_RANGE -5
57889#define DRFLAC_ACCESS_DENIED -6
57890#define DRFLAC_DOES_NOT_EXIST -7
57891#define DRFLAC_ALREADY_EXISTS -8
57892#define DRFLAC_TOO_MANY_OPEN_FILES -9
57893#define DRFLAC_INVALID_FILE -10
57894#define DRFLAC_TOO_BIG -11
57895#define DRFLAC_PATH_TOO_LONG -12
57896#define DRFLAC_NAME_TOO_LONG -13
57897#define DRFLAC_NOT_DIRECTORY -14
57898#define DRFLAC_IS_DIRECTORY -15
57899#define DRFLAC_DIRECTORY_NOT_EMPTY -16
57900#define DRFLAC_END_OF_FILE -17
57901#define DRFLAC_NO_SPACE -18
57902#define DRFLAC_BUSY -19
57903#define DRFLAC_IO_ERROR -20
57904#define DRFLAC_INTERRUPT -21
57905#define DRFLAC_UNAVAILABLE -22
57906#define DRFLAC_ALREADY_IN_USE -23
57907#define DRFLAC_BAD_ADDRESS -24
57908#define DRFLAC_BAD_SEEK -25
57909#define DRFLAC_BAD_PIPE -26
57910#define DRFLAC_DEADLOCK -27
57911#define DRFLAC_TOO_MANY_LINKS -28
57912#define DRFLAC_NOT_IMPLEMENTED -29
57913#define DRFLAC_NO_MESSAGE -30
57914#define DRFLAC_BAD_MESSAGE -31
57915#define DRFLAC_NO_DATA_AVAILABLE -32
57916#define DRFLAC_INVALID_DATA -33
57917#define DRFLAC_TIMEOUT -34
57918#define DRFLAC_NO_NETWORK -35
57919#define DRFLAC_NOT_UNIQUE -36
57920#define DRFLAC_NOT_SOCKET -37
57921#define DRFLAC_NO_ADDRESS -38
57922#define DRFLAC_BAD_PROTOCOL -39
57923#define DRFLAC_PROTOCOL_UNAVAILABLE -40
57924#define DRFLAC_PROTOCOL_NOT_SUPPORTED -41
57925#define DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED -42
57926#define DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED -43
57927#define DRFLAC_SOCKET_NOT_SUPPORTED -44
57928#define DRFLAC_CONNECTION_RESET -45
57929#define DRFLAC_ALREADY_CONNECTED -46
57930#define DRFLAC_NOT_CONNECTED -47
57931#define DRFLAC_CONNECTION_REFUSED -48
57932#define DRFLAC_NO_HOST -49
57933#define DRFLAC_IN_PROGRESS -50
57934#define DRFLAC_CANCELLED -51
57935#define DRFLAC_MEMORY_ALREADY_MAPPED -52
57936#define DRFLAC_AT_END -53
57937#define DRFLAC_CRC_MISMATCH -128
57938#define DRFLAC_SUBFRAME_CONSTANT 0
57939#define DRFLAC_SUBFRAME_VERBATIM 1
57940#define DRFLAC_SUBFRAME_FIXED 8
57941#define DRFLAC_SUBFRAME_LPC 32
57942#define DRFLAC_SUBFRAME_RESERVED 255
57943#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0
57944#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1
57945#define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0
57946#define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8
57947#define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9
57948#define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10
57949#define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
57950DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision)
57951{
57952 if (pMajor) {
57953 *pMajor = DRFLAC_VERSION_MAJOR;
57954 }
57955 if (pMinor) {
57956 *pMinor = DRFLAC_VERSION_MINOR;
57957 }
57958 if (pRevision) {
57959 *pRevision = DRFLAC_VERSION_REVISION;
57960 }
57961}
57962DRFLAC_API const char* drflac_version_string(void)
57963{
57964 return DRFLAC_VERSION_STRING;
57965}
57966#if defined(__has_feature)
57967 #if __has_feature(thread_sanitizer)
57968 #define DRFLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread")))
57969 #else
57970 #define DRFLAC_NO_THREAD_SANITIZE
57971 #endif
57972#else
57973 #define DRFLAC_NO_THREAD_SANITIZE
57974#endif
57975#if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
57976static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE;
57977#endif
57978#ifndef DRFLAC_NO_CPUID
57979static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE;
57980static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE;
57981DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void)
57982{
57983 static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE;
57984 if (!isCPUCapsInitialized) {
57985#if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
57986 int info[4] = {0};
57987 drflac__cpuid(info, 0x80000001);
57988 drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0;
57989#endif
57990 drflac__gIsSSE2Supported = drflac_has_sse2();
57991 drflac__gIsSSE41Supported = drflac_has_sse41();
57992 isCPUCapsInitialized = DRFLAC_TRUE;
57993 }
57994}
57995#else
57996static drflac_bool32 drflac__gIsNEONSupported = DRFLAC_FALSE;
57997static DRFLAC_INLINE drflac_bool32 drflac__has_neon(void)
57998{
57999#if defined(DRFLAC_SUPPORT_NEON)
58000 #if defined(DRFLAC_ARM) && !defined(DRFLAC_NO_NEON)
58001 #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
58002 return DRFLAC_TRUE;
58003 #else
58004 return DRFLAC_FALSE;
58005 #endif
58006 #else
58007 return DRFLAC_FALSE;
58008 #endif
58009#else
58010 return DRFLAC_FALSE;
58011#endif
58012}
58013DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void)
58014{
58015 drflac__gIsNEONSupported = drflac__has_neon();
58016#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
58017 drflac__gIsLZCNTSupported = DRFLAC_TRUE;
58018#endif
58019}
58020#endif
58021static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian(void)
58022{
58023#if defined(DRFLAC_X86) || defined(DRFLAC_X64)
58024 return DRFLAC_TRUE;
58025#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
58026 return DRFLAC_TRUE;
58027#else
58028 int n = 1;
58029 return (*(char*)&n) == 1;
58030#endif
58031}
58032static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n)
58033{
58034#ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC
58035 #if defined(_MSC_VER) && !defined(__clang__)
58036 return _byteswap_ushort(n);
58037 #elif defined(__GNUC__) || defined(__clang__)
58038 return __builtin_bswap16(n);
58039 #elif defined(__WATCOMC__) && defined(__386__)
58040 return _watcom_bswap16(n);
58041 #else
58042 #error "This compiler does not support the byte swap intrinsic."
58043 #endif
58044#else
58045 return ((n & 0xFF00) >> 8) |
58046 ((n & 0x00FF) << 8);
58047#endif
58048}
58049static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n)
58050{
58051#ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC
58052 #if defined(_MSC_VER) && !defined(__clang__)
58053 return _byteswap_ulong(n);
58054 #elif defined(__GNUC__) || defined(__clang__)
58055 #if defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRFLAC_64BIT)
58056 drflac_uint32 r;
58057 __asm__ __volatile__ (
58058 #if defined(DRFLAC_64BIT)
58059 "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n)
58060 #else
58061 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
58062 #endif
58063 );
58064 return r;
58065 #else
58066 return __builtin_bswap32(n);
58067 #endif
58068 #elif defined(__WATCOMC__) && defined(__386__)
58069 return _watcom_bswap32(n);
58070 #else
58071 #error "This compiler does not support the byte swap intrinsic."
58072 #endif
58073#else
58074 return ((n & 0xFF000000) >> 24) |
58075 ((n & 0x00FF0000) >> 8) |
58076 ((n & 0x0000FF00) << 8) |
58077 ((n & 0x000000FF) << 24);
58078#endif
58079}
58080static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n)
58081{
58082#ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC
58083 #if defined(_MSC_VER) && !defined(__clang__)
58084 return _byteswap_uint64(n);
58085 #elif defined(__GNUC__) || defined(__clang__)
58086 return __builtin_bswap64(n);
58087 #elif defined(__WATCOMC__) && defined(__386__)
58088 return _watcom_bswap64(n);
58089 #else
58090 #error "This compiler does not support the byte swap intrinsic."
58091 #endif
58092#else
58093 return ((n & ((drflac_uint64)0xFF000000 << 32)) >> 56) |
58094 ((n & ((drflac_uint64)0x00FF0000 << 32)) >> 40) |
58095 ((n & ((drflac_uint64)0x0000FF00 << 32)) >> 24) |
58096 ((n & ((drflac_uint64)0x000000FF << 32)) >> 8) |
58097 ((n & ((drflac_uint64)0xFF000000 )) << 8) |
58098 ((n & ((drflac_uint64)0x00FF0000 )) << 24) |
58099 ((n & ((drflac_uint64)0x0000FF00 )) << 40) |
58100 ((n & ((drflac_uint64)0x000000FF )) << 56);
58101#endif
58102}
58103static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n)
58104{
58105 if (drflac__is_little_endian()) {
58106 return drflac__swap_endian_uint16(n);
58107 }
58108 return n;
58109}
58110static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n)
58111{
58112 if (drflac__is_little_endian()) {
58113 return drflac__swap_endian_uint32(n);
58114 }
58115 return n;
58116}
58117static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n)
58118{
58119 if (drflac__is_little_endian()) {
58120 return drflac__swap_endian_uint64(n);
58121 }
58122 return n;
58123}
58124static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n)
58125{
58126 if (!drflac__is_little_endian()) {
58127 return drflac__swap_endian_uint32(n);
58128 }
58129 return n;
58130}
58131static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n)
58132{
58133 drflac_uint32 result = 0;
58134 result |= (n & 0x7F000000) >> 3;
58135 result |= (n & 0x007F0000) >> 2;
58136 result |= (n & 0x00007F00) >> 1;
58137 result |= (n & 0x0000007F) >> 0;
58138 return result;
58139}
58140static drflac_uint8 drflac__crc8_table[] = {
58141 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
58142 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
58143 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
58144 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
58145 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
58146 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
58147 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
58148 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
58149 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
58150 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
58151 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
58152 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
58153 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
58154 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
58155 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
58156 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
58157};
58158static drflac_uint16 drflac__crc16_table[] = {
58159 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
58160 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
58161 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
58162 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
58163 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
58164 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
58165 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
58166 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
58167 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
58168 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
58169 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
58170 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
58171 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
58172 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
58173 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
58174 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
58175 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
58176 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
58177 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
58178 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
58179 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
58180 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
58181 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
58182 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
58183 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
58184 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
58185 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
58186 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
58187 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
58188 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
58189 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
58190 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202
58191};
58192static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data)
58193{
58194 return drflac__crc8_table[crc ^ data];
58195}
58196static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count)
58197{
58198#ifdef DR_FLAC_NO_CRC
58199 (void)crc;
58200 (void)data;
58201 (void)count;
58202 return 0;
58203#else
58204#if 0
58205 drflac_uint8 p = 0x07;
58206 for (int i = count-1; i >= 0; --i) {
58207 drflac_uint8 bit = (data & (1 << i)) >> i;
58208 if (crc & 0x80) {
58209 crc = ((crc << 1) | bit) ^ p;
58210 } else {
58211 crc = ((crc << 1) | bit);
58212 }
58213 }
58214 return crc;
58215#else
58216 drflac_uint32 wholeBytes;
58217 drflac_uint32 leftoverBits;
58218 drflac_uint64 leftoverDataMask;
58219 static drflac_uint64 leftoverDataMaskTable[8] = {
58220 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
58221 };
58222 DRFLAC_ASSERT(count <= 32);
58223 wholeBytes = count >> 3;
58224 leftoverBits = count - (wholeBytes*8);
58225 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
58226 switch (wholeBytes) {
58227 case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
58228 case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
58229 case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
58230 case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
58231 case 0: if (leftoverBits > 0) crc = (drflac_uint8)((crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]);
58232 }
58233 return crc;
58234#endif
58235#endif
58236}
58237static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data)
58238{
58239 return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data];
58240}
58241static DRFLAC_INLINE drflac_uint16 drflac_crc16_cache(drflac_uint16 crc, drflac_cache_t data)
58242{
58243#ifdef DRFLAC_64BIT
58244 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF));
58245 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF));
58246 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF));
58247 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF));
58248#endif
58249 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF));
58250 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF));
58251 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF));
58252 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF));
58253 return crc;
58254}
58255static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount)
58256{
58257 switch (byteCount)
58258 {
58259#ifdef DRFLAC_64BIT
58260 case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF));
58261 case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF));
58262 case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF));
58263 case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF));
58264#endif
58265 case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF));
58266 case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF));
58267 case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF));
58268 case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF));
58269 }
58270 return crc;
58271}
58272#if 0
58273static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count)
58274{
58275#ifdef DR_FLAC_NO_CRC
58276 (void)crc;
58277 (void)data;
58278 (void)count;
58279 return 0;
58280#else
58281#if 0
58282 drflac_uint16 p = 0x8005;
58283 for (int i = count-1; i >= 0; --i) {
58284 drflac_uint16 bit = (data & (1ULL << i)) >> i;
58285 if (r & 0x8000) {
58286 r = ((r << 1) | bit) ^ p;
58287 } else {
58288 r = ((r << 1) | bit);
58289 }
58290 }
58291 return crc;
58292#else
58293 drflac_uint32 wholeBytes;
58294 drflac_uint32 leftoverBits;
58295 drflac_uint64 leftoverDataMask;
58296 static drflac_uint64 leftoverDataMaskTable[8] = {
58297 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
58298 };
58299 DRFLAC_ASSERT(count <= 64);
58300 wholeBytes = count >> 3;
58301 leftoverBits = count & 7;
58302 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
58303 switch (wholeBytes) {
58304 default:
58305 case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
58306 case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
58307 case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
58308 case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
58309 case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
58310 }
58311 return crc;
58312#endif
58313#endif
58314}
58315static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count)
58316{
58317#ifdef DR_FLAC_NO_CRC
58318 (void)crc;
58319 (void)data;
58320 (void)count;
58321 return 0;
58322#else
58323 drflac_uint32 wholeBytes;
58324 drflac_uint32 leftoverBits;
58325 drflac_uint64 leftoverDataMask;
58326 static drflac_uint64 leftoverDataMaskTable[8] = {
58327 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
58328 };
58329 DRFLAC_ASSERT(count <= 64);
58330 wholeBytes = count >> 3;
58331 leftoverBits = count & 7;
58332 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
58333 switch (wholeBytes) {
58334 default:
58335 case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits)));
58336 case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits)));
58337 case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits)));
58338 case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits)));
58339 case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits)));
58340 case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits)));
58341 case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits)));
58342 case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits)));
58343 case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
58344 }
58345 return crc;
58346#endif
58347}
58348static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count)
58349{
58350#ifdef DRFLAC_64BIT
58351 return drflac_crc16__64bit(crc, data, count);
58352#else
58353 return drflac_crc16__32bit(crc, data, count);
58354#endif
58355}
58356#endif
58357#ifdef DRFLAC_64BIT
58358#define drflac__be2host__cache_line drflac__be2host_64
58359#else
58360#define drflac__be2host__cache_line drflac__be2host_32
58361#endif
58362#define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache))
58363#define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8)
58364#define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits)
58365#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount)))
58366#define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount))
58367#define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount))
58368#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)))
58369#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)))
58370#define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2))
58371#define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0]))
58372#define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line)
58373#ifndef DR_FLAC_NO_CRC
58374static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs)
58375{
58376 bs->crc16 = 0;
58377 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
58378}
58379static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs)
58380{
58381 if (bs->crc16CacheIgnoredBytes == 0) {
58382 bs->crc16 = drflac_crc16_cache(bs->crc16, bs->crc16Cache);
58383 } else {
58384 bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes);
58385 bs->crc16CacheIgnoredBytes = 0;
58386 }
58387}
58388static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs)
58389{
58390 DRFLAC_ASSERT((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0);
58391 if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) {
58392 drflac__update_crc16(bs);
58393 } else {
58394 bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes);
58395 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
58396 }
58397 return bs->crc16;
58398}
58399#endif
58400static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs)
58401{
58402 size_t bytesRead;
58403 size_t alignedL1LineCount;
58404 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
58405 bs->cache = bs->cacheL2[bs->nextL2Line++];
58406 return DRFLAC_TRUE;
58407 }
58408 if (bs->unalignedByteCount > 0) {
58409 return DRFLAC_FALSE;
58410 }
58411 bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs));
58412 bs->nextL2Line = 0;
58413 if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) {
58414 bs->cache = bs->cacheL2[bs->nextL2Line++];
58415 return DRFLAC_TRUE;
58416 }
58417 alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs);
58418 bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs));
58419 if (bs->unalignedByteCount > 0) {
58420 bs->unalignedCache = bs->cacheL2[alignedL1LineCount];
58421 }
58422 if (alignedL1LineCount > 0) {
58423 size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount;
58424 size_t i;
58425 for (i = alignedL1LineCount; i > 0; --i) {
58426 bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1];
58427 }
58428 bs->nextL2Line = (drflac_uint32)offset;
58429 bs->cache = bs->cacheL2[bs->nextL2Line++];
58430 return DRFLAC_TRUE;
58431 } else {
58432 bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs);
58433 return DRFLAC_FALSE;
58434 }
58435}
58436static drflac_bool32 drflac__reload_cache(drflac_bs* bs)
58437{
58438 size_t bytesRead;
58439#ifndef DR_FLAC_NO_CRC
58440 drflac__update_crc16(bs);
58441#endif
58442 if (drflac__reload_l1_cache_from_l2(bs)) {
58443 bs->cache = drflac__be2host__cache_line(bs->cache);
58444 bs->consumedBits = 0;
58445#ifndef DR_FLAC_NO_CRC
58446 bs->crc16Cache = bs->cache;
58447#endif
58448 return DRFLAC_TRUE;
58449 }
58450 bytesRead = bs->unalignedByteCount;
58451 if (bytesRead == 0) {
58452 bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
58453 return DRFLAC_FALSE;
58454 }
58455 DRFLAC_ASSERT(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs));
58456 bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8;
58457 bs->cache = drflac__be2host__cache_line(bs->unalignedCache);
58458 bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_BITS_REMAINING(bs));
58459 bs->unalignedByteCount = 0;
58460#ifndef DR_FLAC_NO_CRC
58461 bs->crc16Cache = bs->cache >> bs->consumedBits;
58462 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
58463#endif
58464 return DRFLAC_TRUE;
58465}
58466static void drflac__reset_cache(drflac_bs* bs)
58467{
58468 bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs);
58469 bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
58470 bs->cache = 0;
58471 bs->unalignedByteCount = 0;
58472 bs->unalignedCache = 0;
58473#ifndef DR_FLAC_NO_CRC
58474 bs->crc16Cache = 0;
58475 bs->crc16CacheIgnoredBytes = 0;
58476#endif
58477}
58478static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut)
58479{
58480 DRFLAC_ASSERT(bs != NULL);
58481 DRFLAC_ASSERT(pResultOut != NULL);
58482 DRFLAC_ASSERT(bitCount > 0);
58483 DRFLAC_ASSERT(bitCount <= 32);
58484 if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
58485 if (!drflac__reload_cache(bs)) {
58486 return DRFLAC_FALSE;
58487 }
58488 }
58489 if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
58490#ifdef DRFLAC_64BIT
58491 *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
58492 bs->consumedBits += bitCount;
58493 bs->cache <<= bitCount;
58494#else
58495 if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
58496 *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
58497 bs->consumedBits += bitCount;
58498 bs->cache <<= bitCount;
58499 } else {
58500 *pResultOut = (drflac_uint32)bs->cache;
58501 bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
58502 bs->cache = 0;
58503 }
58504#endif
58505 return DRFLAC_TRUE;
58506 } else {
58507 drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs);
58508 drflac_uint32 bitCountLo = bitCount - bitCountHi;
58509 drflac_uint32 resultHi;
58510 DRFLAC_ASSERT(bitCountHi > 0);
58511 DRFLAC_ASSERT(bitCountHi < 32);
58512 resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi);
58513 if (!drflac__reload_cache(bs)) {
58514 return DRFLAC_FALSE;
58515 }
58516 *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo);
58517 bs->consumedBits += bitCountLo;
58518 bs->cache <<= bitCountLo;
58519 return DRFLAC_TRUE;
58520 }
58521}
58522static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult)
58523{
58524 drflac_uint32 result;
58525 DRFLAC_ASSERT(bs != NULL);
58526 DRFLAC_ASSERT(pResult != NULL);
58527 DRFLAC_ASSERT(bitCount > 0);
58528 DRFLAC_ASSERT(bitCount <= 32);
58529 if (!drflac__read_uint32(bs, bitCount, &result)) {
58530 return DRFLAC_FALSE;
58531 }
58532 if (bitCount < 32) {
58533 drflac_uint32 signbit;
58534 signbit = ((result >> (bitCount-1)) & 0x01);
58535 result |= (~signbit + 1) << bitCount;
58536 }
58537 *pResult = (drflac_int32)result;
58538 return DRFLAC_TRUE;
58539}
58540#ifdef DRFLAC_64BIT
58541static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut)
58542{
58543 drflac_uint32 resultHi;
58544 drflac_uint32 resultLo;
58545 DRFLAC_ASSERT(bitCount <= 64);
58546 DRFLAC_ASSERT(bitCount > 32);
58547 if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) {
58548 return DRFLAC_FALSE;
58549 }
58550 if (!drflac__read_uint32(bs, 32, &resultLo)) {
58551 return DRFLAC_FALSE;
58552 }
58553 *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo);
58554 return DRFLAC_TRUE;
58555}
58556#endif
58557#if 0
58558static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut)
58559{
58560 drflac_uint64 result;
58561 drflac_uint64 signbit;
58562 DRFLAC_ASSERT(bitCount <= 64);
58563 if (!drflac__read_uint64(bs, bitCount, &result)) {
58564 return DRFLAC_FALSE;
58565 }
58566 signbit = ((result >> (bitCount-1)) & 0x01);
58567 result |= (~signbit + 1) << bitCount;
58568 *pResultOut = (drflac_int64)result;
58569 return DRFLAC_TRUE;
58570}
58571#endif
58572static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult)
58573{
58574 drflac_uint32 result;
58575 DRFLAC_ASSERT(bs != NULL);
58576 DRFLAC_ASSERT(pResult != NULL);
58577 DRFLAC_ASSERT(bitCount > 0);
58578 DRFLAC_ASSERT(bitCount <= 16);
58579 if (!drflac__read_uint32(bs, bitCount, &result)) {
58580 return DRFLAC_FALSE;
58581 }
58582 *pResult = (drflac_uint16)result;
58583 return DRFLAC_TRUE;
58584}
58585#if 0
58586static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult)
58587{
58588 drflac_int32 result;
58589 DRFLAC_ASSERT(bs != NULL);
58590 DRFLAC_ASSERT(pResult != NULL);
58591 DRFLAC_ASSERT(bitCount > 0);
58592 DRFLAC_ASSERT(bitCount <= 16);
58593 if (!drflac__read_int32(bs, bitCount, &result)) {
58594 return DRFLAC_FALSE;
58595 }
58596 *pResult = (drflac_int16)result;
58597 return DRFLAC_TRUE;
58598}
58599#endif
58600static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult)
58601{
58602 drflac_uint32 result;
58603 DRFLAC_ASSERT(bs != NULL);
58604 DRFLAC_ASSERT(pResult != NULL);
58605 DRFLAC_ASSERT(bitCount > 0);
58606 DRFLAC_ASSERT(bitCount <= 8);
58607 if (!drflac__read_uint32(bs, bitCount, &result)) {
58608 return DRFLAC_FALSE;
58609 }
58610 *pResult = (drflac_uint8)result;
58611 return DRFLAC_TRUE;
58612}
58613static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult)
58614{
58615 drflac_int32 result;
58616 DRFLAC_ASSERT(bs != NULL);
58617 DRFLAC_ASSERT(pResult != NULL);
58618 DRFLAC_ASSERT(bitCount > 0);
58619 DRFLAC_ASSERT(bitCount <= 8);
58620 if (!drflac__read_int32(bs, bitCount, &result)) {
58621 return DRFLAC_FALSE;
58622 }
58623 *pResult = (drflac_int8)result;
58624 return DRFLAC_TRUE;
58625}
58626static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek)
58627{
58628 if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
58629 bs->consumedBits += (drflac_uint32)bitsToSeek;
58630 bs->cache <<= bitsToSeek;
58631 return DRFLAC_TRUE;
58632 } else {
58633 bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs);
58634 bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs);
58635 bs->cache = 0;
58636#ifdef DRFLAC_64BIT
58637 while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
58638 drflac_uint64 bin;
58639 if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
58640 return DRFLAC_FALSE;
58641 }
58642 bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs);
58643 }
58644#else
58645 while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
58646 drflac_uint32 bin;
58647 if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
58648 return DRFLAC_FALSE;
58649 }
58650 bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs);
58651 }
58652#endif
58653 while (bitsToSeek >= 8) {
58654 drflac_uint8 bin;
58655 if (!drflac__read_uint8(bs, 8, &bin)) {
58656 return DRFLAC_FALSE;
58657 }
58658 bitsToSeek -= 8;
58659 }
58660 if (bitsToSeek > 0) {
58661 drflac_uint8 bin;
58662 if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) {
58663 return DRFLAC_FALSE;
58664 }
58665 bitsToSeek = 0;
58666 }
58667 DRFLAC_ASSERT(bitsToSeek == 0);
58668 return DRFLAC_TRUE;
58669 }
58670}
58671static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs)
58672{
58673 DRFLAC_ASSERT(bs != NULL);
58674 if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
58675 return DRFLAC_FALSE;
58676 }
58677 for (;;) {
58678 drflac_uint8 hi;
58679#ifndef DR_FLAC_NO_CRC
58680 drflac__reset_crc16(bs);
58681#endif
58682 if (!drflac__read_uint8(bs, 8, &hi)) {
58683 return DRFLAC_FALSE;
58684 }
58685 if (hi == 0xFF) {
58686 drflac_uint8 lo;
58687 if (!drflac__read_uint8(bs, 6, &lo)) {
58688 return DRFLAC_FALSE;
58689 }
58690 if (lo == 0x3E) {
58691 return DRFLAC_TRUE;
58692 } else {
58693 if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
58694 return DRFLAC_FALSE;
58695 }
58696 }
58697 }
58698 }
58699}
58700#if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
58701#define DRFLAC_IMPLEMENT_CLZ_LZCNT
58702#endif
58703#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(__clang__)
58704#define DRFLAC_IMPLEMENT_CLZ_MSVC
58705#endif
58706#if defined(__WATCOMC__) && defined(__386__)
58707#define DRFLAC_IMPLEMENT_CLZ_WATCOM
58708#endif
58709static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x)
58710{
58711 drflac_uint32 n;
58712 static drflac_uint32 clz_table_4[] = {
58713 0,
58714 4,
58715 3, 3,
58716 2, 2, 2, 2,
58717 1, 1, 1, 1, 1, 1, 1, 1
58718 };
58719 if (x == 0) {
58720 return sizeof(x)*8;
58721 }
58722 n = clz_table_4[x >> (sizeof(x)*8 - 4)];
58723 if (n == 0) {
58724#ifdef DRFLAC_64BIT
58725 if ((x & ((drflac_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; }
58726 if ((x & ((drflac_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; }
58727 if ((x & ((drflac_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; }
58728 if ((x & ((drflac_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; }
58729#else
58730 if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; }
58731 if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; }
58732 if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; }
58733#endif
58734 n += clz_table_4[x >> (sizeof(x)*8 - 4)];
58735 }
58736 return n - 1;
58737}
58738#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT
58739static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void)
58740{
58741#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
58742 return DRFLAC_TRUE;
58743#else
58744 #ifdef DRFLAC_HAS_LZCNT_INTRINSIC
58745 return drflac__gIsLZCNTSupported;
58746 #else
58747 return DRFLAC_FALSE;
58748 #endif
58749#endif
58750}
58751static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x)
58752{
58753#if defined(_MSC_VER)
58754 #ifdef DRFLAC_64BIT
58755 return (drflac_uint32)__lzcnt64(x);
58756 #else
58757 return (drflac_uint32)__lzcnt(x);
58758 #endif
58759#else
58760 #if defined(__GNUC__) || defined(__clang__)
58761 #if defined(DRFLAC_X64)
58762 {
58763 drflac_uint64 r;
58764 __asm__ __volatile__ (
58765 "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
58766 );
58767 return (drflac_uint32)r;
58768 }
58769 #elif defined(DRFLAC_X86)
58770 {
58771 drflac_uint32 r;
58772 __asm__ __volatile__ (
58773 "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
58774 );
58775 return r;
58776 }
58777 #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(DRFLAC_64BIT)
58778 {
58779 unsigned int r;
58780 __asm__ __volatile__ (
58781 #if defined(DRFLAC_64BIT)
58782 "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x)
58783 #else
58784 "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x)
58785 #endif
58786 );
58787 return r;
58788 }
58789 #else
58790 if (x == 0) {
58791 return sizeof(x)*8;
58792 }
58793 #ifdef DRFLAC_64BIT
58794 return (drflac_uint32)__builtin_clzll((drflac_uint64)x);
58795 #else
58796 return (drflac_uint32)__builtin_clzl((drflac_uint32)x);
58797 #endif
58798 #endif
58799 #else
58800 #error "This compiler does not support the lzcnt intrinsic."
58801 #endif
58802#endif
58803}
58804#endif
58805#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC
58806#include <intrin.h>
58807static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x)
58808{
58809 drflac_uint32 n;
58810 if (x == 0) {
58811 return sizeof(x)*8;
58812 }
58813#ifdef DRFLAC_64BIT
58814 _BitScanReverse64((unsigned long*)&n, x);
58815#else
58816 _BitScanReverse((unsigned long*)&n, x);
58817#endif
58818 return sizeof(x)*8 - n - 1;
58819}
58820#endif
58821#ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM
58822static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32);
58823#pragma aux drflac__clz_watcom = \
58824 "bsr eax, eax" \
58825 "xor eax, 31" \
58826 parm [eax] nomemory \
58827 value [eax] \
58828 modify exact [eax] nomemory;
58829#endif
58830static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x)
58831{
58832#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT
58833 if (drflac__is_lzcnt_supported()) {
58834 return drflac__clz_lzcnt(x);
58835 } else
58836#endif
58837 {
58838#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC
58839 return drflac__clz_msvc(x);
58840#elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM)
58841 return (x == 0) ? sizeof(x)*8 : drflac__clz_watcom(x);
58842#else
58843 return drflac__clz_software(x);
58844#endif
58845 }
58846}
58847static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut)
58848{
58849 drflac_uint32 zeroCounter = 0;
58850 drflac_uint32 setBitOffsetPlus1;
58851 while (bs->cache == 0) {
58852 zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
58853 if (!drflac__reload_cache(bs)) {
58854 return DRFLAC_FALSE;
58855 }
58856 }
58857 setBitOffsetPlus1 = drflac__clz(bs->cache);
58858 setBitOffsetPlus1 += 1;
58859 bs->consumedBits += setBitOffsetPlus1;
58860 bs->cache <<= setBitOffsetPlus1;
58861 *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1;
58862 return DRFLAC_TRUE;
58863}
58864static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart)
58865{
58866 DRFLAC_ASSERT(bs != NULL);
58867 DRFLAC_ASSERT(offsetFromStart > 0);
58868 if (offsetFromStart > 0x7FFFFFFF) {
58869 drflac_uint64 bytesRemaining = offsetFromStart;
58870 if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
58871 return DRFLAC_FALSE;
58872 }
58873 bytesRemaining -= 0x7FFFFFFF;
58874 while (bytesRemaining > 0x7FFFFFFF) {
58875 if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
58876 return DRFLAC_FALSE;
58877 }
58878 bytesRemaining -= 0x7FFFFFFF;
58879 }
58880 if (bytesRemaining > 0) {
58881 if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) {
58882 return DRFLAC_FALSE;
58883 }
58884 }
58885 } else {
58886 if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) {
58887 return DRFLAC_FALSE;
58888 }
58889 }
58890 drflac__reset_cache(bs);
58891 return DRFLAC_TRUE;
58892}
58893static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut)
58894{
58895 drflac_uint8 crc;
58896 drflac_uint64 result;
58897 drflac_uint8 utf8[7] = {0};
58898 int byteCount;
58899 int i;
58900 DRFLAC_ASSERT(bs != NULL);
58901 DRFLAC_ASSERT(pNumberOut != NULL);
58902 DRFLAC_ASSERT(pCRCOut != NULL);
58903 crc = *pCRCOut;
58904 if (!drflac__read_uint8(bs, 8, utf8)) {
58905 *pNumberOut = 0;
58906 return DRFLAC_AT_END;
58907 }
58908 crc = drflac_crc8(crc, utf8[0], 8);
58909 if ((utf8[0] & 0x80) == 0) {
58910 *pNumberOut = utf8[0];
58911 *pCRCOut = crc;
58912 return DRFLAC_SUCCESS;
58913 }
58914 if ((utf8[0] & 0xE0) == 0xC0) {
58915 byteCount = 2;
58916 } else if ((utf8[0] & 0xF0) == 0xE0) {
58917 byteCount = 3;
58918 } else if ((utf8[0] & 0xF8) == 0xF0) {
58919 byteCount = 4;
58920 } else if ((utf8[0] & 0xFC) == 0xF8) {
58921 byteCount = 5;
58922 } else if ((utf8[0] & 0xFE) == 0xFC) {
58923 byteCount = 6;
58924 } else if ((utf8[0] & 0xFF) == 0xFE) {
58925 byteCount = 7;
58926 } else {
58927 *pNumberOut = 0;
58928 return DRFLAC_CRC_MISMATCH;
58929 }
58930 DRFLAC_ASSERT(byteCount > 1);
58931 result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1)));
58932 for (i = 1; i < byteCount; ++i) {
58933 if (!drflac__read_uint8(bs, 8, utf8 + i)) {
58934 *pNumberOut = 0;
58935 return DRFLAC_AT_END;
58936 }
58937 crc = drflac_crc8(crc, utf8[i], 8);
58938 result = (result << 6) | (utf8[i] & 0x3F);
58939 }
58940 *pNumberOut = result;
58941 *pCRCOut = crc;
58942 return DRFLAC_SUCCESS;
58943}
58944static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
58945{
58946 drflac_int32 prediction = 0;
58947 DRFLAC_ASSERT(order <= 32);
58948 switch (order)
58949 {
58950 case 32: prediction += coefficients[31] * pDecodedSamples[-32];
58951 case 31: prediction += coefficients[30] * pDecodedSamples[-31];
58952 case 30: prediction += coefficients[29] * pDecodedSamples[-30];
58953 case 29: prediction += coefficients[28] * pDecodedSamples[-29];
58954 case 28: prediction += coefficients[27] * pDecodedSamples[-28];
58955 case 27: prediction += coefficients[26] * pDecodedSamples[-27];
58956 case 26: prediction += coefficients[25] * pDecodedSamples[-26];
58957 case 25: prediction += coefficients[24] * pDecodedSamples[-25];
58958 case 24: prediction += coefficients[23] * pDecodedSamples[-24];
58959 case 23: prediction += coefficients[22] * pDecodedSamples[-23];
58960 case 22: prediction += coefficients[21] * pDecodedSamples[-22];
58961 case 21: prediction += coefficients[20] * pDecodedSamples[-21];
58962 case 20: prediction += coefficients[19] * pDecodedSamples[-20];
58963 case 19: prediction += coefficients[18] * pDecodedSamples[-19];
58964 case 18: prediction += coefficients[17] * pDecodedSamples[-18];
58965 case 17: prediction += coefficients[16] * pDecodedSamples[-17];
58966 case 16: prediction += coefficients[15] * pDecodedSamples[-16];
58967 case 15: prediction += coefficients[14] * pDecodedSamples[-15];
58968 case 14: prediction += coefficients[13] * pDecodedSamples[-14];
58969 case 13: prediction += coefficients[12] * pDecodedSamples[-13];
58970 case 12: prediction += coefficients[11] * pDecodedSamples[-12];
58971 case 11: prediction += coefficients[10] * pDecodedSamples[-11];
58972 case 10: prediction += coefficients[ 9] * pDecodedSamples[-10];
58973 case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9];
58974 case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8];
58975 case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7];
58976 case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6];
58977 case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5];
58978 case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4];
58979 case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3];
58980 case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2];
58981 case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1];
58982 }
58983 return (drflac_int32)(prediction >> shift);
58984}
58985static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
58986{
58987 drflac_int64 prediction;
58988 DRFLAC_ASSERT(order <= 32);
58989#ifndef DRFLAC_64BIT
58990 if (order == 8)
58991 {
58992 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
58993 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
58994 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
58995 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
58996 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
58997 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
58998 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
58999 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
59000 }
59001 else if (order == 7)
59002 {
59003 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
59004 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
59005 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
59006 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
59007 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
59008 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
59009 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
59010 }
59011 else if (order == 3)
59012 {
59013 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
59014 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
59015 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
59016 }
59017 else if (order == 6)
59018 {
59019 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
59020 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
59021 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
59022 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
59023 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
59024 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
59025 }
59026 else if (order == 5)
59027 {
59028 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
59029 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
59030 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
59031 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
59032 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
59033 }
59034 else if (order == 4)
59035 {
59036 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
59037 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
59038 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
59039 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
59040 }
59041 else if (order == 12)
59042 {
59043 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
59044 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
59045 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
59046 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
59047 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
59048 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
59049 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
59050 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
59051 prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
59052 prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
59053 prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
59054 prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12];
59055 }
59056 else if (order == 2)
59057 {
59058 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
59059 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
59060 }
59061 else if (order == 1)
59062 {
59063 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
59064 }
59065 else if (order == 10)
59066 {
59067 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
59068 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
59069 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
59070 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
59071 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
59072 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
59073 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
59074 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
59075 prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
59076 prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
59077 }
59078 else if (order == 9)
59079 {
59080 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
59081 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
59082 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
59083 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
59084 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
59085 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
59086 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
59087 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
59088 prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
59089 }
59090 else if (order == 11)
59091 {
59092 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
59093 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
59094 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
59095 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
59096 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
59097 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
59098 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
59099 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
59100 prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
59101 prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
59102 prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
59103 }
59104 else
59105 {
59106 int j;
59107 prediction = 0;
59108 for (j = 0; j < (int)order; ++j) {
59109 prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1];
59110 }
59111 }
59112#endif
59113#ifdef DRFLAC_64BIT
59114 prediction = 0;
59115 switch (order)
59116 {
59117 case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32];
59118 case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31];
59119 case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30];
59120 case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29];
59121 case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28];
59122 case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27];
59123 case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26];
59124 case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25];
59125 case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24];
59126 case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23];
59127 case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22];
59128 case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21];
59129 case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20];
59130 case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19];
59131 case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18];
59132 case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17];
59133 case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16];
59134 case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15];
59135 case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14];
59136 case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13];
59137 case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12];
59138 case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
59139 case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10];
59140 case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9];
59141 case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8];
59142 case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7];
59143 case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6];
59144 case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5];
59145 case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4];
59146 case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3];
59147 case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2];
59148 case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1];
59149 }
59150#endif
59151 return (drflac_int32)(prediction >> shift);
59152}
59153#if 0
59154static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(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)
59155{
59156 drflac_uint32 i;
59157 DRFLAC_ASSERT(bs != NULL);
59158 DRFLAC_ASSERT(pSamplesOut != NULL);
59159 for (i = 0; i < count; ++i) {
59160 drflac_uint32 zeroCounter = 0;
59161 for (;;) {
59162 drflac_uint8 bit;
59163 if (!drflac__read_uint8(bs, 1, &bit)) {
59164 return DRFLAC_FALSE;
59165 }
59166 if (bit == 0) {
59167 zeroCounter += 1;
59168 } else {
59169 break;
59170 }
59171 }
59172 drflac_uint32 decodedRice;
59173 if (riceParam > 0) {
59174 if (!drflac__read_uint32(bs, riceParam, &decodedRice)) {
59175 return DRFLAC_FALSE;
59176 }
59177 } else {
59178 decodedRice = 0;
59179 }
59180 decodedRice |= (zeroCounter << riceParam);
59181 if ((decodedRice & 0x01)) {
59182 decodedRice = ~(decodedRice >> 1);
59183 } else {
59184 decodedRice = (decodedRice >> 1);
59185 }
59186 if (bitsPerSample+shift >= 32) {
59187 pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i);
59188 } else {
59189 pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i);
59190 }
59191 }
59192 return DRFLAC_TRUE;
59193}
59194#endif
59195#if 0
59196static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
59197{
59198 drflac_uint32 zeroCounter = 0;
59199 drflac_uint32 decodedRice;
59200 for (;;) {
59201 drflac_uint8 bit;
59202 if (!drflac__read_uint8(bs, 1, &bit)) {
59203 return DRFLAC_FALSE;
59204 }
59205 if (bit == 0) {
59206 zeroCounter += 1;
59207 } else {
59208 break;
59209 }
59210 }
59211 if (riceParam > 0) {
59212 if (!drflac__read_uint32(bs, riceParam, &decodedRice)) {
59213 return DRFLAC_FALSE;
59214 }
59215 } else {
59216 decodedRice = 0;
59217 }
59218 *pZeroCounterOut = zeroCounter;
59219 *pRiceParamPartOut = decodedRice;
59220 return DRFLAC_TRUE;
59221}
59222#endif
59223#if 0
59224static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
59225{
59226 drflac_cache_t riceParamMask;
59227 drflac_uint32 zeroCounter;
59228 drflac_uint32 setBitOffsetPlus1;
59229 drflac_uint32 riceParamPart;
59230 drflac_uint32 riceLength;
59231 DRFLAC_ASSERT(riceParam > 0);
59232 riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam);
59233 zeroCounter = 0;
59234 while (bs->cache == 0) {
59235 zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
59236 if (!drflac__reload_cache(bs)) {
59237 return DRFLAC_FALSE;
59238 }
59239 }
59240 setBitOffsetPlus1 = drflac__clz(bs->cache);
59241 zeroCounter += setBitOffsetPlus1;
59242 setBitOffsetPlus1 += 1;
59243 riceLength = setBitOffsetPlus1 + riceParam;
59244 if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
59245 riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength));
59246 bs->consumedBits += riceLength;
59247 bs->cache <<= riceLength;
59248 } else {
59249 drflac_uint32 bitCountLo;
59250 drflac_cache_t resultHi;
59251 bs->consumedBits += riceLength;
59252 bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1);
59253 bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs);
59254 resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam);
59255 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
59256#ifndef DR_FLAC_NO_CRC
59257 drflac__update_crc16(bs);
59258#endif
59259 bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
59260 bs->consumedBits = 0;
59261#ifndef DR_FLAC_NO_CRC
59262 bs->crc16Cache = bs->cache;
59263#endif
59264 } else {
59265 if (!drflac__reload_cache(bs)) {
59266 return DRFLAC_FALSE;
59267 }
59268 }
59269 riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo));
59270 bs->consumedBits += bitCountLo;
59271 bs->cache <<= bitCountLo;
59272 }
59273 pZeroCounterOut[0] = zeroCounter;
59274 pRiceParamPartOut[0] = riceParamPart;
59275 return DRFLAC_TRUE;
59276}
59277#endif
59278static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
59279{
59280 drflac_uint32 riceParamPlus1 = riceParam + 1;
59281 drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1);
59282 drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
59283 drflac_cache_t bs_cache = bs->cache;
59284 drflac_uint32 bs_consumedBits = bs->consumedBits;
59285 drflac_uint32 lzcount = drflac__clz(bs_cache);
59286 if (lzcount < sizeof(bs_cache)*8) {
59287 pZeroCounterOut[0] = lzcount;
59288 extract_rice_param_part:
59289 bs_cache <<= lzcount;
59290 bs_consumedBits += lzcount;
59291 if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
59292 pRiceParamPartOut[0] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
59293 bs_cache <<= riceParamPlus1;
59294 bs_consumedBits += riceParamPlus1;
59295 } else {
59296 drflac_uint32 riceParamPartHi;
59297 drflac_uint32 riceParamPartLo;
59298 drflac_uint32 riceParamPartLoBitCount;
59299 riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
59300 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
59301 DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
59302 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
59303 #ifndef DR_FLAC_NO_CRC
59304 drflac__update_crc16(bs);
59305 #endif
59306 bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
59307 bs_consumedBits = riceParamPartLoBitCount;
59308 #ifndef DR_FLAC_NO_CRC
59309 bs->crc16Cache = bs_cache;
59310 #endif
59311 } else {
59312 if (!drflac__reload_cache(bs)) {
59313 return DRFLAC_FALSE;
59314 }
59315 bs_cache = bs->cache;
59316 bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
59317 }
59318 riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount)));
59319 pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo;
59320 bs_cache <<= riceParamPartLoBitCount;
59321 }
59322 } else {
59323 drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits);
59324 for (;;) {
59325 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
59326 #ifndef DR_FLAC_NO_CRC
59327 drflac__update_crc16(bs);
59328 #endif
59329 bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
59330 bs_consumedBits = 0;
59331 #ifndef DR_FLAC_NO_CRC
59332 bs->crc16Cache = bs_cache;
59333 #endif
59334 } else {
59335 if (!drflac__reload_cache(bs)) {
59336 return DRFLAC_FALSE;
59337 }
59338 bs_cache = bs->cache;
59339 bs_consumedBits = bs->consumedBits;
59340 }
59341 lzcount = drflac__clz(bs_cache);
59342 zeroCounter += lzcount;
59343 if (lzcount < sizeof(bs_cache)*8) {
59344 break;
59345 }
59346 }
59347 pZeroCounterOut[0] = zeroCounter;
59348 goto extract_rice_param_part;
59349 }
59350 bs->cache = bs_cache;
59351 bs->consumedBits = bs_consumedBits;
59352 return DRFLAC_TRUE;
59353}
59354static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac_uint8 riceParam)
59355{
59356 drflac_uint32 riceParamPlus1 = riceParam + 1;
59357 drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
59358 drflac_cache_t bs_cache = bs->cache;
59359 drflac_uint32 bs_consumedBits = bs->consumedBits;
59360 drflac_uint32 lzcount = drflac__clz(bs_cache);
59361 if (lzcount < sizeof(bs_cache)*8) {
59362 extract_rice_param_part:
59363 bs_cache <<= lzcount;
59364 bs_consumedBits += lzcount;
59365 if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
59366 bs_cache <<= riceParamPlus1;
59367 bs_consumedBits += riceParamPlus1;
59368 } else {
59369 drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
59370 DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
59371 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
59372 #ifndef DR_FLAC_NO_CRC
59373 drflac__update_crc16(bs);
59374 #endif
59375 bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
59376 bs_consumedBits = riceParamPartLoBitCount;
59377 #ifndef DR_FLAC_NO_CRC
59378 bs->crc16Cache = bs_cache;
59379 #endif
59380 } else {
59381 if (!drflac__reload_cache(bs)) {
59382 return DRFLAC_FALSE;
59383 }
59384 bs_cache = bs->cache;
59385 bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
59386 }
59387 bs_cache <<= riceParamPartLoBitCount;
59388 }
59389 } else {
59390 for (;;) {
59391 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
59392 #ifndef DR_FLAC_NO_CRC
59393 drflac__update_crc16(bs);
59394 #endif
59395 bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
59396 bs_consumedBits = 0;
59397 #ifndef DR_FLAC_NO_CRC
59398 bs->crc16Cache = bs_cache;
59399 #endif
59400 } else {
59401 if (!drflac__reload_cache(bs)) {
59402 return DRFLAC_FALSE;
59403 }
59404 bs_cache = bs->cache;
59405 bs_consumedBits = bs->consumedBits;
59406 }
59407 lzcount = drflac__clz(bs_cache);
59408 if (lzcount < sizeof(bs_cache)*8) {
59409 break;
59410 }
59411 }
59412 goto extract_rice_param_part;
59413 }
59414 bs->cache = bs_cache;
59415 bs->consumedBits = bs_consumedBits;
59416 return DRFLAC_TRUE;
59417}
59418static 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)
59419{
59420 drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
59421 drflac_uint32 zeroCountPart0;
59422 drflac_uint32 riceParamPart0;
59423 drflac_uint32 riceParamMask;
59424 drflac_uint32 i;
59425 DRFLAC_ASSERT(bs != NULL);
59426 DRFLAC_ASSERT(pSamplesOut != NULL);
59427 (void)bitsPerSample;
59428 (void)order;
59429 (void)shift;
59430 (void)coefficients;
59431 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
59432 i = 0;
59433 while (i < count) {
59434 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
59435 return DRFLAC_FALSE;
59436 }
59437 riceParamPart0 &= riceParamMask;
59438 riceParamPart0 |= (zeroCountPart0 << riceParam);
59439 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
59440 pSamplesOut[i] = riceParamPart0;
59441 i += 1;
59442 }
59443 return DRFLAC_TRUE;
59444}
59445static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(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)
59446{
59447 drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
59448 drflac_uint32 zeroCountPart0 = 0;
59449 drflac_uint32 zeroCountPart1 = 0;
59450 drflac_uint32 zeroCountPart2 = 0;
59451 drflac_uint32 zeroCountPart3 = 0;
59452 drflac_uint32 riceParamPart0 = 0;
59453 drflac_uint32 riceParamPart1 = 0;
59454 drflac_uint32 riceParamPart2 = 0;
59455 drflac_uint32 riceParamPart3 = 0;
59456 drflac_uint32 riceParamMask;
59457 const drflac_int32* pSamplesOutEnd;
59458 drflac_uint32 i;
59459 DRFLAC_ASSERT(bs != NULL);
59460 DRFLAC_ASSERT(pSamplesOut != NULL);
59461 if (order == 0) {
59462 return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
59463 }
59464 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
59465 pSamplesOutEnd = pSamplesOut + (count & ~3);
59466 if (bitsPerSample+shift > 32) {
59467 while (pSamplesOut < pSamplesOutEnd) {
59468 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
59469 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
59470 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
59471 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
59472 return DRFLAC_FALSE;
59473 }
59474 riceParamPart0 &= riceParamMask;
59475 riceParamPart1 &= riceParamMask;
59476 riceParamPart2 &= riceParamMask;
59477 riceParamPart3 &= riceParamMask;
59478 riceParamPart0 |= (zeroCountPart0 << riceParam);
59479 riceParamPart1 |= (zeroCountPart1 << riceParam);
59480 riceParamPart2 |= (zeroCountPart2 << riceParam);
59481 riceParamPart3 |= (zeroCountPart3 << riceParam);
59482 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
59483 riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
59484 riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
59485 riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
59486 pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0);
59487 pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 1);
59488 pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 2);
59489 pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 3);
59490 pSamplesOut += 4;
59491 }
59492 } else {
59493 while (pSamplesOut < pSamplesOutEnd) {
59494 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
59495 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
59496 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
59497 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
59498 return DRFLAC_FALSE;
59499 }
59500 riceParamPart0 &= riceParamMask;
59501 riceParamPart1 &= riceParamMask;
59502 riceParamPart2 &= riceParamMask;
59503 riceParamPart3 &= riceParamMask;
59504 riceParamPart0 |= (zeroCountPart0 << riceParam);
59505 riceParamPart1 |= (zeroCountPart1 << riceParam);
59506 riceParamPart2 |= (zeroCountPart2 << riceParam);
59507 riceParamPart3 |= (zeroCountPart3 << riceParam);
59508 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
59509 riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
59510 riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
59511 riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
59512 pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
59513 pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 1);
59514 pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 2);
59515 pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 3);
59516 pSamplesOut += 4;
59517 }
59518 }
59519 i = (count & ~3);
59520 while (i < count) {
59521 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
59522 return DRFLAC_FALSE;
59523 }
59524 riceParamPart0 &= riceParamMask;
59525 riceParamPart0 |= (zeroCountPart0 << riceParam);
59526 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
59527 if (bitsPerSample+shift > 32) {
59528 pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0);
59529 } else {
59530 pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
59531 }
59532 i += 1;
59533 pSamplesOut += 1;
59534 }
59535 return DRFLAC_TRUE;
59536}
59537#if defined(DRFLAC_SUPPORT_SSE2)
59538static DRFLAC_INLINE __m128i drflac__mm_packs_interleaved_epi32(__m128i a, __m128i b)
59539{
59540 __m128i r;
59541 r = _mm_packs_epi32(a, b);
59542 r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0));
59543 r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));
59544 r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));
59545 return r;
59546}
59547#endif
59548#if defined(DRFLAC_SUPPORT_SSE41)
59549static DRFLAC_INLINE __m128i drflac__mm_not_si128(__m128i a)
59550{
59551 return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128()));
59552}
59553static DRFLAC_INLINE __m128i drflac__mm_hadd_epi32(__m128i x)
59554{
59555 __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));
59556 __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2));
59557 return _mm_add_epi32(x64, x32);
59558}
59559static DRFLAC_INLINE __m128i drflac__mm_hadd_epi64(__m128i x)
59560{
59561 return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));
59562}
59563static DRFLAC_INLINE __m128i drflac__mm_srai_epi64(__m128i x, int count)
59564{
59565 __m128i lo = _mm_srli_epi64(x, count);
59566 __m128i hi = _mm_srai_epi32(x, count);
59567 hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0));
59568 return _mm_or_si128(lo, hi);
59569}
59570static 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)
59571{
59572 int i;
59573 drflac_uint32 riceParamMask;
59574 drflac_int32* pDecodedSamples = pSamplesOut;
59575 drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
59576 drflac_uint32 zeroCountParts0 = 0;
59577 drflac_uint32 zeroCountParts1 = 0;
59578 drflac_uint32 zeroCountParts2 = 0;
59579 drflac_uint32 zeroCountParts3 = 0;
59580 drflac_uint32 riceParamParts0 = 0;
59581 drflac_uint32 riceParamParts1 = 0;
59582 drflac_uint32 riceParamParts2 = 0;
59583 drflac_uint32 riceParamParts3 = 0;
59584 __m128i coefficients128_0;
59585 __m128i coefficients128_4;
59586 __m128i coefficients128_8;
59587 __m128i samples128_0;
59588 __m128i samples128_4;
59589 __m128i samples128_8;
59590 __m128i riceParamMask128;
59591 const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
59592 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
59593 riceParamMask128 = _mm_set1_epi32(riceParamMask);
59594 coefficients128_0 = _mm_setzero_si128();
59595 coefficients128_4 = _mm_setzero_si128();
59596 coefficients128_8 = _mm_setzero_si128();
59597 samples128_0 = _mm_setzero_si128();
59598 samples128_4 = _mm_setzero_si128();
59599 samples128_8 = _mm_setzero_si128();
59600#if 1
59601 {
59602 int runningOrder = order;
59603 if (runningOrder >= 4) {
59604 coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));
59605 samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4));
59606 runningOrder -= 4;
59607 } else {
59608 switch (runningOrder) {
59609 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;
59610 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;
59611 case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break;
59612 }
59613 runningOrder = 0;
59614 }
59615 if (runningOrder >= 4) {
59616 coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));
59617 samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8));
59618 runningOrder -= 4;
59619 } else {
59620 switch (runningOrder) {
59621 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;
59622 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;
59623 case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break;
59624 }
59625 runningOrder = 0;
59626 }
59627 if (runningOrder == 4) {
59628 coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));
59629 samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12));
59630 runningOrder -= 4;
59631 } else {
59632 switch (runningOrder) {
59633 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;
59634 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;
59635 case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break;
59636 }
59637 runningOrder = 0;
59638 }
59639 coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));
59640 coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));
59641 coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));
59642 }
59643#else
59644 switch (order)
59645 {
59646 case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12];
59647 case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11];
59648 case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10];
59649 case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9];
59650 case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8];
59651 case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7];
59652 case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6];
59653 case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5];
59654 case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4];
59655 case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3];
59656 case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2];
59657 case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1];
59658 }
59659#endif
59660 while (pDecodedSamples < pDecodedSamplesEnd) {
59661 __m128i prediction128;
59662 __m128i zeroCountPart128;
59663 __m128i riceParamPart128;
59664 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
59665 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
59666 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
59667 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
59668 return DRFLAC_FALSE;
59669 }
59670 zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
59671 riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
59672 riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
59673 riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
59674 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)));
59675 if (order <= 4) {
59676 for (i = 0; i < 4; i += 1) {
59677 prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0);
59678 prediction128 = drflac__mm_hadd_epi32(prediction128);
59679 prediction128 = _mm_srai_epi32(prediction128, shift);
59680 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
59681 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
59682 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
59683 }
59684 } else if (order <= 8) {
59685 for (i = 0; i < 4; i += 1) {
59686 prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4);
59687 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));
59688 prediction128 = drflac__mm_hadd_epi32(prediction128);
59689 prediction128 = _mm_srai_epi32(prediction128, shift);
59690 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
59691 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
59692 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
59693 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
59694 }
59695 } else {
59696 for (i = 0; i < 4; i += 1) {
59697 prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8);
59698 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4));
59699 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));
59700 prediction128 = drflac__mm_hadd_epi32(prediction128);
59701 prediction128 = _mm_srai_epi32(prediction128, shift);
59702 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
59703 samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4);
59704 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
59705 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
59706 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
59707 }
59708 }
59709 _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);
59710 pDecodedSamples += 4;
59711 }
59712 i = (count & ~3);
59713 while (i < (int)count) {
59714 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
59715 return DRFLAC_FALSE;
59716 }
59717 riceParamParts0 &= riceParamMask;
59718 riceParamParts0 |= (zeroCountParts0 << riceParam);
59719 riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
59720 pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);
59721 i += 1;
59722 pDecodedSamples += 1;
59723 }
59724 return DRFLAC_TRUE;
59725}
59726static 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)
59727{
59728 int i;
59729 drflac_uint32 riceParamMask;
59730 drflac_int32* pDecodedSamples = pSamplesOut;
59731 drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
59732 drflac_uint32 zeroCountParts0 = 0;
59733 drflac_uint32 zeroCountParts1 = 0;
59734 drflac_uint32 zeroCountParts2 = 0;
59735 drflac_uint32 zeroCountParts3 = 0;
59736 drflac_uint32 riceParamParts0 = 0;
59737 drflac_uint32 riceParamParts1 = 0;
59738 drflac_uint32 riceParamParts2 = 0;
59739 drflac_uint32 riceParamParts3 = 0;
59740 __m128i coefficients128_0;
59741 __m128i coefficients128_4;
59742 __m128i coefficients128_8;
59743 __m128i samples128_0;
59744 __m128i samples128_4;
59745 __m128i samples128_8;
59746 __m128i prediction128;
59747 __m128i riceParamMask128;
59748 const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
59749 DRFLAC_ASSERT(order <= 12);
59750 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
59751 riceParamMask128 = _mm_set1_epi32(riceParamMask);
59752 prediction128 = _mm_setzero_si128();
59753 coefficients128_0 = _mm_setzero_si128();
59754 coefficients128_4 = _mm_setzero_si128();
59755 coefficients128_8 = _mm_setzero_si128();
59756 samples128_0 = _mm_setzero_si128();
59757 samples128_4 = _mm_setzero_si128();
59758 samples128_8 = _mm_setzero_si128();
59759#if 1
59760 {
59761 int runningOrder = order;
59762 if (runningOrder >= 4) {
59763 coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));
59764 samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4));
59765 runningOrder -= 4;
59766 } else {
59767 switch (runningOrder) {
59768 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;
59769 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;
59770 case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break;
59771 }
59772 runningOrder = 0;
59773 }
59774 if (runningOrder >= 4) {
59775 coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));
59776 samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8));
59777 runningOrder -= 4;
59778 } else {
59779 switch (runningOrder) {
59780 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;
59781 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;
59782 case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break;
59783 }
59784 runningOrder = 0;
59785 }
59786 if (runningOrder == 4) {
59787 coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));
59788 samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12));
59789 runningOrder -= 4;
59790 } else {
59791 switch (runningOrder) {
59792 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;
59793 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;
59794 case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break;
59795 }
59796 runningOrder = 0;
59797 }
59798 coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));
59799 coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));
59800 coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));
59801 }
59802#else
59803 switch (order)
59804 {
59805 case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12];
59806 case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11];
59807 case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10];
59808 case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9];
59809 case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8];
59810 case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7];
59811 case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6];
59812 case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5];
59813 case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4];
59814 case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3];
59815 case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2];
59816 case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1];
59817 }
59818#endif
59819 while (pDecodedSamples < pDecodedSamplesEnd) {
59820 __m128i zeroCountPart128;
59821 __m128i riceParamPart128;
59822 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
59823 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
59824 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
59825 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
59826 return DRFLAC_FALSE;
59827 }
59828 zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
59829 riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
59830 riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
59831 riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
59832 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)));
59833 for (i = 0; i < 4; i += 1) {
59834 prediction128 = _mm_xor_si128(prediction128, prediction128);
59835 switch (order)
59836 {
59837 case 12:
59838 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))));
59839 case 10:
59840 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))));
59841 case 8:
59842 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))));
59843 case 6:
59844 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))));
59845 case 4:
59846 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))));
59847 case 2:
59848 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))));
59849 }
59850 prediction128 = drflac__mm_hadd_epi64(prediction128);
59851 prediction128 = drflac__mm_srai_epi64(prediction128, shift);
59852 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
59853 samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4);
59854 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
59855 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
59856 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
59857 }
59858 _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);
59859 pDecodedSamples += 4;
59860 }
59861 i = (count & ~3);
59862 while (i < (int)count) {
59863 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
59864 return DRFLAC_FALSE;
59865 }
59866 riceParamParts0 &= riceParamMask;
59867 riceParamParts0 |= (zeroCountParts0 << riceParam);
59868 riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
59869 pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);
59870 i += 1;
59871 pDecodedSamples += 1;
59872 }
59873 return DRFLAC_TRUE;
59874}
59875static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(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)
59876{
59877 DRFLAC_ASSERT(bs != NULL);
59878 DRFLAC_ASSERT(pSamplesOut != NULL);
59879 if (order > 0 && order <= 12) {
59880 if (bitsPerSample+shift > 32) {
59881 return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, order, shift, coefficients, pSamplesOut);
59882 } else {
59883 return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, order, shift, coefficients, pSamplesOut);
59884 }
59885 } else {
59886 return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
59887 }
59888}
59889#endif
59890#if defined(DRFLAC_SUPPORT_NEON)
59891static DRFLAC_INLINE void drflac__vst2q_s32(drflac_int32* p, int32x4x2_t x)
59892{
59893 vst1q_s32(p+0, x.val[0]);
59894 vst1q_s32(p+4, x.val[1]);
59895}
59896static DRFLAC_INLINE void drflac__vst2q_u32(drflac_uint32* p, uint32x4x2_t x)
59897{
59898 vst1q_u32(p+0, x.val[0]);
59899 vst1q_u32(p+4, x.val[1]);
59900}
59901static DRFLAC_INLINE void drflac__vst2q_f32(float* p, float32x4x2_t x)
59902{
59903 vst1q_f32(p+0, x.val[0]);
59904 vst1q_f32(p+4, x.val[1]);
59905}
59906static DRFLAC_INLINE void drflac__vst2q_s16(drflac_int16* p, int16x4x2_t x)
59907{
59908 vst1q_s16(p, vcombine_s16(x.val[0], x.val[1]));
59909}
59910static DRFLAC_INLINE void drflac__vst2q_u16(drflac_uint16* p, uint16x4x2_t x)
59911{
59912 vst1q_u16(p, vcombine_u16(x.val[0], x.val[1]));
59913}
59914static DRFLAC_INLINE int32x4_t drflac__vdupq_n_s32x4(drflac_int32 x3, drflac_int32 x2, drflac_int32 x1, drflac_int32 x0)
59915{
59916 drflac_int32 x[4];
59917 x[3] = x3;
59918 x[2] = x2;
59919 x[1] = x1;
59920 x[0] = x0;
59921 return vld1q_s32(x);
59922}
59923static DRFLAC_INLINE int32x4_t drflac__valignrq_s32_1(int32x4_t a, int32x4_t b)
59924{
59925 return vextq_s32(b, a, 1);
59926}
59927static DRFLAC_INLINE uint32x4_t drflac__valignrq_u32_1(uint32x4_t a, uint32x4_t b)
59928{
59929 return vextq_u32(b, a, 1);
59930}
59931static DRFLAC_INLINE int32x2_t drflac__vhaddq_s32(int32x4_t x)
59932{
59933 int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x));
59934 return vpadd_s32(r, r);
59935}
59936static DRFLAC_INLINE int64x1_t drflac__vhaddq_s64(int64x2_t x)
59937{
59938 return vadd_s64(vget_high_s64(x), vget_low_s64(x));
59939}
59940static DRFLAC_INLINE int32x4_t drflac__vrevq_s32(int32x4_t x)
59941{
59942 return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x)));
59943}
59944static DRFLAC_INLINE int32x4_t drflac__vnotq_s32(int32x4_t x)
59945{
59946 return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF));
59947}
59948static DRFLAC_INLINE uint32x4_t drflac__vnotq_u32(uint32x4_t x)
59949{
59950 return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF));
59951}
59952static 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)
59953{
59954 int i;
59955 drflac_uint32 riceParamMask;
59956 drflac_int32* pDecodedSamples = pSamplesOut;
59957 drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
59958 drflac_uint32 zeroCountParts[4];
59959 drflac_uint32 riceParamParts[4];
59960 int32x4_t coefficients128_0;
59961 int32x4_t coefficients128_4;
59962 int32x4_t coefficients128_8;
59963 int32x4_t samples128_0;
59964 int32x4_t samples128_4;
59965 int32x4_t samples128_8;
59966 uint32x4_t riceParamMask128;
59967 int32x4_t riceParam128;
59968 int32x2_t shift64;
59969 uint32x4_t one128;
59970 const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
59971 riceParamMask = ~((~0UL) << riceParam);
59972 riceParamMask128 = vdupq_n_u32(riceParamMask);
59973 riceParam128 = vdupq_n_s32(riceParam);
59974 shift64 = vdup_n_s32(-shift);
59975 one128 = vdupq_n_u32(1);
59976 {
59977 int runningOrder = order;
59978 drflac_int32 tempC[4] = {0, 0, 0, 0};
59979 drflac_int32 tempS[4] = {0, 0, 0, 0};
59980 if (runningOrder >= 4) {
59981 coefficients128_0 = vld1q_s32(coefficients + 0);
59982 samples128_0 = vld1q_s32(pSamplesOut - 4);
59983 runningOrder -= 4;
59984 } else {
59985 switch (runningOrder) {
59986 case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3];
59987 case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2];
59988 case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1];
59989 }
59990 coefficients128_0 = vld1q_s32(tempC);
59991 samples128_0 = vld1q_s32(tempS);
59992 runningOrder = 0;
59993 }
59994 if (runningOrder >= 4) {
59995 coefficients128_4 = vld1q_s32(coefficients + 4);
59996 samples128_4 = vld1q_s32(pSamplesOut - 8);
59997 runningOrder -= 4;
59998 } else {
59999 switch (runningOrder) {
60000 case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7];
60001 case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6];
60002 case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5];
60003 }
60004 coefficients128_4 = vld1q_s32(tempC);
60005 samples128_4 = vld1q_s32(tempS);
60006 runningOrder = 0;
60007 }
60008 if (runningOrder == 4) {
60009 coefficients128_8 = vld1q_s32(coefficients + 8);
60010 samples128_8 = vld1q_s32(pSamplesOut - 12);
60011 runningOrder -= 4;
60012 } else {
60013 switch (runningOrder) {
60014 case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11];
60015 case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10];
60016 case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9];
60017 }
60018 coefficients128_8 = vld1q_s32(tempC);
60019 samples128_8 = vld1q_s32(tempS);
60020 runningOrder = 0;
60021 }
60022 coefficients128_0 = drflac__vrevq_s32(coefficients128_0);
60023 coefficients128_4 = drflac__vrevq_s32(coefficients128_4);
60024 coefficients128_8 = drflac__vrevq_s32(coefficients128_8);
60025 }
60026 while (pDecodedSamples < pDecodedSamplesEnd) {
60027 int32x4_t prediction128;
60028 int32x2_t prediction64;
60029 uint32x4_t zeroCountPart128;
60030 uint32x4_t riceParamPart128;
60031 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||
60032 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||
60033 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||
60034 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {
60035 return DRFLAC_FALSE;
60036 }
60037 zeroCountPart128 = vld1q_u32(zeroCountParts);
60038 riceParamPart128 = vld1q_u32(riceParamParts);
60039 riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);
60040 riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));
60041 riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));
60042 if (order <= 4) {
60043 for (i = 0; i < 4; i += 1) {
60044 prediction128 = vmulq_s32(coefficients128_0, samples128_0);
60045 prediction64 = drflac__vhaddq_s32(prediction128);
60046 prediction64 = vshl_s32(prediction64, shift64);
60047 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
60048 samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
60049 riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
60050 }
60051 } else if (order <= 8) {
60052 for (i = 0; i < 4; i += 1) {
60053 prediction128 = vmulq_s32(coefficients128_4, samples128_4);
60054 prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);
60055 prediction64 = drflac__vhaddq_s32(prediction128);
60056 prediction64 = vshl_s32(prediction64, shift64);
60057 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
60058 samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4);
60059 samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
60060 riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
60061 }
60062 } else {
60063 for (i = 0; i < 4; i += 1) {
60064 prediction128 = vmulq_s32(coefficients128_8, samples128_8);
60065 prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4);
60066 prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);
60067 prediction64 = drflac__vhaddq_s32(prediction128);
60068 prediction64 = vshl_s32(prediction64, shift64);
60069 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
60070 samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8);
60071 samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4);
60072 samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
60073 riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
60074 }
60075 }
60076 vst1q_s32(pDecodedSamples, samples128_0);
60077 pDecodedSamples += 4;
60078 }
60079 i = (count & ~3);
60080 while (i < (int)count) {
60081 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {
60082 return DRFLAC_FALSE;
60083 }
60084 riceParamParts[0] &= riceParamMask;
60085 riceParamParts[0] |= (zeroCountParts[0] << riceParam);
60086 riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];
60087 pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);
60088 i += 1;
60089 pDecodedSamples += 1;
60090 }
60091 return DRFLAC_TRUE;
60092}
60093static 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)
60094{
60095 int i;
60096 drflac_uint32 riceParamMask;
60097 drflac_int32* pDecodedSamples = pSamplesOut;
60098 drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
60099 drflac_uint32 zeroCountParts[4];
60100 drflac_uint32 riceParamParts[4];
60101 int32x4_t coefficients128_0;
60102 int32x4_t coefficients128_4;
60103 int32x4_t coefficients128_8;
60104 int32x4_t samples128_0;
60105 int32x4_t samples128_4;
60106 int32x4_t samples128_8;
60107 uint32x4_t riceParamMask128;
60108 int32x4_t riceParam128;
60109 int64x1_t shift64;
60110 uint32x4_t one128;
60111 const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
60112 riceParamMask = ~((~0UL) << riceParam);
60113 riceParamMask128 = vdupq_n_u32(riceParamMask);
60114 riceParam128 = vdupq_n_s32(riceParam);
60115 shift64 = vdup_n_s64(-shift);
60116 one128 = vdupq_n_u32(1);
60117 {
60118 int runningOrder = order;
60119 drflac_int32 tempC[4] = {0, 0, 0, 0};
60120 drflac_int32 tempS[4] = {0, 0, 0, 0};
60121 if (runningOrder >= 4) {
60122 coefficients128_0 = vld1q_s32(coefficients + 0);
60123 samples128_0 = vld1q_s32(pSamplesOut - 4);
60124 runningOrder -= 4;
60125 } else {
60126 switch (runningOrder) {
60127 case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3];
60128 case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2];
60129 case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1];
60130 }
60131 coefficients128_0 = vld1q_s32(tempC);
60132 samples128_0 = vld1q_s32(tempS);
60133 runningOrder = 0;
60134 }
60135 if (runningOrder >= 4) {
60136 coefficients128_4 = vld1q_s32(coefficients + 4);
60137 samples128_4 = vld1q_s32(pSamplesOut - 8);
60138 runningOrder -= 4;
60139 } else {
60140 switch (runningOrder) {
60141 case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7];
60142 case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6];
60143 case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5];
60144 }
60145 coefficients128_4 = vld1q_s32(tempC);
60146 samples128_4 = vld1q_s32(tempS);
60147 runningOrder = 0;
60148 }
60149 if (runningOrder == 4) {
60150 coefficients128_8 = vld1q_s32(coefficients + 8);
60151 samples128_8 = vld1q_s32(pSamplesOut - 12);
60152 runningOrder -= 4;
60153 } else {
60154 switch (runningOrder) {
60155 case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11];
60156 case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10];
60157 case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9];
60158 }
60159 coefficients128_8 = vld1q_s32(tempC);
60160 samples128_8 = vld1q_s32(tempS);
60161 runningOrder = 0;
60162 }
60163 coefficients128_0 = drflac__vrevq_s32(coefficients128_0);
60164 coefficients128_4 = drflac__vrevq_s32(coefficients128_4);
60165 coefficients128_8 = drflac__vrevq_s32(coefficients128_8);
60166 }
60167 while (pDecodedSamples < pDecodedSamplesEnd) {
60168 int64x2_t prediction128;
60169 uint32x4_t zeroCountPart128;
60170 uint32x4_t riceParamPart128;
60171 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||
60172 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||
60173 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||
60174 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {
60175 return DRFLAC_FALSE;
60176 }
60177 zeroCountPart128 = vld1q_u32(zeroCountParts);
60178 riceParamPart128 = vld1q_u32(riceParamParts);
60179 riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);
60180 riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));
60181 riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));
60182 for (i = 0; i < 4; i += 1) {
60183 int64x1_t prediction64;
60184 prediction128 = veorq_s64(prediction128, prediction128);
60185 switch (order)
60186 {
60187 case 12:
60188 case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8)));
60189 case 10:
60190 case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8)));
60191 case 8:
60192 case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4)));
60193 case 6:
60194 case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4)));
60195 case 4:
60196 case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0)));
60197 case 2:
60198 case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0)));
60199 }
60200 prediction64 = drflac__vhaddq_s64(prediction128);
60201 prediction64 = vshl_s64(prediction64, shift64);
60202 prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0)));
60203 samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8);
60204 samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4);
60205 samples128_0 = drflac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0);
60206 riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
60207 }
60208 vst1q_s32(pDecodedSamples, samples128_0);
60209 pDecodedSamples += 4;
60210 }
60211 i = (count & ~3);
60212 while (i < (int)count) {
60213 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {
60214 return DRFLAC_FALSE;
60215 }
60216 riceParamParts[0] &= riceParamMask;
60217 riceParamParts[0] |= (zeroCountParts[0] << riceParam);
60218 riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];
60219 pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);
60220 i += 1;
60221 pDecodedSamples += 1;
60222 }
60223 return DRFLAC_TRUE;
60224}
60225static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(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)
60226{
60227 DRFLAC_ASSERT(bs != NULL);
60228 DRFLAC_ASSERT(pSamplesOut != NULL);
60229 if (order > 0 && order <= 12) {
60230 if (bitsPerSample+shift > 32) {
60231 return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, order, shift, coefficients, pSamplesOut);
60232 } else {
60233 return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, order, shift, coefficients, pSamplesOut);
60234 }
60235 } else {
60236 return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
60237 }
60238}
60239#endif
60240static drflac_bool32 drflac__decode_samples_with_residual__rice(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)
60241{
60242#if defined(DRFLAC_SUPPORT_SSE41)
60243 if (drflac__gIsSSE41Supported) {
60244 return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
60245 } else
60246#elif defined(DRFLAC_SUPPORT_NEON)
60247 if (drflac__gIsNEONSupported) {
60248 return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
60249 } else
60250#endif
60251 {
60252 #if 0
60253 return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
60254 #else
60255 return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
60256 #endif
60257 }
60258}
60259static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam)
60260{
60261 drflac_uint32 i;
60262 DRFLAC_ASSERT(bs != NULL);
60263 for (i = 0; i < count; ++i) {
60264 if (!drflac__seek_rice_parts(bs, riceParam)) {
60265 return DRFLAC_FALSE;
60266 }
60267 }
60268 return DRFLAC_TRUE;
60269}
60270static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
60271{
60272 drflac_uint32 i;
60273 DRFLAC_ASSERT(bs != NULL);
60274 DRFLAC_ASSERT(unencodedBitsPerSample <= 31);
60275 DRFLAC_ASSERT(pSamplesOut != NULL);
60276 for (i = 0; i < count; ++i) {
60277 if (unencodedBitsPerSample > 0) {
60278 if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) {
60279 return DRFLAC_FALSE;
60280 }
60281 } else {
60282 pSamplesOut[i] = 0;
60283 }
60284 if (bitsPerSample >= 24) {
60285 pSamplesOut[i] += drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i);
60286 } else {
60287 pSamplesOut[i] += drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i);
60288 }
60289 }
60290 return DRFLAC_TRUE;
60291}
60292static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
60293{
60294 drflac_uint8 residualMethod;
60295 drflac_uint8 partitionOrder;
60296 drflac_uint32 samplesInPartition;
60297 drflac_uint32 partitionsRemaining;
60298 DRFLAC_ASSERT(bs != NULL);
60299 DRFLAC_ASSERT(blockSize != 0);
60300 DRFLAC_ASSERT(pDecodedSamples != NULL);
60301 if (!drflac__read_uint8(bs, 2, &residualMethod)) {
60302 return DRFLAC_FALSE;
60303 }
60304 if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
60305 return DRFLAC_FALSE;
60306 }
60307 pDecodedSamples += order;
60308 if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
60309 return DRFLAC_FALSE;
60310 }
60311 if (partitionOrder > 8) {
60312 return DRFLAC_FALSE;
60313 }
60314 if ((blockSize / (1 << partitionOrder)) < order) {
60315 return DRFLAC_FALSE;
60316 }
60317 samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
60318 partitionsRemaining = (1 << partitionOrder);
60319 for (;;) {
60320 drflac_uint8 riceParam = 0;
60321 if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
60322 if (!drflac__read_uint8(bs, 4, &riceParam)) {
60323 return DRFLAC_FALSE;
60324 }
60325 if (riceParam == 15) {
60326 riceParam = 0xFF;
60327 }
60328 } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
60329 if (!drflac__read_uint8(bs, 5, &riceParam)) {
60330 return DRFLAC_FALSE;
60331 }
60332 if (riceParam == 31) {
60333 riceParam = 0xFF;
60334 }
60335 }
60336 if (riceParam != 0xFF) {
60337 if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) {
60338 return DRFLAC_FALSE;
60339 }
60340 } else {
60341 drflac_uint8 unencodedBitsPerSample = 0;
60342 if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
60343 return DRFLAC_FALSE;
60344 }
60345 if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, order, shift, coefficients, pDecodedSamples)) {
60346 return DRFLAC_FALSE;
60347 }
60348 }
60349 pDecodedSamples += samplesInPartition;
60350 if (partitionsRemaining == 1) {
60351 break;
60352 }
60353 partitionsRemaining -= 1;
60354 if (partitionOrder != 0) {
60355 samplesInPartition = blockSize / (1 << partitionOrder);
60356 }
60357 }
60358 return DRFLAC_TRUE;
60359}
60360static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order)
60361{
60362 drflac_uint8 residualMethod;
60363 drflac_uint8 partitionOrder;
60364 drflac_uint32 samplesInPartition;
60365 drflac_uint32 partitionsRemaining;
60366 DRFLAC_ASSERT(bs != NULL);
60367 DRFLAC_ASSERT(blockSize != 0);
60368 if (!drflac__read_uint8(bs, 2, &residualMethod)) {
60369 return DRFLAC_FALSE;
60370 }
60371 if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
60372 return DRFLAC_FALSE;
60373 }
60374 if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
60375 return DRFLAC_FALSE;
60376 }
60377 if (partitionOrder > 8) {
60378 return DRFLAC_FALSE;
60379 }
60380 if ((blockSize / (1 << partitionOrder)) <= order) {
60381 return DRFLAC_FALSE;
60382 }
60383 samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
60384 partitionsRemaining = (1 << partitionOrder);
60385 for (;;)
60386 {
60387 drflac_uint8 riceParam = 0;
60388 if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
60389 if (!drflac__read_uint8(bs, 4, &riceParam)) {
60390 return DRFLAC_FALSE;
60391 }
60392 if (riceParam == 15) {
60393 riceParam = 0xFF;
60394 }
60395 } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
60396 if (!drflac__read_uint8(bs, 5, &riceParam)) {
60397 return DRFLAC_FALSE;
60398 }
60399 if (riceParam == 31) {
60400 riceParam = 0xFF;
60401 }
60402 }
60403 if (riceParam != 0xFF) {
60404 if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) {
60405 return DRFLAC_FALSE;
60406 }
60407 } else {
60408 drflac_uint8 unencodedBitsPerSample = 0;
60409 if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
60410 return DRFLAC_FALSE;
60411 }
60412 if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) {
60413 return DRFLAC_FALSE;
60414 }
60415 }
60416 if (partitionsRemaining == 1) {
60417 break;
60418 }
60419 partitionsRemaining -= 1;
60420 samplesInPartition = blockSize / (1 << partitionOrder);
60421 }
60422 return DRFLAC_TRUE;
60423}
60424static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples)
60425{
60426 drflac_uint32 i;
60427 drflac_int32 sample;
60428 if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) {
60429 return DRFLAC_FALSE;
60430 }
60431 for (i = 0; i < blockSize; ++i) {
60432 pDecodedSamples[i] = sample;
60433 }
60434 return DRFLAC_TRUE;
60435}
60436static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples)
60437{
60438 drflac_uint32 i;
60439 for (i = 0; i < blockSize; ++i) {
60440 drflac_int32 sample;
60441 if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) {
60442 return DRFLAC_FALSE;
60443 }
60444 pDecodedSamples[i] = sample;
60445 }
60446 return DRFLAC_TRUE;
60447}
60448static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples)
60449{
60450 drflac_uint32 i;
60451 static drflac_int32 lpcCoefficientsTable[5][4] = {
60452 {0, 0, 0, 0},
60453 {1, 0, 0, 0},
60454 {2, -1, 0, 0},
60455 {3, -3, 1, 0},
60456 {4, -6, 4, -1}
60457 };
60458 for (i = 0; i < lpcOrder; ++i) {
60459 drflac_int32 sample;
60460 if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) {
60461 return DRFLAC_FALSE;
60462 }
60463 pDecodedSamples[i] = sample;
60464 }
60465 if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) {
60466 return DRFLAC_FALSE;
60467 }
60468 return DRFLAC_TRUE;
60469}
60470static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples)
60471{
60472 drflac_uint8 i;
60473 drflac_uint8 lpcPrecision;
60474 drflac_int8 lpcShift;
60475 drflac_int32 coefficients[32];
60476 for (i = 0; i < lpcOrder; ++i) {
60477 drflac_int32 sample;
60478 if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
60479 return DRFLAC_FALSE;
60480 }
60481 pDecodedSamples[i] = sample;
60482 }
60483 if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
60484 return DRFLAC_FALSE;
60485 }
60486 if (lpcPrecision == 15) {
60487 return DRFLAC_FALSE;
60488 }
60489 lpcPrecision += 1;
60490 if (!drflac__read_int8(bs, 5, &lpcShift)) {
60491 return DRFLAC_FALSE;
60492 }
60493 if (lpcShift < 0) {
60494 return DRFLAC_FALSE;
60495 }
60496 DRFLAC_ZERO_MEMORY(coefficients, sizeof(coefficients));
60497 for (i = 0; i < lpcOrder; ++i) {
60498 if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) {
60499 return DRFLAC_FALSE;
60500 }
60501 }
60502 if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, coefficients, pDecodedSamples)) {
60503 return DRFLAC_FALSE;
60504 }
60505 return DRFLAC_TRUE;
60506}
60507static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header)
60508{
60509 const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000};
60510 const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1};
60511 DRFLAC_ASSERT(bs != NULL);
60512 DRFLAC_ASSERT(header != NULL);
60513 for (;;) {
60514 drflac_uint8 crc8 = 0xCE;
60515 drflac_uint8 reserved = 0;
60516 drflac_uint8 blockingStrategy = 0;
60517 drflac_uint8 blockSize = 0;
60518 drflac_uint8 sampleRate = 0;
60519 drflac_uint8 channelAssignment = 0;
60520 drflac_uint8 bitsPerSample = 0;
60521 drflac_bool32 isVariableBlockSize;
60522 if (!drflac__find_and_seek_to_next_sync_code(bs)) {
60523 return DRFLAC_FALSE;
60524 }
60525 if (!drflac__read_uint8(bs, 1, &reserved)) {
60526 return DRFLAC_FALSE;
60527 }
60528 if (reserved == 1) {
60529 continue;
60530 }
60531 crc8 = drflac_crc8(crc8, reserved, 1);
60532 if (!drflac__read_uint8(bs, 1, &blockingStrategy)) {
60533 return DRFLAC_FALSE;
60534 }
60535 crc8 = drflac_crc8(crc8, blockingStrategy, 1);
60536 if (!drflac__read_uint8(bs, 4, &blockSize)) {
60537 return DRFLAC_FALSE;
60538 }
60539 if (blockSize == 0) {
60540 continue;
60541 }
60542 crc8 = drflac_crc8(crc8, blockSize, 4);
60543 if (!drflac__read_uint8(bs, 4, &sampleRate)) {
60544 return DRFLAC_FALSE;
60545 }
60546 crc8 = drflac_crc8(crc8, sampleRate, 4);
60547 if (!drflac__read_uint8(bs, 4, &channelAssignment)) {
60548 return DRFLAC_FALSE;
60549 }
60550 if (channelAssignment > 10) {
60551 continue;
60552 }
60553 crc8 = drflac_crc8(crc8, channelAssignment, 4);
60554 if (!drflac__read_uint8(bs, 3, &bitsPerSample)) {
60555 return DRFLAC_FALSE;
60556 }
60557 if (bitsPerSample == 3 || bitsPerSample == 7) {
60558 continue;
60559 }
60560 crc8 = drflac_crc8(crc8, bitsPerSample, 3);
60561 if (!drflac__read_uint8(bs, 1, &reserved)) {
60562 return DRFLAC_FALSE;
60563 }
60564 if (reserved == 1) {
60565 continue;
60566 }
60567 crc8 = drflac_crc8(crc8, reserved, 1);
60568 isVariableBlockSize = blockingStrategy == 1;
60569 if (isVariableBlockSize) {
60570 drflac_uint64 pcmFrameNumber;
60571 drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8);
60572 if (result != DRFLAC_SUCCESS) {
60573 if (result == DRFLAC_AT_END) {
60574 return DRFLAC_FALSE;
60575 } else {
60576 continue;
60577 }
60578 }
60579 header->flacFrameNumber = 0;
60580 header->pcmFrameNumber = pcmFrameNumber;
60581 } else {
60582 drflac_uint64 flacFrameNumber = 0;
60583 drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8);
60584 if (result != DRFLAC_SUCCESS) {
60585 if (result == DRFLAC_AT_END) {
60586 return DRFLAC_FALSE;
60587 } else {
60588 continue;
60589 }
60590 }
60591 header->flacFrameNumber = (drflac_uint32)flacFrameNumber;
60592 header->pcmFrameNumber = 0;
60593 }
60594 DRFLAC_ASSERT(blockSize > 0);
60595 if (blockSize == 1) {
60596 header->blockSizeInPCMFrames = 192;
60597 } else if (blockSize <= 5) {
60598 DRFLAC_ASSERT(blockSize >= 2);
60599 header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2));
60600 } else if (blockSize == 6) {
60601 if (!drflac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) {
60602 return DRFLAC_FALSE;
60603 }
60604 crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 8);
60605 header->blockSizeInPCMFrames += 1;
60606 } else if (blockSize == 7) {
60607 if (!drflac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) {
60608 return DRFLAC_FALSE;
60609 }
60610 crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16);
60611 header->blockSizeInPCMFrames += 1;
60612 } else {
60613 DRFLAC_ASSERT(blockSize >= 8);
60614 header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8));
60615 }
60616 if (sampleRate <= 11) {
60617 header->sampleRate = sampleRateTable[sampleRate];
60618 } else if (sampleRate == 12) {
60619 if (!drflac__read_uint32(bs, 8, &header->sampleRate)) {
60620 return DRFLAC_FALSE;
60621 }
60622 crc8 = drflac_crc8(crc8, header->sampleRate, 8);
60623 header->sampleRate *= 1000;
60624 } else if (sampleRate == 13) {
60625 if (!drflac__read_uint32(bs, 16, &header->sampleRate)) {
60626 return DRFLAC_FALSE;
60627 }
60628 crc8 = drflac_crc8(crc8, header->sampleRate, 16);
60629 } else if (sampleRate == 14) {
60630 if (!drflac__read_uint32(bs, 16, &header->sampleRate)) {
60631 return DRFLAC_FALSE;
60632 }
60633 crc8 = drflac_crc8(crc8, header->sampleRate, 16);
60634 header->sampleRate *= 10;
60635 } else {
60636 continue;
60637 }
60638 header->channelAssignment = channelAssignment;
60639 header->bitsPerSample = bitsPerSampleTable[bitsPerSample];
60640 if (header->bitsPerSample == 0) {
60641 header->bitsPerSample = streaminfoBitsPerSample;
60642 }
60643 if (!drflac__read_uint8(bs, 8, &header->crc8)) {
60644 return DRFLAC_FALSE;
60645 }
60646#ifndef DR_FLAC_NO_CRC
60647 if (header->crc8 != crc8) {
60648 continue;
60649 }
60650#endif
60651 return DRFLAC_TRUE;
60652 }
60653}
60654static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe)
60655{
60656 drflac_uint8 header;
60657 int type;
60658 if (!drflac__read_uint8(bs, 8, &header)) {
60659 return DRFLAC_FALSE;
60660 }
60661 if ((header & 0x80) != 0) {
60662 return DRFLAC_FALSE;
60663 }
60664 type = (header & 0x7E) >> 1;
60665 if (type == 0) {
60666 pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT;
60667 } else if (type == 1) {
60668 pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM;
60669 } else {
60670 if ((type & 0x20) != 0) {
60671 pSubframe->subframeType = DRFLAC_SUBFRAME_LPC;
60672 pSubframe->lpcOrder = (drflac_uint8)(type & 0x1F) + 1;
60673 } else if ((type & 0x08) != 0) {
60674 pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED;
60675 pSubframe->lpcOrder = (drflac_uint8)(type & 0x07);
60676 if (pSubframe->lpcOrder > 4) {
60677 pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED;
60678 pSubframe->lpcOrder = 0;
60679 }
60680 } else {
60681 pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED;
60682 }
60683 }
60684 if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) {
60685 return DRFLAC_FALSE;
60686 }
60687 pSubframe->wastedBitsPerSample = 0;
60688 if ((header & 0x01) == 1) {
60689 unsigned int wastedBitsPerSample;
60690 if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) {
60691 return DRFLAC_FALSE;
60692 }
60693 pSubframe->wastedBitsPerSample = (drflac_uint8)wastedBitsPerSample + 1;
60694 }
60695 return DRFLAC_TRUE;
60696}
60697static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut)
60698{
60699 drflac_subframe* pSubframe;
60700 drflac_uint32 subframeBitsPerSample;
60701 DRFLAC_ASSERT(bs != NULL);
60702 DRFLAC_ASSERT(frame != NULL);
60703 pSubframe = frame->subframes + subframeIndex;
60704 if (!drflac__read_subframe_header(bs, pSubframe)) {
60705 return DRFLAC_FALSE;
60706 }
60707 subframeBitsPerSample = frame->header.bitsPerSample;
60708 if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
60709 subframeBitsPerSample += 1;
60710 } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
60711 subframeBitsPerSample += 1;
60712 }
60713 if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
60714 return DRFLAC_FALSE;
60715 }
60716 subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
60717 pSubframe->pSamplesS32 = pDecodedSamplesOut;
60718 switch (pSubframe->subframeType)
60719 {
60720 case DRFLAC_SUBFRAME_CONSTANT:
60721 {
60722 drflac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
60723 } break;
60724 case DRFLAC_SUBFRAME_VERBATIM:
60725 {
60726 drflac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
60727 } break;
60728 case DRFLAC_SUBFRAME_FIXED:
60729 {
60730 drflac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
60731 } break;
60732 case DRFLAC_SUBFRAME_LPC:
60733 {
60734 drflac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
60735 } break;
60736 default: return DRFLAC_FALSE;
60737 }
60738 return DRFLAC_TRUE;
60739}
60740static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex)
60741{
60742 drflac_subframe* pSubframe;
60743 drflac_uint32 subframeBitsPerSample;
60744 DRFLAC_ASSERT(bs != NULL);
60745 DRFLAC_ASSERT(frame != NULL);
60746 pSubframe = frame->subframes + subframeIndex;
60747 if (!drflac__read_subframe_header(bs, pSubframe)) {
60748 return DRFLAC_FALSE;
60749 }
60750 subframeBitsPerSample = frame->header.bitsPerSample;
60751 if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
60752 subframeBitsPerSample += 1;
60753 } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
60754 subframeBitsPerSample += 1;
60755 }
60756 if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
60757 return DRFLAC_FALSE;
60758 }
60759 subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
60760 pSubframe->pSamplesS32 = NULL;
60761 switch (pSubframe->subframeType)
60762 {
60763 case DRFLAC_SUBFRAME_CONSTANT:
60764 {
60765 if (!drflac__seek_bits(bs, subframeBitsPerSample)) {
60766 return DRFLAC_FALSE;
60767 }
60768 } break;
60769 case DRFLAC_SUBFRAME_VERBATIM:
60770 {
60771 unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample;
60772 if (!drflac__seek_bits(bs, bitsToSeek)) {
60773 return DRFLAC_FALSE;
60774 }
60775 } break;
60776 case DRFLAC_SUBFRAME_FIXED:
60777 {
60778 unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;
60779 if (!drflac__seek_bits(bs, bitsToSeek)) {
60780 return DRFLAC_FALSE;
60781 }
60782 if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {
60783 return DRFLAC_FALSE;
60784 }
60785 } break;
60786 case DRFLAC_SUBFRAME_LPC:
60787 {
60788 drflac_uint8 lpcPrecision;
60789 unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;
60790 if (!drflac__seek_bits(bs, bitsToSeek)) {
60791 return DRFLAC_FALSE;
60792 }
60793 if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
60794 return DRFLAC_FALSE;
60795 }
60796 if (lpcPrecision == 15) {
60797 return DRFLAC_FALSE;
60798 }
60799 lpcPrecision += 1;
60800 bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5;
60801 if (!drflac__seek_bits(bs, bitsToSeek)) {
60802 return DRFLAC_FALSE;
60803 }
60804 if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {
60805 return DRFLAC_FALSE;
60806 }
60807 } break;
60808 default: return DRFLAC_FALSE;
60809 }
60810 return DRFLAC_TRUE;
60811}
60812static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment)
60813{
60814 drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2};
60815 DRFLAC_ASSERT(channelAssignment <= 10);
60816 return lookup[channelAssignment];
60817}
60818static drflac_result drflac__decode_flac_frame(drflac* pFlac)
60819{
60820 int channelCount;
60821 int i;
60822 drflac_uint8 paddingSizeInBits;
60823 drflac_uint16 desiredCRC16;
60824#ifndef DR_FLAC_NO_CRC
60825 drflac_uint16 actualCRC16;
60826#endif
60827 DRFLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes));
60828 if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) {
60829 return DRFLAC_ERROR;
60830 }
60831 channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
60832 if (channelCount != (int)pFlac->channels) {
60833 return DRFLAC_ERROR;
60834 }
60835 for (i = 0; i < channelCount; ++i) {
60836 if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) {
60837 return DRFLAC_ERROR;
60838 }
60839 }
60840 paddingSizeInBits = (drflac_uint8)(DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7);
60841 if (paddingSizeInBits > 0) {
60842 drflac_uint8 padding = 0;
60843 if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) {
60844 return DRFLAC_AT_END;
60845 }
60846 }
60847#ifndef DR_FLAC_NO_CRC
60848 actualCRC16 = drflac__flush_crc16(&pFlac->bs);
60849#endif
60850 if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
60851 return DRFLAC_AT_END;
60852 }
60853#ifndef DR_FLAC_NO_CRC
60854 if (actualCRC16 != desiredCRC16) {
60855 return DRFLAC_CRC_MISMATCH;
60856 }
60857#endif
60858 pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
60859 return DRFLAC_SUCCESS;
60860}
60861static drflac_result drflac__seek_flac_frame(drflac* pFlac)
60862{
60863 int channelCount;
60864 int i;
60865 drflac_uint16 desiredCRC16;
60866#ifndef DR_FLAC_NO_CRC
60867 drflac_uint16 actualCRC16;
60868#endif
60869 channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
60870 for (i = 0; i < channelCount; ++i) {
60871 if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) {
60872 return DRFLAC_ERROR;
60873 }
60874 }
60875 if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) {
60876 return DRFLAC_ERROR;
60877 }
60878#ifndef DR_FLAC_NO_CRC
60879 actualCRC16 = drflac__flush_crc16(&pFlac->bs);
60880#endif
60881 if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
60882 return DRFLAC_AT_END;
60883 }
60884#ifndef DR_FLAC_NO_CRC
60885 if (actualCRC16 != desiredCRC16) {
60886 return DRFLAC_CRC_MISMATCH;
60887 }
60888#endif
60889 return DRFLAC_SUCCESS;
60890}
60891static drflac_bool32 drflac__read_and_decode_next_flac_frame(drflac* pFlac)
60892{
60893 DRFLAC_ASSERT(pFlac != NULL);
60894 for (;;) {
60895 drflac_result result;
60896 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
60897 return DRFLAC_FALSE;
60898 }
60899 result = drflac__decode_flac_frame(pFlac);
60900 if (result != DRFLAC_SUCCESS) {
60901 if (result == DRFLAC_CRC_MISMATCH) {
60902 continue;
60903 } else {
60904 return DRFLAC_FALSE;
60905 }
60906 }
60907 return DRFLAC_TRUE;
60908 }
60909}
60910static void drflac__get_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drflac_uint64* pFirstPCMFrame, drflac_uint64* pLastPCMFrame)
60911{
60912 drflac_uint64 firstPCMFrame;
60913 drflac_uint64 lastPCMFrame;
60914 DRFLAC_ASSERT(pFlac != NULL);
60915 firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber;
60916 if (firstPCMFrame == 0) {
60917 firstPCMFrame = ((drflac_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames;
60918 }
60919 lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
60920 if (lastPCMFrame > 0) {
60921 lastPCMFrame -= 1;
60922 }
60923 if (pFirstPCMFrame) {
60924 *pFirstPCMFrame = firstPCMFrame;
60925 }
60926 if (pLastPCMFrame) {
60927 *pLastPCMFrame = lastPCMFrame;
60928 }
60929}
60930static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac)
60931{
60932 drflac_bool32 result;
60933 DRFLAC_ASSERT(pFlac != NULL);
60934 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes);
60935 DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));
60936 pFlac->currentPCMFrame = 0;
60937 return result;
60938}
60939static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac)
60940{
60941 DRFLAC_ASSERT(pFlac != NULL);
60942 return drflac__seek_flac_frame(pFlac);
60943}
60944static drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek)
60945{
60946 drflac_uint64 pcmFramesRead = 0;
60947 while (pcmFramesToSeek > 0) {
60948 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
60949 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
60950 break;
60951 }
60952 } else {
60953 if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) {
60954 pcmFramesRead += pcmFramesToSeek;
60955 pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)pcmFramesToSeek;
60956 pcmFramesToSeek = 0;
60957 } else {
60958 pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining;
60959 pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining;
60960 pFlac->currentFLACFrame.pcmFramesRemaining = 0;
60961 }
60962 }
60963 }
60964 pFlac->currentPCMFrame += pcmFramesRead;
60965 return pcmFramesRead;
60966}
60967static drflac_bool32 drflac__seek_to_pcm_frame__brute_force(drflac* pFlac, drflac_uint64 pcmFrameIndex)
60968{
60969 drflac_bool32 isMidFrame = DRFLAC_FALSE;
60970 drflac_uint64 runningPCMFrameCount;
60971 DRFLAC_ASSERT(pFlac != NULL);
60972 if (pcmFrameIndex >= pFlac->currentPCMFrame) {
60973 runningPCMFrameCount = pFlac->currentPCMFrame;
60974 if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
60975 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
60976 return DRFLAC_FALSE;
60977 }
60978 } else {
60979 isMidFrame = DRFLAC_TRUE;
60980 }
60981 } else {
60982 runningPCMFrameCount = 0;
60983 if (!drflac__seek_to_first_frame(pFlac)) {
60984 return DRFLAC_FALSE;
60985 }
60986 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
60987 return DRFLAC_FALSE;
60988 }
60989 }
60990 for (;;) {
60991 drflac_uint64 pcmFrameCountInThisFLACFrame;
60992 drflac_uint64 firstPCMFrameInFLACFrame = 0;
60993 drflac_uint64 lastPCMFrameInFLACFrame = 0;
60994 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
60995 pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
60996 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {
60997 drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;
60998 if (!isMidFrame) {
60999 drflac_result result = drflac__decode_flac_frame(pFlac);
61000 if (result == DRFLAC_SUCCESS) {
61001 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
61002 } else {
61003 if (result == DRFLAC_CRC_MISMATCH) {
61004 goto next_iteration;
61005 } else {
61006 return DRFLAC_FALSE;
61007 }
61008 }
61009 } else {
61010 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
61011 }
61012 } else {
61013 if (!isMidFrame) {
61014 drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
61015 if (result == DRFLAC_SUCCESS) {
61016 runningPCMFrameCount += pcmFrameCountInThisFLACFrame;
61017 } else {
61018 if (result == DRFLAC_CRC_MISMATCH) {
61019 goto next_iteration;
61020 } else {
61021 return DRFLAC_FALSE;
61022 }
61023 }
61024 } else {
61025 runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;
61026 pFlac->currentFLACFrame.pcmFramesRemaining = 0;
61027 isMidFrame = DRFLAC_FALSE;
61028 }
61029 if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {
61030 return DRFLAC_TRUE;
61031 }
61032 }
61033 next_iteration:
61034 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
61035 return DRFLAC_FALSE;
61036 }
61037 }
61038}
61039#if !defined(DR_FLAC_NO_CRC)
61040#define DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f
61041static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFlac, drflac_uint64 targetByte, drflac_uint64 rangeLo, drflac_uint64 rangeHi, drflac_uint64* pLastSuccessfulSeekOffset)
61042{
61043 DRFLAC_ASSERT(pFlac != NULL);
61044 DRFLAC_ASSERT(pLastSuccessfulSeekOffset != NULL);
61045 DRFLAC_ASSERT(targetByte >= rangeLo);
61046 DRFLAC_ASSERT(targetByte <= rangeHi);
61047 *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes;
61048 for (;;) {
61049 drflac_uint64 lastTargetByte = targetByte;
61050 if (!drflac__seek_to_byte(&pFlac->bs, targetByte)) {
61051 if (targetByte == 0) {
61052 drflac__seek_to_first_frame(pFlac);
61053 return DRFLAC_FALSE;
61054 }
61055 targetByte = rangeLo + ((rangeHi - rangeLo)/2);
61056 rangeHi = targetByte;
61057 } else {
61058 DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));
61059#if 1
61060 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
61061 targetByte = rangeLo + ((rangeHi - rangeLo)/2);
61062 rangeHi = targetByte;
61063 } else {
61064 break;
61065 }
61066#else
61067 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
61068 targetByte = rangeLo + ((rangeHi - rangeLo)/2);
61069 rangeHi = targetByte;
61070 } else {
61071 break;
61072 }
61073#endif
61074 }
61075 if(targetByte == lastTargetByte) {
61076 return DRFLAC_FALSE;
61077 }
61078 }
61079 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);
61080 DRFLAC_ASSERT(targetByte <= rangeHi);
61081 *pLastSuccessfulSeekOffset = targetByte;
61082 return DRFLAC_TRUE;
61083}
61084static drflac_bool32 drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 offset)
61085{
61086#if 0
61087 if (drflac__decode_flac_frame(pFlac) != DRFLAC_SUCCESS) {
61088 if (drflac__read_and_decode_next_flac_frame(pFlac) == DRFLAC_FALSE) {
61089 return DRFLAC_FALSE;
61090 }
61091 }
61092#endif
61093 return drflac__seek_forward_by_pcm_frames(pFlac, offset) == offset;
61094}
61095static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* pFlac, drflac_uint64 pcmFrameIndex, drflac_uint64 byteRangeLo, drflac_uint64 byteRangeHi)
61096{
61097 drflac_uint64 targetByte;
61098 drflac_uint64 pcmRangeLo = pFlac->totalPCMFrameCount;
61099 drflac_uint64 pcmRangeHi = 0;
61100 drflac_uint64 lastSuccessfulSeekOffset = (drflac_uint64)-1;
61101 drflac_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo;
61102 drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;
61103 targetByte = byteRangeLo + (drflac_uint64)(((drflac_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO);
61104 if (targetByte > byteRangeHi) {
61105 targetByte = byteRangeHi;
61106 }
61107 for (;;) {
61108 if (drflac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) {
61109 drflac_uint64 newPCMRangeLo;
61110 drflac_uint64 newPCMRangeHi;
61111 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi);
61112 if (pcmRangeLo == newPCMRangeLo) {
61113 if (!drflac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) {
61114 break;
61115 }
61116 if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {
61117 return DRFLAC_TRUE;
61118 } else {
61119 break;
61120 }
61121 }
61122 pcmRangeLo = newPCMRangeLo;
61123 pcmRangeHi = newPCMRangeHi;
61124 if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) {
61125 if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) {
61126 return DRFLAC_TRUE;
61127 } else {
61128 break;
61129 }
61130 } else {
61131 const float approxCompressionRatio = (drflac_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((drflac_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f);
61132 if (pcmRangeLo > pcmFrameIndex) {
61133 byteRangeHi = lastSuccessfulSeekOffset;
61134 if (byteRangeLo > byteRangeHi) {
61135 byteRangeLo = byteRangeHi;
61136 }
61137 targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2);
61138 if (targetByte < byteRangeLo) {
61139 targetByte = byteRangeLo;
61140 }
61141 } else {
61142 if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) {
61143 if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {
61144 return DRFLAC_TRUE;
61145 } else {
61146 break;
61147 }
61148 } else {
61149 byteRangeLo = lastSuccessfulSeekOffset;
61150 if (byteRangeHi < byteRangeLo) {
61151 byteRangeHi = byteRangeLo;
61152 }
61153 targetByte = lastSuccessfulSeekOffset + (drflac_uint64)(((drflac_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio);
61154 if (targetByte > byteRangeHi) {
61155 targetByte = byteRangeHi;
61156 }
61157 if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) {
61158 closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset;
61159 }
61160 }
61161 }
61162 }
61163 } else {
61164 break;
61165 }
61166 }
61167 drflac__seek_to_first_frame(pFlac);
61168 return DRFLAC_FALSE;
61169}
61170static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drflac_uint64 pcmFrameIndex)
61171{
61172 drflac_uint64 byteRangeLo;
61173 drflac_uint64 byteRangeHi;
61174 drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;
61175 if (drflac__seek_to_first_frame(pFlac) == DRFLAC_FALSE) {
61176 return DRFLAC_FALSE;
61177 }
61178 if (pcmFrameIndex < seekForwardThreshold) {
61179 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex;
61180 }
61181 byteRangeLo = pFlac->firstFLACFramePosInBytes;
61182 byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f);
61183 return drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi);
61184}
61185#endif
61186static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac_uint64 pcmFrameIndex)
61187{
61188 drflac_uint32 iClosestSeekpoint = 0;
61189 drflac_bool32 isMidFrame = DRFLAC_FALSE;
61190 drflac_uint64 runningPCMFrameCount;
61191 drflac_uint32 iSeekpoint;
61192 DRFLAC_ASSERT(pFlac != NULL);
61193 if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) {
61194 return DRFLAC_FALSE;
61195 }
61196 for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
61197 if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) {
61198 break;
61199 }
61200 iClosestSeekpoint = iSeekpoint;
61201 }
61202 if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) {
61203 return DRFLAC_FALSE;
61204 }
61205 if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) {
61206 return DRFLAC_FALSE;
61207 }
61208#if !defined(DR_FLAC_NO_CRC)
61209 if (pFlac->totalPCMFrameCount > 0) {
61210 drflac_uint64 byteRangeLo;
61211 drflac_uint64 byteRangeHi;
61212 byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f);
61213 byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset;
61214 if (iClosestSeekpoint < pFlac->seekpointCount-1) {
61215 drflac_uint32 iNextSeekpoint = iClosestSeekpoint + 1;
61216 if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) {
61217 return DRFLAC_FALSE;
61218 }
61219 if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((drflac_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) {
61220 byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1;
61221 }
61222 }
61223 if (drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {
61224 if (drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
61225 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);
61226 if (drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) {
61227 return DRFLAC_TRUE;
61228 }
61229 }
61230 }
61231 }
61232#endif
61233 if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) {
61234 runningPCMFrameCount = pFlac->currentPCMFrame;
61235 if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
61236 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
61237 return DRFLAC_FALSE;
61238 }
61239 } else {
61240 isMidFrame = DRFLAC_TRUE;
61241 }
61242 } else {
61243 runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame;
61244 if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {
61245 return DRFLAC_FALSE;
61246 }
61247 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
61248 return DRFLAC_FALSE;
61249 }
61250 }
61251 for (;;) {
61252 drflac_uint64 pcmFrameCountInThisFLACFrame;
61253 drflac_uint64 firstPCMFrameInFLACFrame = 0;
61254 drflac_uint64 lastPCMFrameInFLACFrame = 0;
61255 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
61256 pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
61257 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {
61258 drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;
61259 if (!isMidFrame) {
61260 drflac_result result = drflac__decode_flac_frame(pFlac);
61261 if (result == DRFLAC_SUCCESS) {
61262 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
61263 } else {
61264 if (result == DRFLAC_CRC_MISMATCH) {
61265 goto next_iteration;
61266 } else {
61267 return DRFLAC_FALSE;
61268 }
61269 }
61270 } else {
61271 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
61272 }
61273 } else {
61274 if (!isMidFrame) {
61275 drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
61276 if (result == DRFLAC_SUCCESS) {
61277 runningPCMFrameCount += pcmFrameCountInThisFLACFrame;
61278 } else {
61279 if (result == DRFLAC_CRC_MISMATCH) {
61280 goto next_iteration;
61281 } else {
61282 return DRFLAC_FALSE;
61283 }
61284 }
61285 } else {
61286 runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;
61287 pFlac->currentFLACFrame.pcmFramesRemaining = 0;
61288 isMidFrame = DRFLAC_FALSE;
61289 }
61290 if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {
61291 return DRFLAC_TRUE;
61292 }
61293 }
61294 next_iteration:
61295 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
61296 return DRFLAC_FALSE;
61297 }
61298 }
61299}
61300#ifndef DR_FLAC_NO_OGG
61301typedef struct
61302{
61303 drflac_uint8 capturePattern[4];
61304 drflac_uint8 structureVersion;
61305 drflac_uint8 headerType;
61306 drflac_uint64 granulePosition;
61307 drflac_uint32 serialNumber;
61308 drflac_uint32 sequenceNumber;
61309 drflac_uint32 checksum;
61310 drflac_uint8 segmentCount;
61311 drflac_uint8 segmentTable[255];
61312} drflac_ogg_page_header;
61313#endif
61314typedef struct
61315{
61316 drflac_read_proc onRead;
61317 drflac_seek_proc onSeek;
61318 drflac_meta_proc onMeta;
61319 drflac_container container;
61320 void* pUserData;
61321 void* pUserDataMD;
61322 drflac_uint32 sampleRate;
61323 drflac_uint8 channels;
61324 drflac_uint8 bitsPerSample;
61325 drflac_uint64 totalPCMFrameCount;
61326 drflac_uint16 maxBlockSizeInPCMFrames;
61327 drflac_uint64 runningFilePos;
61328 drflac_bool32 hasStreamInfoBlock;
61329 drflac_bool32 hasMetadataBlocks;
61330 drflac_bs bs;
61331 drflac_frame_header firstFrameHeader;
61332#ifndef DR_FLAC_NO_OGG
61333 drflac_uint32 oggSerial;
61334 drflac_uint64 oggFirstBytePos;
61335 drflac_ogg_page_header oggBosHeader;
61336#endif
61337} drflac_init_info;
61338static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize)
61339{
61340 blockHeader = drflac__be2host_32(blockHeader);
61341 *isLastBlock = (drflac_uint8)((blockHeader & 0x80000000UL) >> 31);
61342 *blockType = (drflac_uint8)((blockHeader & 0x7F000000UL) >> 24);
61343 *blockSize = (blockHeader & 0x00FFFFFFUL);
61344}
61345static 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)
61346{
61347 drflac_uint32 blockHeader;
61348 *blockSize = 0;
61349 if (onRead(pUserData, &blockHeader, 4) != 4) {
61350 return DRFLAC_FALSE;
61351 }
61352 drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize);
61353 return DRFLAC_TRUE;
61354}
61355static drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo)
61356{
61357 drflac_uint32 blockSizes;
61358 drflac_uint64 frameSizes = 0;
61359 drflac_uint64 importantProps;
61360 drflac_uint8 md5[16];
61361 if (onRead(pUserData, &blockSizes, 4) != 4) {
61362 return DRFLAC_FALSE;
61363 }
61364 if (onRead(pUserData, &frameSizes, 6) != 6) {
61365 return DRFLAC_FALSE;
61366 }
61367 if (onRead(pUserData, &importantProps, 8) != 8) {
61368 return DRFLAC_FALSE;
61369 }
61370 if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) {
61371 return DRFLAC_FALSE;
61372 }
61373 blockSizes = drflac__be2host_32(blockSizes);
61374 frameSizes = drflac__be2host_64(frameSizes);
61375 importantProps = drflac__be2host_64(importantProps);
61376 pStreamInfo->minBlockSizeInPCMFrames = (drflac_uint16)((blockSizes & 0xFFFF0000) >> 16);
61377 pStreamInfo->maxBlockSizeInPCMFrames = (drflac_uint16) (blockSizes & 0x0000FFFF);
61378 pStreamInfo->minFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 24)) >> 40);
61379 pStreamInfo->maxFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 0)) >> 16);
61380 pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (((drflac_uint64)0x000FFFFF << 16) << 28)) >> 44);
61381 pStreamInfo->channels = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000000E << 16) << 24)) >> 41) + 1;
61382 pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000001F << 16) << 20)) >> 36) + 1;
61383 pStreamInfo->totalPCMFrameCount = ((importantProps & ((((drflac_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF)));
61384 DRFLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5));
61385 return DRFLAC_TRUE;
61386}
61387static void* drflac__malloc_default(size_t sz, void* pUserData)
61388{
61389 (void)pUserData;
61390 return DRFLAC_MALLOC(sz);
61391}
61392static void* drflac__realloc_default(void* p, size_t sz, void* pUserData)
61393{
61394 (void)pUserData;
61395 return DRFLAC_REALLOC(p, sz);
61396}
61397static void drflac__free_default(void* p, void* pUserData)
61398{
61399 (void)pUserData;
61400 DRFLAC_FREE(p);
61401}
61402static void* drflac__malloc_from_callbacks(size_t sz, const drflac_allocation_callbacks* pAllocationCallbacks)
61403{
61404 if (pAllocationCallbacks == NULL) {
61405 return NULL;
61406 }
61407 if (pAllocationCallbacks->onMalloc != NULL) {
61408 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
61409 }
61410 if (pAllocationCallbacks->onRealloc != NULL) {
61411 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
61412 }
61413 return NULL;
61414}
61415static void* drflac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drflac_allocation_callbacks* pAllocationCallbacks)
61416{
61417 if (pAllocationCallbacks == NULL) {
61418 return NULL;
61419 }
61420 if (pAllocationCallbacks->onRealloc != NULL) {
61421 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
61422 }
61423 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
61424 void* p2;
61425 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
61426 if (p2 == NULL) {
61427 return NULL;
61428 }
61429 if (p != NULL) {
61430 DRFLAC_COPY_MEMORY(p2, p, szOld);
61431 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
61432 }
61433 return p2;
61434 }
61435 return NULL;
61436}
61437static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbacks* pAllocationCallbacks)
61438{
61439 if (p == NULL || pAllocationCallbacks == NULL) {
61440 return;
61441 }
61442 if (pAllocationCallbacks->onFree != NULL) {
61443 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
61444 }
61445}
61446static 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)
61447{
61448 drflac_uint64 runningFilePos = 42;
61449 drflac_uint64 seektablePos = 0;
61450 drflac_uint32 seektableSize = 0;
61451 for (;;) {
61452 drflac_metadata metadata;
61453 drflac_uint8 isLastBlock = 0;
61454 drflac_uint8 blockType;
61455 drflac_uint32 blockSize;
61456 if (drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == DRFLAC_FALSE) {
61457 return DRFLAC_FALSE;
61458 }
61459 runningFilePos += 4;
61460 metadata.type = blockType;
61461 metadata.pRawData = NULL;
61462 metadata.rawDataSize = 0;
61463 switch (blockType)
61464 {
61465 case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION:
61466 {
61467 if (blockSize < 4) {
61468 return DRFLAC_FALSE;
61469 }
61470 if (onMeta) {
61471 void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
61472 if (pRawData == NULL) {
61473 return DRFLAC_FALSE;
61474 }
61475 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
61476 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61477 return DRFLAC_FALSE;
61478 }
61479 metadata.pRawData = pRawData;
61480 metadata.rawDataSize = blockSize;
61481 metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData);
61482 metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32));
61483 metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32);
61484 onMeta(pUserDataMD, &metadata);
61485 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61486 }
61487 } break;
61488 case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE:
61489 {
61490 seektablePos = runningFilePos;
61491 seektableSize = blockSize;
61492 if (onMeta) {
61493 drflac_uint32 iSeekpoint;
61494 void* pRawData;
61495 pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
61496 if (pRawData == NULL) {
61497 return DRFLAC_FALSE;
61498 }
61499 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
61500 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61501 return DRFLAC_FALSE;
61502 }
61503 metadata.pRawData = pRawData;
61504 metadata.rawDataSize = blockSize;
61505 metadata.data.seektable.seekpointCount = blockSize/sizeof(drflac_seekpoint);
61506 metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData;
61507 for (iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) {
61508 drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint;
61509 pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame);
61510 pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset);
61511 pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount);
61512 }
61513 onMeta(pUserDataMD, &metadata);
61514 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61515 }
61516 } break;
61517 case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT:
61518 {
61519 if (blockSize < 8) {
61520 return DRFLAC_FALSE;
61521 }
61522 if (onMeta) {
61523 void* pRawData;
61524 const char* pRunningData;
61525 const char* pRunningDataEnd;
61526 drflac_uint32 i;
61527 pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
61528 if (pRawData == NULL) {
61529 return DRFLAC_FALSE;
61530 }
61531 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
61532 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61533 return DRFLAC_FALSE;
61534 }
61535 metadata.pRawData = pRawData;
61536 metadata.rawDataSize = blockSize;
61537 pRunningData = (const char*)pRawData;
61538 pRunningDataEnd = (const char*)pRawData + blockSize;
61539 metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
61540 if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) {
61541 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61542 return DRFLAC_FALSE;
61543 }
61544 metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength;
61545 metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
61546 if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) {
61547 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61548 return DRFLAC_FALSE;
61549 }
61550 metadata.data.vorbis_comment.pComments = pRunningData;
61551 for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) {
61552 drflac_uint32 commentLength;
61553 if (pRunningDataEnd - pRunningData < 4) {
61554 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61555 return DRFLAC_FALSE;
61556 }
61557 commentLength = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
61558 if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) {
61559 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61560 return DRFLAC_FALSE;
61561 }
61562 pRunningData += commentLength;
61563 }
61564 onMeta(pUserDataMD, &metadata);
61565 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61566 }
61567 } break;
61568 case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET:
61569 {
61570 if (blockSize < 396) {
61571 return DRFLAC_FALSE;
61572 }
61573 if (onMeta) {
61574 void* pRawData;
61575 const char* pRunningData;
61576 const char* pRunningDataEnd;
61577 drflac_uint8 iTrack;
61578 drflac_uint8 iIndex;
61579 pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
61580 if (pRawData == NULL) {
61581 return DRFLAC_FALSE;
61582 }
61583 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
61584 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61585 return DRFLAC_FALSE;
61586 }
61587 metadata.pRawData = pRawData;
61588 metadata.rawDataSize = blockSize;
61589 pRunningData = (const char*)pRawData;
61590 pRunningDataEnd = (const char*)pRawData + blockSize;
61591 DRFLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128;
61592 metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8;
61593 metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259;
61594 metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1;
61595 metadata.data.cuesheet.pTrackData = pRunningData;
61596 for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) {
61597 drflac_uint8 indexCount;
61598 drflac_uint32 indexPointSize;
61599 if (pRunningDataEnd - pRunningData < 36) {
61600 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61601 return DRFLAC_FALSE;
61602 }
61603 pRunningData += 35;
61604 indexCount = pRunningData[0]; pRunningData += 1;
61605 indexPointSize = indexCount * sizeof(drflac_cuesheet_track_index);
61606 if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) {
61607 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61608 return DRFLAC_FALSE;
61609 }
61610 for (iIndex = 0; iIndex < indexCount; ++iIndex) {
61611 drflac_cuesheet_track_index* pTrack = (drflac_cuesheet_track_index*)pRunningData;
61612 pRunningData += sizeof(drflac_cuesheet_track_index);
61613 pTrack->offset = drflac__be2host_64(pTrack->offset);
61614 }
61615 }
61616 onMeta(pUserDataMD, &metadata);
61617 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61618 }
61619 } break;
61620 case DRFLAC_METADATA_BLOCK_TYPE_PICTURE:
61621 {
61622 if (blockSize < 32) {
61623 return DRFLAC_FALSE;
61624 }
61625 if (onMeta) {
61626 void* pRawData;
61627 const char* pRunningData;
61628 const char* pRunningDataEnd;
61629 pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
61630 if (pRawData == NULL) {
61631 return DRFLAC_FALSE;
61632 }
61633 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
61634 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61635 return DRFLAC_FALSE;
61636 }
61637 metadata.pRawData = pRawData;
61638 metadata.rawDataSize = blockSize;
61639 pRunningData = (const char*)pRawData;
61640 pRunningDataEnd = (const char*)pRawData + blockSize;
61641 metadata.data.picture.type = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
61642 metadata.data.picture.mimeLength = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
61643 if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) {
61644 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61645 return DRFLAC_FALSE;
61646 }
61647 metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength;
61648 metadata.data.picture.descriptionLength = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
61649 if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) {
61650 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61651 return DRFLAC_FALSE;
61652 }
61653 metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength;
61654 metadata.data.picture.width = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
61655 metadata.data.picture.height = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
61656 metadata.data.picture.colorDepth = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
61657 metadata.data.picture.indexColorCount = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
61658 metadata.data.picture.pictureDataSize = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
61659 metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData;
61660 if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) {
61661 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61662 return DRFLAC_FALSE;
61663 }
61664 onMeta(pUserDataMD, &metadata);
61665 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61666 }
61667 } break;
61668 case DRFLAC_METADATA_BLOCK_TYPE_PADDING:
61669 {
61670 if (onMeta) {
61671 metadata.data.padding.unused = 0;
61672 if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
61673 isLastBlock = DRFLAC_TRUE;
61674 } else {
61675 onMeta(pUserDataMD, &metadata);
61676 }
61677 }
61678 } break;
61679 case DRFLAC_METADATA_BLOCK_TYPE_INVALID:
61680 {
61681 if (onMeta) {
61682 if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
61683 isLastBlock = DRFLAC_TRUE;
61684 }
61685 }
61686 } break;
61687 default:
61688 {
61689 if (onMeta) {
61690 void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
61691 if (pRawData == NULL) {
61692 return DRFLAC_FALSE;
61693 }
61694 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
61695 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61696 return DRFLAC_FALSE;
61697 }
61698 metadata.pRawData = pRawData;
61699 metadata.rawDataSize = blockSize;
61700 onMeta(pUserDataMD, &metadata);
61701 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
61702 }
61703 } break;
61704 }
61705 if (onMeta == NULL && blockSize > 0) {
61706 if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
61707 isLastBlock = DRFLAC_TRUE;
61708 }
61709 }
61710 runningFilePos += blockSize;
61711 if (isLastBlock) {
61712 break;
61713 }
61714 }
61715 *pSeektablePos = seektablePos;
61716 *pSeektableSize = seektableSize;
61717 *pFirstFramePos = runningFilePos;
61718 return DRFLAC_TRUE;
61719}
61720static 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)
61721{
61722 drflac_uint8 isLastBlock;
61723 drflac_uint8 blockType;
61724 drflac_uint32 blockSize;
61725 (void)onSeek;
61726 pInit->container = drflac_container_native;
61727 if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
61728 return DRFLAC_FALSE;
61729 }
61730 if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
61731 if (!relaxed) {
61732 return DRFLAC_FALSE;
61733 } else {
61734 pInit->hasStreamInfoBlock = DRFLAC_FALSE;
61735 pInit->hasMetadataBlocks = DRFLAC_FALSE;
61736 if (!drflac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) {
61737 return DRFLAC_FALSE;
61738 }
61739 if (pInit->firstFrameHeader.bitsPerSample == 0) {
61740 return DRFLAC_FALSE;
61741 }
61742 pInit->sampleRate = pInit->firstFrameHeader.sampleRate;
61743 pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment);
61744 pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample;
61745 pInit->maxBlockSizeInPCMFrames = 65535;
61746 return DRFLAC_TRUE;
61747 }
61748 } else {
61749 drflac_streaminfo streaminfo;
61750 if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) {
61751 return DRFLAC_FALSE;
61752 }
61753 pInit->hasStreamInfoBlock = DRFLAC_TRUE;
61754 pInit->sampleRate = streaminfo.sampleRate;
61755 pInit->channels = streaminfo.channels;
61756 pInit->bitsPerSample = streaminfo.bitsPerSample;
61757 pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount;
61758 pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;
61759 pInit->hasMetadataBlocks = !isLastBlock;
61760 if (onMeta) {
61761 drflac_metadata metadata;
61762 metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO;
61763 metadata.pRawData = NULL;
61764 metadata.rawDataSize = 0;
61765 metadata.data.streaminfo = streaminfo;
61766 onMeta(pUserDataMD, &metadata);
61767 }
61768 return DRFLAC_TRUE;
61769 }
61770}
61771#ifndef DR_FLAC_NO_OGG
61772#define DRFLAC_OGG_MAX_PAGE_SIZE 65307
61773#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199
61774typedef enum
61775{
61776 drflac_ogg_recover_on_crc_mismatch,
61777 drflac_ogg_fail_on_crc_mismatch
61778} drflac_ogg_crc_mismatch_recovery;
61779#ifndef DR_FLAC_NO_CRC
61780static drflac_uint32 drflac__crc32_table[] = {
61781 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L,
61782 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L,
61783 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L,
61784 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL,
61785 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L,
61786 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L,
61787 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L,
61788 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL,
61789 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L,
61790 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L,
61791 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L,
61792 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL,
61793 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L,
61794 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L,
61795 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L,
61796 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL,
61797 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL,
61798 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L,
61799 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L,
61800 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL,
61801 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL,
61802 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L,
61803 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L,
61804 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL,
61805 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL,
61806 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L,
61807 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L,
61808 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL,
61809 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL,
61810 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L,
61811 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L,
61812 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL,
61813 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L,
61814 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL,
61815 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL,
61816 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L,
61817 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L,
61818 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL,
61819 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL,
61820 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L,
61821 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L,
61822 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL,
61823 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL,
61824 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L,
61825 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L,
61826 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL,
61827 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL,
61828 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L,
61829 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L,
61830 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL,
61831 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L,
61832 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L,
61833 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L,
61834 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL,
61835 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L,
61836 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L,
61837 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L,
61838 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL,
61839 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L,
61840 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L,
61841 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L,
61842 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL,
61843 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L,
61844 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L
61845};
61846#endif
61847static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data)
61848{
61849#ifndef DR_FLAC_NO_CRC
61850 return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data];
61851#else
61852 (void)data;
61853 return crc32;
61854#endif
61855}
61856#if 0
61857static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data)
61858{
61859 crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF));
61860 crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF));
61861 crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF));
61862 crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF));
61863 return crc32;
61864}
61865static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data)
61866{
61867 crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF));
61868 crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF));
61869 return crc32;
61870}
61871#endif
61872static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize)
61873{
61874 drflac_uint32 i;
61875 for (i = 0; i < dataSize; ++i) {
61876 crc32 = drflac_crc32_byte(crc32, pData[i]);
61877 }
61878 return crc32;
61879}
61880static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4])
61881{
61882 return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S';
61883}
61884static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader)
61885{
61886 return 27 + pHeader->segmentCount;
61887}
61888static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader)
61889{
61890 drflac_uint32 pageBodySize = 0;
61891 int i;
61892 for (i = 0; i < pHeader->segmentCount; ++i) {
61893 pageBodySize += pHeader->segmentTable[i];
61894 }
61895 return pageBodySize;
61896}
61897static 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)
61898{
61899 drflac_uint8 data[23];
61900 drflac_uint32 i;
61901 DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32);
61902 if (onRead(pUserData, data, 23) != 23) {
61903 return DRFLAC_AT_END;
61904 }
61905 *pBytesRead += 23;
61906 pHeader->capturePattern[0] = 'O';
61907 pHeader->capturePattern[1] = 'g';
61908 pHeader->capturePattern[2] = 'g';
61909 pHeader->capturePattern[3] = 'S';
61910 pHeader->structureVersion = data[0];
61911 pHeader->headerType = data[1];
61912 DRFLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8);
61913 DRFLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4);
61914 DRFLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4);
61915 DRFLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4);
61916 pHeader->segmentCount = data[22];
61917 data[18] = 0;
61918 data[19] = 0;
61919 data[20] = 0;
61920 data[21] = 0;
61921 for (i = 0; i < 23; ++i) {
61922 *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]);
61923 }
61924 if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) {
61925 return DRFLAC_AT_END;
61926 }
61927 *pBytesRead += pHeader->segmentCount;
61928 for (i = 0; i < pHeader->segmentCount; ++i) {
61929 *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]);
61930 }
61931 return DRFLAC_SUCCESS;
61932}
61933static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32)
61934{
61935 drflac_uint8 id[4];
61936 *pBytesRead = 0;
61937 if (onRead(pUserData, id, 4) != 4) {
61938 return DRFLAC_AT_END;
61939 }
61940 *pBytesRead += 4;
61941 for (;;) {
61942 if (drflac_ogg__is_capture_pattern(id)) {
61943 drflac_result result;
61944 *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
61945 result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32);
61946 if (result == DRFLAC_SUCCESS) {
61947 return DRFLAC_SUCCESS;
61948 } else {
61949 if (result == DRFLAC_CRC_MISMATCH) {
61950 continue;
61951 } else {
61952 return result;
61953 }
61954 }
61955 } else {
61956 id[0] = id[1];
61957 id[1] = id[2];
61958 id[2] = id[3];
61959 if (onRead(pUserData, &id[3], 1) != 1) {
61960 return DRFLAC_AT_END;
61961 }
61962 *pBytesRead += 1;
61963 }
61964 }
61965}
61966typedef struct
61967{
61968 drflac_read_proc onRead;
61969 drflac_seek_proc onSeek;
61970 void* pUserData;
61971 drflac_uint64 currentBytePos;
61972 drflac_uint64 firstBytePos;
61973 drflac_uint32 serialNumber;
61974 drflac_ogg_page_header bosPageHeader;
61975 drflac_ogg_page_header currentPageHeader;
61976 drflac_uint32 bytesRemainingInPage;
61977 drflac_uint32 pageDataSize;
61978 drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE];
61979} drflac_oggbs;
61980static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead)
61981{
61982 size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead);
61983 oggbs->currentBytePos += bytesActuallyRead;
61984 return bytesActuallyRead;
61985}
61986static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin)
61987{
61988 if (origin == drflac_seek_origin_start) {
61989 if (offset <= 0x7FFFFFFF) {
61990 if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) {
61991 return DRFLAC_FALSE;
61992 }
61993 oggbs->currentBytePos = offset;
61994 return DRFLAC_TRUE;
61995 } else {
61996 if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
61997 return DRFLAC_FALSE;
61998 }
61999 oggbs->currentBytePos = offset;
62000 return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current);
62001 }
62002 } else {
62003 while (offset > 0x7FFFFFFF) {
62004 if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
62005 return DRFLAC_FALSE;
62006 }
62007 oggbs->currentBytePos += 0x7FFFFFFF;
62008 offset -= 0x7FFFFFFF;
62009 }
62010 if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) {
62011 return DRFLAC_FALSE;
62012 }
62013 oggbs->currentBytePos += offset;
62014 return DRFLAC_TRUE;
62015 }
62016}
62017static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod)
62018{
62019 drflac_ogg_page_header header;
62020 for (;;) {
62021 drflac_uint32 crc32 = 0;
62022 drflac_uint32 bytesRead;
62023 drflac_uint32 pageBodySize;
62024#ifndef DR_FLAC_NO_CRC
62025 drflac_uint32 actualCRC32;
62026#endif
62027 if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
62028 return DRFLAC_FALSE;
62029 }
62030 oggbs->currentBytePos += bytesRead;
62031 pageBodySize = drflac_ogg__get_page_body_size(&header);
62032 if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) {
62033 continue;
62034 }
62035 if (header.serialNumber != oggbs->serialNumber) {
62036 if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) {
62037 return DRFLAC_FALSE;
62038 }
62039 continue;
62040 }
62041 if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) {
62042 return DRFLAC_FALSE;
62043 }
62044 oggbs->pageDataSize = pageBodySize;
62045#ifndef DR_FLAC_NO_CRC
62046 actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize);
62047 if (actualCRC32 != header.checksum) {
62048 if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) {
62049 continue;
62050 } else {
62051 drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch);
62052 return DRFLAC_FALSE;
62053 }
62054 }
62055#else
62056 (void)recoveryMethod;
62057#endif
62058 oggbs->currentPageHeader = header;
62059 oggbs->bytesRemainingInPage = pageBodySize;
62060 return DRFLAC_TRUE;
62061 }
62062}
62063#if 0
62064static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg)
62065{
62066 drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage;
62067 drflac_uint8 iSeg = 0;
62068 drflac_uint32 iByte = 0;
62069 while (iByte < bytesConsumedInPage) {
62070 drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
62071 if (iByte + segmentSize > bytesConsumedInPage) {
62072 break;
62073 } else {
62074 iSeg += 1;
62075 iByte += segmentSize;
62076 }
62077 }
62078 *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte);
62079 return iSeg;
62080}
62081static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs)
62082{
62083 for (;;) {
62084 drflac_bool32 atEndOfPage = DRFLAC_FALSE;
62085 drflac_uint8 bytesRemainingInSeg;
62086 drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg);
62087 drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg;
62088 for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) {
62089 drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
62090 if (segmentSize < 255) {
62091 if (iSeg == oggbs->currentPageHeader.segmentCount-1) {
62092 atEndOfPage = DRFLAC_TRUE;
62093 }
62094 break;
62095 }
62096 bytesToEndOfPacketOrPage += segmentSize;
62097 }
62098 drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current);
62099 oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage;
62100 if (atEndOfPage) {
62101 if (!drflac_oggbs__goto_next_page(oggbs)) {
62102 return DRFLAC_FALSE;
62103 }
62104 if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
62105 return DRFLAC_TRUE;
62106 }
62107 } else {
62108 return DRFLAC_TRUE;
62109 }
62110 }
62111}
62112static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs)
62113{
62114 return drflac_oggbs__seek_to_next_packet(oggbs);
62115}
62116#endif
62117static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead)
62118{
62119 drflac_oggbs* oggbs = (drflac_oggbs*)pUserData;
62120 drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut;
62121 size_t bytesRead = 0;
62122 DRFLAC_ASSERT(oggbs != NULL);
62123 DRFLAC_ASSERT(pRunningBufferOut != NULL);
62124 while (bytesRead < bytesToRead) {
62125 size_t bytesRemainingToRead = bytesToRead - bytesRead;
62126 if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) {
62127 DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead);
62128 bytesRead += bytesRemainingToRead;
62129 oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead;
62130 break;
62131 }
62132 if (oggbs->bytesRemainingInPage > 0) {
62133 DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage);
62134 bytesRead += oggbs->bytesRemainingInPage;
62135 pRunningBufferOut += oggbs->bytesRemainingInPage;
62136 oggbs->bytesRemainingInPage = 0;
62137 }
62138 DRFLAC_ASSERT(bytesRemainingToRead > 0);
62139 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
62140 break;
62141 }
62142 }
62143 return bytesRead;
62144}
62145static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin)
62146{
62147 drflac_oggbs* oggbs = (drflac_oggbs*)pUserData;
62148 int bytesSeeked = 0;
62149 DRFLAC_ASSERT(oggbs != NULL);
62150 DRFLAC_ASSERT(offset >= 0);
62151 if (origin == drflac_seek_origin_start) {
62152 if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) {
62153 return DRFLAC_FALSE;
62154 }
62155 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
62156 return DRFLAC_FALSE;
62157 }
62158 return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current);
62159 }
62160 DRFLAC_ASSERT(origin == drflac_seek_origin_current);
62161 while (bytesSeeked < offset) {
62162 int bytesRemainingToSeek = offset - bytesSeeked;
62163 DRFLAC_ASSERT(bytesRemainingToSeek >= 0);
62164 if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) {
62165 bytesSeeked += bytesRemainingToSeek;
62166 (void)bytesSeeked;
62167 oggbs->bytesRemainingInPage -= bytesRemainingToSeek;
62168 break;
62169 }
62170 if (oggbs->bytesRemainingInPage > 0) {
62171 bytesSeeked += (int)oggbs->bytesRemainingInPage;
62172 oggbs->bytesRemainingInPage = 0;
62173 }
62174 DRFLAC_ASSERT(bytesRemainingToSeek > 0);
62175 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
62176 return DRFLAC_FALSE;
62177 }
62178 }
62179 return DRFLAC_TRUE;
62180}
62181static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex)
62182{
62183 drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
62184 drflac_uint64 originalBytePos;
62185 drflac_uint64 runningGranulePosition;
62186 drflac_uint64 runningFrameBytePos;
62187 drflac_uint64 runningPCMFrameCount;
62188 DRFLAC_ASSERT(oggbs != NULL);
62189 originalBytePos = oggbs->currentBytePos;
62190 if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) {
62191 return DRFLAC_FALSE;
62192 }
62193 oggbs->bytesRemainingInPage = 0;
62194 runningGranulePosition = 0;
62195 for (;;) {
62196 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
62197 drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start);
62198 return DRFLAC_FALSE;
62199 }
62200 runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize;
62201 if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) {
62202 break;
62203 }
62204 if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
62205 if (oggbs->currentPageHeader.segmentTable[0] >= 2) {
62206 drflac_uint8 firstBytesInPage[2];
62207 firstBytesInPage[0] = oggbs->pageData[0];
62208 firstBytesInPage[1] = oggbs->pageData[1];
62209 if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) {
62210 runningGranulePosition = oggbs->currentPageHeader.granulePosition;
62211 }
62212 continue;
62213 }
62214 }
62215 }
62216 if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) {
62217 return DRFLAC_FALSE;
62218 }
62219 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
62220 return DRFLAC_FALSE;
62221 }
62222 runningPCMFrameCount = runningGranulePosition;
62223 for (;;) {
62224 drflac_uint64 firstPCMFrameInFLACFrame = 0;
62225 drflac_uint64 lastPCMFrameInFLACFrame = 0;
62226 drflac_uint64 pcmFrameCountInThisFrame;
62227 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
62228 return DRFLAC_FALSE;
62229 }
62230 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
62231 pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
62232 if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) {
62233 drflac_result result = drflac__decode_flac_frame(pFlac);
62234 if (result == DRFLAC_SUCCESS) {
62235 pFlac->currentPCMFrame = pcmFrameIndex;
62236 pFlac->currentFLACFrame.pcmFramesRemaining = 0;
62237 return DRFLAC_TRUE;
62238 } else {
62239 return DRFLAC_FALSE;
62240 }
62241 }
62242 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) {
62243 drflac_result result = drflac__decode_flac_frame(pFlac);
62244 if (result == DRFLAC_SUCCESS) {
62245 drflac_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount);
62246 if (pcmFramesToDecode == 0) {
62247 return DRFLAC_TRUE;
62248 }
62249 pFlac->currentPCMFrame = runningPCMFrameCount;
62250 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
62251 } else {
62252 if (result == DRFLAC_CRC_MISMATCH) {
62253 continue;
62254 } else {
62255 return DRFLAC_FALSE;
62256 }
62257 }
62258 } else {
62259 drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
62260 if (result == DRFLAC_SUCCESS) {
62261 runningPCMFrameCount += pcmFrameCountInThisFrame;
62262 } else {
62263 if (result == DRFLAC_CRC_MISMATCH) {
62264 continue;
62265 } else {
62266 return DRFLAC_FALSE;
62267 }
62268 }
62269 }
62270 }
62271}
62272static 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)
62273{
62274 drflac_ogg_page_header header;
62275 drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
62276 drflac_uint32 bytesRead = 0;
62277 (void)relaxed;
62278 pInit->container = drflac_container_ogg;
62279 pInit->oggFirstBytePos = 0;
62280 if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
62281 return DRFLAC_FALSE;
62282 }
62283 pInit->runningFilePos += bytesRead;
62284 for (;;) {
62285 int pageBodySize;
62286 if ((header.headerType & 0x02) == 0) {
62287 return DRFLAC_FALSE;
62288 }
62289 pageBodySize = drflac_ogg__get_page_body_size(&header);
62290 if (pageBodySize == 51) {
62291 drflac_uint32 bytesRemainingInPage = pageBodySize;
62292 drflac_uint8 packetType;
62293 if (onRead(pUserData, &packetType, 1) != 1) {
62294 return DRFLAC_FALSE;
62295 }
62296 bytesRemainingInPage -= 1;
62297 if (packetType == 0x7F) {
62298 drflac_uint8 sig[4];
62299 if (onRead(pUserData, sig, 4) != 4) {
62300 return DRFLAC_FALSE;
62301 }
62302 bytesRemainingInPage -= 4;
62303 if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') {
62304 drflac_uint8 mappingVersion[2];
62305 if (onRead(pUserData, mappingVersion, 2) != 2) {
62306 return DRFLAC_FALSE;
62307 }
62308 if (mappingVersion[0] != 1) {
62309 return DRFLAC_FALSE;
62310 }
62311 if (!onSeek(pUserData, 2, drflac_seek_origin_current)) {
62312 return DRFLAC_FALSE;
62313 }
62314 if (onRead(pUserData, sig, 4) != 4) {
62315 return DRFLAC_FALSE;
62316 }
62317 if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') {
62318 drflac_streaminfo streaminfo;
62319 drflac_uint8 isLastBlock;
62320 drflac_uint8 blockType;
62321 drflac_uint32 blockSize;
62322 if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
62323 return DRFLAC_FALSE;
62324 }
62325 if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
62326 return DRFLAC_FALSE;
62327 }
62328 if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) {
62329 pInit->hasStreamInfoBlock = DRFLAC_TRUE;
62330 pInit->sampleRate = streaminfo.sampleRate;
62331 pInit->channels = streaminfo.channels;
62332 pInit->bitsPerSample = streaminfo.bitsPerSample;
62333 pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount;
62334 pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;
62335 pInit->hasMetadataBlocks = !isLastBlock;
62336 if (onMeta) {
62337 drflac_metadata metadata;
62338 metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO;
62339 metadata.pRawData = NULL;
62340 metadata.rawDataSize = 0;
62341 metadata.data.streaminfo = streaminfo;
62342 onMeta(pUserDataMD, &metadata);
62343 }
62344 pInit->runningFilePos += pageBodySize;
62345 pInit->oggFirstBytePos = pInit->runningFilePos - 79;
62346 pInit->oggSerial = header.serialNumber;
62347 pInit->oggBosHeader = header;
62348 break;
62349 } else {
62350 return DRFLAC_FALSE;
62351 }
62352 } else {
62353 return DRFLAC_FALSE;
62354 }
62355 } else {
62356 if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
62357 return DRFLAC_FALSE;
62358 }
62359 }
62360 } else {
62361 if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
62362 return DRFLAC_FALSE;
62363 }
62364 }
62365 } else {
62366 if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) {
62367 return DRFLAC_FALSE;
62368 }
62369 }
62370 pInit->runningFilePos += pageBodySize;
62371 if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
62372 return DRFLAC_FALSE;
62373 }
62374 pInit->runningFilePos += bytesRead;
62375 }
62376 pInit->hasMetadataBlocks = DRFLAC_TRUE;
62377 return DRFLAC_TRUE;
62378}
62379#endif
62380static 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)
62381{
62382 drflac_bool32 relaxed;
62383 drflac_uint8 id[4];
62384 if (pInit == NULL || onRead == NULL || onSeek == NULL) {
62385 return DRFLAC_FALSE;
62386 }
62387 DRFLAC_ZERO_MEMORY(pInit, sizeof(*pInit));
62388 pInit->onRead = onRead;
62389 pInit->onSeek = onSeek;
62390 pInit->onMeta = onMeta;
62391 pInit->container = container;
62392 pInit->pUserData = pUserData;
62393 pInit->pUserDataMD = pUserDataMD;
62394 pInit->bs.onRead = onRead;
62395 pInit->bs.onSeek = onSeek;
62396 pInit->bs.pUserData = pUserData;
62397 drflac__reset_cache(&pInit->bs);
62398 relaxed = container != drflac_container_unknown;
62399 for (;;) {
62400 if (onRead(pUserData, id, 4) != 4) {
62401 return DRFLAC_FALSE;
62402 }
62403 pInit->runningFilePos += 4;
62404 if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') {
62405 drflac_uint8 header[6];
62406 drflac_uint8 flags;
62407 drflac_uint32 headerSize;
62408 if (onRead(pUserData, header, 6) != 6) {
62409 return DRFLAC_FALSE;
62410 }
62411 pInit->runningFilePos += 6;
62412 flags = header[1];
62413 DRFLAC_COPY_MEMORY(&headerSize, header+2, 4);
62414 headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize));
62415 if (flags & 0x10) {
62416 headerSize += 10;
62417 }
62418 if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) {
62419 return DRFLAC_FALSE;
62420 }
62421 pInit->runningFilePos += headerSize;
62422 } else {
62423 break;
62424 }
62425 }
62426 if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') {
62427 return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
62428 }
62429#ifndef DR_FLAC_NO_OGG
62430 if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') {
62431 return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
62432 }
62433#endif
62434 if (relaxed) {
62435 if (container == drflac_container_native) {
62436 return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
62437 }
62438#ifndef DR_FLAC_NO_OGG
62439 if (container == drflac_container_ogg) {
62440 return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
62441 }
62442#endif
62443 }
62444 return DRFLAC_FALSE;
62445}
62446static void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit)
62447{
62448 DRFLAC_ASSERT(pFlac != NULL);
62449 DRFLAC_ASSERT(pInit != NULL);
62450 DRFLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac));
62451 pFlac->bs = pInit->bs;
62452 pFlac->onMeta = pInit->onMeta;
62453 pFlac->pUserDataMD = pInit->pUserDataMD;
62454 pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames;
62455 pFlac->sampleRate = pInit->sampleRate;
62456 pFlac->channels = (drflac_uint8)pInit->channels;
62457 pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample;
62458 pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount;
62459 pFlac->container = pInit->container;
62460}
62461static 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)
62462{
62463 drflac_init_info init;
62464 drflac_uint32 allocationSize;
62465 drflac_uint32 wholeSIMDVectorCountPerChannel;
62466 drflac_uint32 decodedSamplesAllocationSize;
62467#ifndef DR_FLAC_NO_OGG
62468 drflac_oggbs oggbs;
62469#endif
62470 drflac_uint64 firstFramePos;
62471 drflac_uint64 seektablePos;
62472 drflac_uint32 seektableSize;
62473 drflac_allocation_callbacks allocationCallbacks;
62474 drflac* pFlac;
62475 drflac__init_cpu_caps();
62476 if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) {
62477 return NULL;
62478 }
62479 if (pAllocationCallbacks != NULL) {
62480 allocationCallbacks = *pAllocationCallbacks;
62481 if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) {
62482 return NULL;
62483 }
62484 } else {
62485 allocationCallbacks.pUserData = NULL;
62486 allocationCallbacks.onMalloc = drflac__malloc_default;
62487 allocationCallbacks.onRealloc = drflac__realloc_default;
62488 allocationCallbacks.onFree = drflac__free_default;
62489 }
62490 allocationSize = sizeof(drflac);
62491 if ((init.maxBlockSizeInPCMFrames % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) {
62492 wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32)));
62493 } else {
62494 wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1;
62495 }
62496 decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels;
62497 allocationSize += decodedSamplesAllocationSize;
62498 allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE;
62499#ifndef DR_FLAC_NO_OGG
62500 if (init.container == drflac_container_ogg) {
62501 allocationSize += sizeof(drflac_oggbs);
62502 }
62503 DRFLAC_ZERO_MEMORY(&oggbs, sizeof(oggbs));
62504 if (init.container == drflac_container_ogg) {
62505 oggbs.onRead = onRead;
62506 oggbs.onSeek = onSeek;
62507 oggbs.pUserData = pUserData;
62508 oggbs.currentBytePos = init.oggFirstBytePos;
62509 oggbs.firstBytePos = init.oggFirstBytePos;
62510 oggbs.serialNumber = init.oggSerial;
62511 oggbs.bosPageHeader = init.oggBosHeader;
62512 oggbs.bytesRemainingInPage = 0;
62513 }
62514#endif
62515 firstFramePos = 42;
62516 seektablePos = 0;
62517 seektableSize = 0;
62518 if (init.hasMetadataBlocks) {
62519 drflac_read_proc onReadOverride = onRead;
62520 drflac_seek_proc onSeekOverride = onSeek;
62521 void* pUserDataOverride = pUserData;
62522#ifndef DR_FLAC_NO_OGG
62523 if (init.container == drflac_container_ogg) {
62524 onReadOverride = drflac__on_read_ogg;
62525 onSeekOverride = drflac__on_seek_ogg;
62526 pUserDataOverride = (void*)&oggbs;
62527 }
62528#endif
62529 if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seektableSize, &allocationCallbacks)) {
62530 return NULL;
62531 }
62532 allocationSize += seektableSize;
62533 }
62534 pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks);
62535 if (pFlac == NULL) {
62536 return NULL;
62537 }
62538 drflac__init_from_info(pFlac, &init);
62539 pFlac->allocationCallbacks = allocationCallbacks;
62540 pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE);
62541#ifndef DR_FLAC_NO_OGG
62542 if (init.container == drflac_container_ogg) {
62543 drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + seektableSize);
62544 *pInternalOggbs = oggbs;
62545 pFlac->bs.onRead = drflac__on_read_ogg;
62546 pFlac->bs.onSeek = drflac__on_seek_ogg;
62547 pFlac->bs.pUserData = (void*)pInternalOggbs;
62548 pFlac->_oggbs = (void*)pInternalOggbs;
62549 }
62550#endif
62551 pFlac->firstFLACFramePosInBytes = firstFramePos;
62552#ifndef DR_FLAC_NO_OGG
62553 if (init.container == drflac_container_ogg)
62554 {
62555 pFlac->pSeekpoints = NULL;
62556 pFlac->seekpointCount = 0;
62557 }
62558 else
62559#endif
62560 {
62561 if (seektablePos != 0) {
62562 pFlac->seekpointCount = seektableSize / sizeof(*pFlac->pSeekpoints);
62563 pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize);
62564 DRFLAC_ASSERT(pFlac->bs.onSeek != NULL);
62565 DRFLAC_ASSERT(pFlac->bs.onRead != NULL);
62566 if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) {
62567 if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints, seektableSize) == seektableSize) {
62568 drflac_uint32 iSeekpoint;
62569 for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
62570 pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame);
62571 pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset);
62572 pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount);
62573 }
62574 } else {
62575 pFlac->pSeekpoints = NULL;
62576 pFlac->seekpointCount = 0;
62577 }
62578 if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) {
62579 drflac__free_from_callbacks(pFlac, &allocationCallbacks);
62580 return NULL;
62581 }
62582 } else {
62583 pFlac->pSeekpoints = NULL;
62584 pFlac->seekpointCount = 0;
62585 }
62586 }
62587 }
62588 if (!init.hasStreamInfoBlock) {
62589 pFlac->currentFLACFrame.header = init.firstFrameHeader;
62590 for (;;) {
62591 drflac_result result = drflac__decode_flac_frame(pFlac);
62592 if (result == DRFLAC_SUCCESS) {
62593 break;
62594 } else {
62595 if (result == DRFLAC_CRC_MISMATCH) {
62596 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
62597 drflac__free_from_callbacks(pFlac, &allocationCallbacks);
62598 return NULL;
62599 }
62600 continue;
62601 } else {
62602 drflac__free_from_callbacks(pFlac, &allocationCallbacks);
62603 return NULL;
62604 }
62605 }
62606 }
62607 }
62608 return pFlac;
62609}
62610#ifndef DR_FLAC_NO_STDIO
62611#include <stdio.h>
62612#include <wchar.h>
62613#include <errno.h>
62614static drflac_result drflac_result_from_errno(int e)
62615{
62616 switch (e)
62617 {
62618 case 0: return DRFLAC_SUCCESS;
62619 #ifdef EPERM
62620 case EPERM: return DRFLAC_INVALID_OPERATION;
62621 #endif
62622 #ifdef ENOENT
62623 case ENOENT: return DRFLAC_DOES_NOT_EXIST;
62624 #endif
62625 #ifdef ESRCH
62626 case ESRCH: return DRFLAC_DOES_NOT_EXIST;
62627 #endif
62628 #ifdef EINTR
62629 case EINTR: return DRFLAC_INTERRUPT;
62630 #endif
62631 #ifdef EIO
62632 case EIO: return DRFLAC_IO_ERROR;
62633 #endif
62634 #ifdef ENXIO
62635 case ENXIO: return DRFLAC_DOES_NOT_EXIST;
62636 #endif
62637 #ifdef E2BIG
62638 case E2BIG: return DRFLAC_INVALID_ARGS;
62639 #endif
62640 #ifdef ENOEXEC
62641 case ENOEXEC: return DRFLAC_INVALID_FILE;
62642 #endif
62643 #ifdef EBADF
62644 case EBADF: return DRFLAC_INVALID_FILE;
62645 #endif
62646 #ifdef ECHILD
62647 case ECHILD: return DRFLAC_ERROR;
62648 #endif
62649 #ifdef EAGAIN
62650 case EAGAIN: return DRFLAC_UNAVAILABLE;
62651 #endif
62652 #ifdef ENOMEM
62653 case ENOMEM: return DRFLAC_OUT_OF_MEMORY;
62654 #endif
62655 #ifdef EACCES
62656 case EACCES: return DRFLAC_ACCESS_DENIED;
62657 #endif
62658 #ifdef EFAULT
62659 case EFAULT: return DRFLAC_BAD_ADDRESS;
62660 #endif
62661 #ifdef ENOTBLK
62662 case ENOTBLK: return DRFLAC_ERROR;
62663 #endif
62664 #ifdef EBUSY
62665 case EBUSY: return DRFLAC_BUSY;
62666 #endif
62667 #ifdef EEXIST
62668 case EEXIST: return DRFLAC_ALREADY_EXISTS;
62669 #endif
62670 #ifdef EXDEV
62671 case EXDEV: return DRFLAC_ERROR;
62672 #endif
62673 #ifdef ENODEV
62674 case ENODEV: return DRFLAC_DOES_NOT_EXIST;
62675 #endif
62676 #ifdef ENOTDIR
62677 case ENOTDIR: return DRFLAC_NOT_DIRECTORY;
62678 #endif
62679 #ifdef EISDIR
62680 case EISDIR: return DRFLAC_IS_DIRECTORY;
62681 #endif
62682 #ifdef EINVAL
62683 case EINVAL: return DRFLAC_INVALID_ARGS;
62684 #endif
62685 #ifdef ENFILE
62686 case ENFILE: return DRFLAC_TOO_MANY_OPEN_FILES;
62687 #endif
62688 #ifdef EMFILE
62689 case EMFILE: return DRFLAC_TOO_MANY_OPEN_FILES;
62690 #endif
62691 #ifdef ENOTTY
62692 case ENOTTY: return DRFLAC_INVALID_OPERATION;
62693 #endif
62694 #ifdef ETXTBSY
62695 case ETXTBSY: return DRFLAC_BUSY;
62696 #endif
62697 #ifdef EFBIG
62698 case EFBIG: return DRFLAC_TOO_BIG;
62699 #endif
62700 #ifdef ENOSPC
62701 case ENOSPC: return DRFLAC_NO_SPACE;
62702 #endif
62703 #ifdef ESPIPE
62704 case ESPIPE: return DRFLAC_BAD_SEEK;
62705 #endif
62706 #ifdef EROFS
62707 case EROFS: return DRFLAC_ACCESS_DENIED;
62708 #endif
62709 #ifdef EMLINK
62710 case EMLINK: return DRFLAC_TOO_MANY_LINKS;
62711 #endif
62712 #ifdef EPIPE
62713 case EPIPE: return DRFLAC_BAD_PIPE;
62714 #endif
62715 #ifdef EDOM
62716 case EDOM: return DRFLAC_OUT_OF_RANGE;
62717 #endif
62718 #ifdef ERANGE
62719 case ERANGE: return DRFLAC_OUT_OF_RANGE;
62720 #endif
62721 #ifdef EDEADLK
62722 case EDEADLK: return DRFLAC_DEADLOCK;
62723 #endif
62724 #ifdef ENAMETOOLONG
62725 case ENAMETOOLONG: return DRFLAC_PATH_TOO_LONG;
62726 #endif
62727 #ifdef ENOLCK
62728 case ENOLCK: return DRFLAC_ERROR;
62729 #endif
62730 #ifdef ENOSYS
62731 case ENOSYS: return DRFLAC_NOT_IMPLEMENTED;
62732 #endif
62733 #ifdef ENOTEMPTY
62734 case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY;
62735 #endif
62736 #ifdef ELOOP
62737 case ELOOP: return DRFLAC_TOO_MANY_LINKS;
62738 #endif
62739 #ifdef ENOMSG
62740 case ENOMSG: return DRFLAC_NO_MESSAGE;
62741 #endif
62742 #ifdef EIDRM
62743 case EIDRM: return DRFLAC_ERROR;
62744 #endif
62745 #ifdef ECHRNG
62746 case ECHRNG: return DRFLAC_ERROR;
62747 #endif
62748 #ifdef EL2NSYNC
62749 case EL2NSYNC: return DRFLAC_ERROR;
62750 #endif
62751 #ifdef EL3HLT
62752 case EL3HLT: return DRFLAC_ERROR;
62753 #endif
62754 #ifdef EL3RST
62755 case EL3RST: return DRFLAC_ERROR;
62756 #endif
62757 #ifdef ELNRNG
62758 case ELNRNG: return DRFLAC_OUT_OF_RANGE;
62759 #endif
62760 #ifdef EUNATCH
62761 case EUNATCH: return DRFLAC_ERROR;
62762 #endif
62763 #ifdef ENOCSI
62764 case ENOCSI: return DRFLAC_ERROR;
62765 #endif
62766 #ifdef EL2HLT
62767 case EL2HLT: return DRFLAC_ERROR;
62768 #endif
62769 #ifdef EBADE
62770 case EBADE: return DRFLAC_ERROR;
62771 #endif
62772 #ifdef EBADR
62773 case EBADR: return DRFLAC_ERROR;
62774 #endif
62775 #ifdef EXFULL
62776 case EXFULL: return DRFLAC_ERROR;
62777 #endif
62778 #ifdef ENOANO
62779 case ENOANO: return DRFLAC_ERROR;
62780 #endif
62781 #ifdef EBADRQC
62782 case EBADRQC: return DRFLAC_ERROR;
62783 #endif
62784 #ifdef EBADSLT
62785 case EBADSLT: return DRFLAC_ERROR;
62786 #endif
62787 #ifdef EBFONT
62788 case EBFONT: return DRFLAC_INVALID_FILE;
62789 #endif
62790 #ifdef ENOSTR
62791 case ENOSTR: return DRFLAC_ERROR;
62792 #endif
62793 #ifdef ENODATA
62794 case ENODATA: return DRFLAC_NO_DATA_AVAILABLE;
62795 #endif
62796 #ifdef ETIME
62797 case ETIME: return DRFLAC_TIMEOUT;
62798 #endif
62799 #ifdef ENOSR
62800 case ENOSR: return DRFLAC_NO_DATA_AVAILABLE;
62801 #endif
62802 #ifdef ENONET
62803 case ENONET: return DRFLAC_NO_NETWORK;
62804 #endif
62805 #ifdef ENOPKG
62806 case ENOPKG: return DRFLAC_ERROR;
62807 #endif
62808 #ifdef EREMOTE
62809 case EREMOTE: return DRFLAC_ERROR;
62810 #endif
62811 #ifdef ENOLINK
62812 case ENOLINK: return DRFLAC_ERROR;
62813 #endif
62814 #ifdef EADV
62815 case EADV: return DRFLAC_ERROR;
62816 #endif
62817 #ifdef ESRMNT
62818 case ESRMNT: return DRFLAC_ERROR;
62819 #endif
62820 #ifdef ECOMM
62821 case ECOMM: return DRFLAC_ERROR;
62822 #endif
62823 #ifdef EPROTO
62824 case EPROTO: return DRFLAC_ERROR;
62825 #endif
62826 #ifdef EMULTIHOP
62827 case EMULTIHOP: return DRFLAC_ERROR;
62828 #endif
62829 #ifdef EDOTDOT
62830 case EDOTDOT: return DRFLAC_ERROR;
62831 #endif
62832 #ifdef EBADMSG
62833 case EBADMSG: return DRFLAC_BAD_MESSAGE;
62834 #endif
62835 #ifdef EOVERFLOW
62836 case EOVERFLOW: return DRFLAC_TOO_BIG;
62837 #endif
62838 #ifdef ENOTUNIQ
62839 case ENOTUNIQ: return DRFLAC_NOT_UNIQUE;
62840 #endif
62841 #ifdef EBADFD
62842 case EBADFD: return DRFLAC_ERROR;
62843 #endif
62844 #ifdef EREMCHG
62845 case EREMCHG: return DRFLAC_ERROR;
62846 #endif
62847 #ifdef ELIBACC
62848 case ELIBACC: return DRFLAC_ACCESS_DENIED;
62849 #endif
62850 #ifdef ELIBBAD
62851 case ELIBBAD: return DRFLAC_INVALID_FILE;
62852 #endif
62853 #ifdef ELIBSCN
62854 case ELIBSCN: return DRFLAC_INVALID_FILE;
62855 #endif
62856 #ifdef ELIBMAX
62857 case ELIBMAX: return DRFLAC_ERROR;
62858 #endif
62859 #ifdef ELIBEXEC
62860 case ELIBEXEC: return DRFLAC_ERROR;
62861 #endif
62862 #ifdef EILSEQ
62863 case EILSEQ: return DRFLAC_INVALID_DATA;
62864 #endif
62865 #ifdef ERESTART
62866 case ERESTART: return DRFLAC_ERROR;
62867 #endif
62868 #ifdef ESTRPIPE
62869 case ESTRPIPE: return DRFLAC_ERROR;
62870 #endif
62871 #ifdef EUSERS
62872 case EUSERS: return DRFLAC_ERROR;
62873 #endif
62874 #ifdef ENOTSOCK
62875 case ENOTSOCK: return DRFLAC_NOT_SOCKET;
62876 #endif
62877 #ifdef EDESTADDRREQ
62878 case EDESTADDRREQ: return DRFLAC_NO_ADDRESS;
62879 #endif
62880 #ifdef EMSGSIZE
62881 case EMSGSIZE: return DRFLAC_TOO_BIG;
62882 #endif
62883 #ifdef EPROTOTYPE
62884 case EPROTOTYPE: return DRFLAC_BAD_PROTOCOL;
62885 #endif
62886 #ifdef ENOPROTOOPT
62887 case ENOPROTOOPT: return DRFLAC_PROTOCOL_UNAVAILABLE;
62888 #endif
62889 #ifdef EPROTONOSUPPORT
62890 case EPROTONOSUPPORT: return DRFLAC_PROTOCOL_NOT_SUPPORTED;
62891 #endif
62892 #ifdef ESOCKTNOSUPPORT
62893 case ESOCKTNOSUPPORT: return DRFLAC_SOCKET_NOT_SUPPORTED;
62894 #endif
62895 #ifdef EOPNOTSUPP
62896 case EOPNOTSUPP: return DRFLAC_INVALID_OPERATION;
62897 #endif
62898 #ifdef EPFNOSUPPORT
62899 case EPFNOSUPPORT: return DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED;
62900 #endif
62901 #ifdef EAFNOSUPPORT
62902 case EAFNOSUPPORT: return DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED;
62903 #endif
62904 #ifdef EADDRINUSE
62905 case EADDRINUSE: return DRFLAC_ALREADY_IN_USE;
62906 #endif
62907 #ifdef EADDRNOTAVAIL
62908 case EADDRNOTAVAIL: return DRFLAC_ERROR;
62909 #endif
62910 #ifdef ENETDOWN
62911 case ENETDOWN: return DRFLAC_NO_NETWORK;
62912 #endif
62913 #ifdef ENETUNREACH
62914 case ENETUNREACH: return DRFLAC_NO_NETWORK;
62915 #endif
62916 #ifdef ENETRESET
62917 case ENETRESET: return DRFLAC_NO_NETWORK;
62918 #endif
62919 #ifdef ECONNABORTED
62920 case ECONNABORTED: return DRFLAC_NO_NETWORK;
62921 #endif
62922 #ifdef ECONNRESET
62923 case ECONNRESET: return DRFLAC_CONNECTION_RESET;
62924 #endif
62925 #ifdef ENOBUFS
62926 case ENOBUFS: return DRFLAC_NO_SPACE;
62927 #endif
62928 #ifdef EISCONN
62929 case EISCONN: return DRFLAC_ALREADY_CONNECTED;
62930 #endif
62931 #ifdef ENOTCONN
62932 case ENOTCONN: return DRFLAC_NOT_CONNECTED;
62933 #endif
62934 #ifdef ESHUTDOWN
62935 case ESHUTDOWN: return DRFLAC_ERROR;
62936 #endif
62937 #ifdef ETOOMANYREFS
62938 case ETOOMANYREFS: return DRFLAC_ERROR;
62939 #endif
62940 #ifdef ETIMEDOUT
62941 case ETIMEDOUT: return DRFLAC_TIMEOUT;
62942 #endif
62943 #ifdef ECONNREFUSED
62944 case ECONNREFUSED: return DRFLAC_CONNECTION_REFUSED;
62945 #endif
62946 #ifdef EHOSTDOWN
62947 case EHOSTDOWN: return DRFLAC_NO_HOST;
62948 #endif
62949 #ifdef EHOSTUNREACH
62950 case EHOSTUNREACH: return DRFLAC_NO_HOST;
62951 #endif
62952 #ifdef EALREADY
62953 case EALREADY: return DRFLAC_IN_PROGRESS;
62954 #endif
62955 #ifdef EINPROGRESS
62956 case EINPROGRESS: return DRFLAC_IN_PROGRESS;
62957 #endif
62958 #ifdef ESTALE
62959 case ESTALE: return DRFLAC_INVALID_FILE;
62960 #endif
62961 #ifdef EUCLEAN
62962 case EUCLEAN: return DRFLAC_ERROR;
62963 #endif
62964 #ifdef ENOTNAM
62965 case ENOTNAM: return DRFLAC_ERROR;
62966 #endif
62967 #ifdef ENAVAIL
62968 case ENAVAIL: return DRFLAC_ERROR;
62969 #endif
62970 #ifdef EISNAM
62971 case EISNAM: return DRFLAC_ERROR;
62972 #endif
62973 #ifdef EREMOTEIO
62974 case EREMOTEIO: return DRFLAC_IO_ERROR;
62975 #endif
62976 #ifdef EDQUOT
62977 case EDQUOT: return DRFLAC_NO_SPACE;
62978 #endif
62979 #ifdef ENOMEDIUM
62980 case ENOMEDIUM: return DRFLAC_DOES_NOT_EXIST;
62981 #endif
62982 #ifdef EMEDIUMTYPE
62983 case EMEDIUMTYPE: return DRFLAC_ERROR;
62984 #endif
62985 #ifdef ECANCELED
62986 case ECANCELED: return DRFLAC_CANCELLED;
62987 #endif
62988 #ifdef ENOKEY
62989 case ENOKEY: return DRFLAC_ERROR;
62990 #endif
62991 #ifdef EKEYEXPIRED
62992 case EKEYEXPIRED: return DRFLAC_ERROR;
62993 #endif
62994 #ifdef EKEYREVOKED
62995 case EKEYREVOKED: return DRFLAC_ERROR;
62996 #endif
62997 #ifdef EKEYREJECTED
62998 case EKEYREJECTED: return DRFLAC_ERROR;
62999 #endif
63000 #ifdef EOWNERDEAD
63001 case EOWNERDEAD: return DRFLAC_ERROR;
63002 #endif
63003 #ifdef ENOTRECOVERABLE
63004 case ENOTRECOVERABLE: return DRFLAC_ERROR;
63005 #endif
63006 #ifdef ERFKILL
63007 case ERFKILL: return DRFLAC_ERROR;
63008 #endif
63009 #ifdef EHWPOISON
63010 case EHWPOISON: return DRFLAC_ERROR;
63011 #endif
63012 default: return DRFLAC_ERROR;
63013 }
63014}
63015static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
63016{
63017#if defined(_MSC_VER) && _MSC_VER >= 1400
63018 errno_t err;
63019#endif
63020 if (ppFile != NULL) {
63021 *ppFile = NULL;
63022 }
63023 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
63024 return DRFLAC_INVALID_ARGS;
63025 }
63026#if defined(_MSC_VER) && _MSC_VER >= 1400
63027 err = fopen_s(ppFile, pFilePath, pOpenMode);
63028 if (err != 0) {
63029 return drflac_result_from_errno(err);
63030 }
63031#else
63032#if defined(_WIN32) || defined(__APPLE__)
63033 *ppFile = fopen(pFilePath, pOpenMode);
63034#else
63035 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
63036 *ppFile = fopen64(pFilePath, pOpenMode);
63037 #else
63038 *ppFile = fopen(pFilePath, pOpenMode);
63039 #endif
63040#endif
63041 if (*ppFile == NULL) {
63042 drflac_result result = drflac_result_from_errno(errno);
63043 if (result == DRFLAC_SUCCESS) {
63044 result = DRFLAC_ERROR;
63045 }
63046 return result;
63047 }
63048#endif
63049 return DRFLAC_SUCCESS;
63050}
63051#if defined(_WIN32)
63052 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
63053 #define DRFLAC_HAS_WFOPEN
63054 #endif
63055#endif
63056static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks)
63057{
63058 if (ppFile != NULL) {
63059 *ppFile = NULL;
63060 }
63061 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
63062 return DRFLAC_INVALID_ARGS;
63063 }
63064#if defined(DRFLAC_HAS_WFOPEN)
63065 {
63066 #if defined(_MSC_VER) && _MSC_VER >= 1400
63067 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
63068 if (err != 0) {
63069 return drflac_result_from_errno(err);
63070 }
63071 #else
63072 *ppFile = _wfopen(pFilePath, pOpenMode);
63073 if (*ppFile == NULL) {
63074 return drflac_result_from_errno(errno);
63075 }
63076 #endif
63077 (void)pAllocationCallbacks;
63078 }
63079#else
63080 {
63081 mbstate_t mbs;
63082 size_t lenMB;
63083 const wchar_t* pFilePathTemp = pFilePath;
63084 char* pFilePathMB = NULL;
63085 char pOpenModeMB[32] = {0};
63086 DRFLAC_ZERO_OBJECT(&mbs);
63087 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
63088 if (lenMB == (size_t)-1) {
63089 return drflac_result_from_errno(errno);
63090 }
63091 pFilePathMB = (char*)drflac__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
63092 if (pFilePathMB == NULL) {
63093 return DRFLAC_OUT_OF_MEMORY;
63094 }
63095 pFilePathTemp = pFilePath;
63096 DRFLAC_ZERO_OBJECT(&mbs);
63097 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
63098 {
63099 size_t i = 0;
63100 for (;;) {
63101 if (pOpenMode[i] == 0) {
63102 pOpenModeMB[i] = '\0';
63103 break;
63104 }
63105 pOpenModeMB[i] = (char)pOpenMode[i];
63106 i += 1;
63107 }
63108 }
63109 *ppFile = fopen(pFilePathMB, pOpenModeMB);
63110 drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
63111 }
63112 if (*ppFile == NULL) {
63113 return DRFLAC_ERROR;
63114 }
63115#endif
63116 return DRFLAC_SUCCESS;
63117}
63118static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
63119{
63120 return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData);
63121}
63122static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin)
63123{
63124 DRFLAC_ASSERT(offset >= 0);
63125 return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
63126}
63127DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks)
63128{
63129 drflac* pFlac;
63130 FILE* pFile;
63131 if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) {
63132 return NULL;
63133 }
63134 pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
63135 if (pFlac == NULL) {
63136 fclose(pFile);
63137 return NULL;
63138 }
63139 return pFlac;
63140}
63141DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks)
63142{
63143 drflac* pFlac;
63144 FILE* pFile;
63145 if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) {
63146 return NULL;
63147 }
63148 pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
63149 if (pFlac == NULL) {
63150 fclose(pFile);
63151 return NULL;
63152 }
63153 return pFlac;
63154}
63155DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
63156{
63157 drflac* pFlac;
63158 FILE* pFile;
63159 if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) {
63160 return NULL;
63161 }
63162 pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
63163 if (pFlac == NULL) {
63164 fclose(pFile);
63165 return pFlac;
63166 }
63167 return pFlac;
63168}
63169DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
63170{
63171 drflac* pFlac;
63172 FILE* pFile;
63173 if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) {
63174 return NULL;
63175 }
63176 pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
63177 if (pFlac == NULL) {
63178 fclose(pFile);
63179 return pFlac;
63180 }
63181 return pFlac;
63182}
63183#endif
63184static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)
63185{
63186 drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
63187 size_t bytesRemaining;
63188 DRFLAC_ASSERT(memoryStream != NULL);
63189 DRFLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos);
63190 bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos;
63191 if (bytesToRead > bytesRemaining) {
63192 bytesToRead = bytesRemaining;
63193 }
63194 if (bytesToRead > 0) {
63195 DRFLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead);
63196 memoryStream->currentReadPos += bytesToRead;
63197 }
63198 return bytesToRead;
63199}
63200static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin)
63201{
63202 drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
63203 DRFLAC_ASSERT(memoryStream != NULL);
63204 DRFLAC_ASSERT(offset >= 0);
63205 if (offset > (drflac_int64)memoryStream->dataSize) {
63206 return DRFLAC_FALSE;
63207 }
63208 if (origin == drflac_seek_origin_current) {
63209 if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) {
63210 memoryStream->currentReadPos += offset;
63211 } else {
63212 return DRFLAC_FALSE;
63213 }
63214 } else {
63215 if ((drflac_uint32)offset <= memoryStream->dataSize) {
63216 memoryStream->currentReadPos = offset;
63217 } else {
63218 return DRFLAC_FALSE;
63219 }
63220 }
63221 return DRFLAC_TRUE;
63222}
63223DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks)
63224{
63225 drflac__memory_stream memoryStream;
63226 drflac* pFlac;
63227 memoryStream.data = (const drflac_uint8*)pData;
63228 memoryStream.dataSize = dataSize;
63229 memoryStream.currentReadPos = 0;
63230 pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks);
63231 if (pFlac == NULL) {
63232 return NULL;
63233 }
63234 pFlac->memoryStream = memoryStream;
63235#ifndef DR_FLAC_NO_OGG
63236 if (pFlac->container == drflac_container_ogg)
63237 {
63238 drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
63239 oggbs->pUserData = &pFlac->memoryStream;
63240 }
63241 else
63242#endif
63243 {
63244 pFlac->bs.pUserData = &pFlac->memoryStream;
63245 }
63246 return pFlac;
63247}
63248DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
63249{
63250 drflac__memory_stream memoryStream;
63251 drflac* pFlac;
63252 memoryStream.data = (const drflac_uint8*)pData;
63253 memoryStream.dataSize = dataSize;
63254 memoryStream.currentReadPos = 0;
63255 pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks);
63256 if (pFlac == NULL) {
63257 return NULL;
63258 }
63259 pFlac->memoryStream = memoryStream;
63260#ifndef DR_FLAC_NO_OGG
63261 if (pFlac->container == drflac_container_ogg)
63262 {
63263 drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
63264 oggbs->pUserData = &pFlac->memoryStream;
63265 }
63266 else
63267#endif
63268 {
63269 pFlac->bs.pUserData = &pFlac->memoryStream;
63270 }
63271 return pFlac;
63272}
63273DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
63274{
63275 return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
63276}
63277DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
63278{
63279 return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks);
63280}
63281DRFLAC_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)
63282{
63283 return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
63284}
63285DRFLAC_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)
63286{
63287 return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks);
63288}
63289DRFLAC_API void drflac_close(drflac* pFlac)
63290{
63291 if (pFlac == NULL) {
63292 return;
63293 }
63294#ifndef DR_FLAC_NO_STDIO
63295 if (pFlac->bs.onRead == drflac__on_read_stdio) {
63296 fclose((FILE*)pFlac->bs.pUserData);
63297 }
63298#ifndef DR_FLAC_NO_OGG
63299 if (pFlac->container == drflac_container_ogg) {
63300 drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
63301 DRFLAC_ASSERT(pFlac->bs.onRead == drflac__on_read_ogg);
63302 if (oggbs->onRead == drflac__on_read_stdio) {
63303 fclose((FILE*)oggbs->pUserData);
63304 }
63305 }
63306#endif
63307#endif
63308 drflac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks);
63309}
63310#if 0
63311static 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)
63312{
63313 drflac_uint64 i;
63314 for (i = 0; i < frameCount; ++i) {
63315 drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
63316 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
63317 drflac_uint32 right = left - side;
63318 pOutputSamples[i*2+0] = (drflac_int32)left;
63319 pOutputSamples[i*2+1] = (drflac_int32)right;
63320 }
63321}
63322#endif
63323static 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)
63324{
63325 drflac_uint64 i;
63326 drflac_uint64 frameCount4 = frameCount >> 2;
63327 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
63328 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
63329 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63330 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63331 for (i = 0; i < frameCount4; ++i) {
63332 drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
63333 drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
63334 drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
63335 drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
63336 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
63337 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
63338 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
63339 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
63340 drflac_uint32 right0 = left0 - side0;
63341 drflac_uint32 right1 = left1 - side1;
63342 drflac_uint32 right2 = left2 - side2;
63343 drflac_uint32 right3 = left3 - side3;
63344 pOutputSamples[i*8+0] = (drflac_int32)left0;
63345 pOutputSamples[i*8+1] = (drflac_int32)right0;
63346 pOutputSamples[i*8+2] = (drflac_int32)left1;
63347 pOutputSamples[i*8+3] = (drflac_int32)right1;
63348 pOutputSamples[i*8+4] = (drflac_int32)left2;
63349 pOutputSamples[i*8+5] = (drflac_int32)right2;
63350 pOutputSamples[i*8+6] = (drflac_int32)left3;
63351 pOutputSamples[i*8+7] = (drflac_int32)right3;
63352 }
63353 for (i = (frameCount4 << 2); i < frameCount; ++i) {
63354 drflac_uint32 left = pInputSamples0U32[i] << shift0;
63355 drflac_uint32 side = pInputSamples1U32[i] << shift1;
63356 drflac_uint32 right = left - side;
63357 pOutputSamples[i*2+0] = (drflac_int32)left;
63358 pOutputSamples[i*2+1] = (drflac_int32)right;
63359 }
63360}
63361#if defined(DRFLAC_SUPPORT_SSE2)
63362static 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)
63363{
63364 drflac_uint64 i;
63365 drflac_uint64 frameCount4 = frameCount >> 2;
63366 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
63367 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
63368 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63369 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63370 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
63371 for (i = 0; i < frameCount4; ++i) {
63372 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
63373 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
63374 __m128i right = _mm_sub_epi32(left, side);
63375 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
63376 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
63377 }
63378 for (i = (frameCount4 << 2); i < frameCount; ++i) {
63379 drflac_uint32 left = pInputSamples0U32[i] << shift0;
63380 drflac_uint32 side = pInputSamples1U32[i] << shift1;
63381 drflac_uint32 right = left - side;
63382 pOutputSamples[i*2+0] = (drflac_int32)left;
63383 pOutputSamples[i*2+1] = (drflac_int32)right;
63384 }
63385}
63386#endif
63387#if defined(DRFLAC_SUPPORT_NEON)
63388static 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)
63389{
63390 drflac_uint64 i;
63391 drflac_uint64 frameCount4 = frameCount >> 2;
63392 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
63393 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
63394 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63395 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63396 int32x4_t shift0_4;
63397 int32x4_t shift1_4;
63398 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
63399 shift0_4 = vdupq_n_s32(shift0);
63400 shift1_4 = vdupq_n_s32(shift1);
63401 for (i = 0; i < frameCount4; ++i) {
63402 uint32x4_t left;
63403 uint32x4_t side;
63404 uint32x4_t right;
63405 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
63406 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
63407 right = vsubq_u32(left, side);
63408 drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right));
63409 }
63410 for (i = (frameCount4 << 2); i < frameCount; ++i) {
63411 drflac_uint32 left = pInputSamples0U32[i] << shift0;
63412 drflac_uint32 side = pInputSamples1U32[i] << shift1;
63413 drflac_uint32 right = left - side;
63414 pOutputSamples[i*2+0] = (drflac_int32)left;
63415 pOutputSamples[i*2+1] = (drflac_int32)right;
63416 }
63417}
63418#endif
63419static 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)
63420{
63421#if defined(DRFLAC_SUPPORT_SSE2)
63422 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
63423 drflac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
63424 } else
63425#elif defined(DRFLAC_SUPPORT_NEON)
63426 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
63427 drflac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
63428 } else
63429#endif
63430 {
63431#if 0
63432 drflac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
63433#else
63434 drflac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
63435#endif
63436 }
63437}
63438#if 0
63439static 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)
63440{
63441 drflac_uint64 i;
63442 for (i = 0; i < frameCount; ++i) {
63443 drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
63444 drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
63445 drflac_uint32 left = right + side;
63446 pOutputSamples[i*2+0] = (drflac_int32)left;
63447 pOutputSamples[i*2+1] = (drflac_int32)right;
63448 }
63449}
63450#endif
63451static 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)
63452{
63453 drflac_uint64 i;
63454 drflac_uint64 frameCount4 = frameCount >> 2;
63455 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
63456 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
63457 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63458 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63459 for (i = 0; i < frameCount4; ++i) {
63460 drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
63461 drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
63462 drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
63463 drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
63464 drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
63465 drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
63466 drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
63467 drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
63468 drflac_uint32 left0 = right0 + side0;
63469 drflac_uint32 left1 = right1 + side1;
63470 drflac_uint32 left2 = right2 + side2;
63471 drflac_uint32 left3 = right3 + side3;
63472 pOutputSamples[i*8+0] = (drflac_int32)left0;
63473 pOutputSamples[i*8+1] = (drflac_int32)right0;
63474 pOutputSamples[i*8+2] = (drflac_int32)left1;
63475 pOutputSamples[i*8+3] = (drflac_int32)right1;
63476 pOutputSamples[i*8+4] = (drflac_int32)left2;
63477 pOutputSamples[i*8+5] = (drflac_int32)right2;
63478 pOutputSamples[i*8+6] = (drflac_int32)left3;
63479 pOutputSamples[i*8+7] = (drflac_int32)right3;
63480 }
63481 for (i = (frameCount4 << 2); i < frameCount; ++i) {
63482 drflac_uint32 side = pInputSamples0U32[i] << shift0;
63483 drflac_uint32 right = pInputSamples1U32[i] << shift1;
63484 drflac_uint32 left = right + side;
63485 pOutputSamples[i*2+0] = (drflac_int32)left;
63486 pOutputSamples[i*2+1] = (drflac_int32)right;
63487 }
63488}
63489#if defined(DRFLAC_SUPPORT_SSE2)
63490static 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)
63491{
63492 drflac_uint64 i;
63493 drflac_uint64 frameCount4 = frameCount >> 2;
63494 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
63495 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
63496 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63497 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63498 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
63499 for (i = 0; i < frameCount4; ++i) {
63500 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
63501 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
63502 __m128i left = _mm_add_epi32(right, side);
63503 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
63504 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
63505 }
63506 for (i = (frameCount4 << 2); i < frameCount; ++i) {
63507 drflac_uint32 side = pInputSamples0U32[i] << shift0;
63508 drflac_uint32 right = pInputSamples1U32[i] << shift1;
63509 drflac_uint32 left = right + side;
63510 pOutputSamples[i*2+0] = (drflac_int32)left;
63511 pOutputSamples[i*2+1] = (drflac_int32)right;
63512 }
63513}
63514#endif
63515#if defined(DRFLAC_SUPPORT_NEON)
63516static 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)
63517{
63518 drflac_uint64 i;
63519 drflac_uint64 frameCount4 = frameCount >> 2;
63520 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
63521 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
63522 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63523 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63524 int32x4_t shift0_4;
63525 int32x4_t shift1_4;
63526 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
63527 shift0_4 = vdupq_n_s32(shift0);
63528 shift1_4 = vdupq_n_s32(shift1);
63529 for (i = 0; i < frameCount4; ++i) {
63530 uint32x4_t side;
63531 uint32x4_t right;
63532 uint32x4_t left;
63533 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
63534 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
63535 left = vaddq_u32(right, side);
63536 drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right));
63537 }
63538 for (i = (frameCount4 << 2); i < frameCount; ++i) {
63539 drflac_uint32 side = pInputSamples0U32[i] << shift0;
63540 drflac_uint32 right = pInputSamples1U32[i] << shift1;
63541 drflac_uint32 left = right + side;
63542 pOutputSamples[i*2+0] = (drflac_int32)left;
63543 pOutputSamples[i*2+1] = (drflac_int32)right;
63544 }
63545}
63546#endif
63547static 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)
63548{
63549#if defined(DRFLAC_SUPPORT_SSE2)
63550 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
63551 drflac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
63552 } else
63553#elif defined(DRFLAC_SUPPORT_NEON)
63554 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
63555 drflac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
63556 } else
63557#endif
63558 {
63559#if 0
63560 drflac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
63561#else
63562 drflac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
63563#endif
63564 }
63565}
63566#if 0
63567static 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)
63568{
63569 for (drflac_uint64 i = 0; i < frameCount; ++i) {
63570 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63571 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63572 mid = (mid << 1) | (side & 0x01);
63573 pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample);
63574 pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample);
63575 }
63576}
63577#endif
63578static 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)
63579{
63580 drflac_uint64 i;
63581 drflac_uint64 frameCount4 = frameCount >> 2;
63582 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
63583 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
63584 drflac_int32 shift = unusedBitsPerSample;
63585 if (shift > 0) {
63586 shift -= 1;
63587 for (i = 0; i < frameCount4; ++i) {
63588 drflac_uint32 temp0L;
63589 drflac_uint32 temp1L;
63590 drflac_uint32 temp2L;
63591 drflac_uint32 temp3L;
63592 drflac_uint32 temp0R;
63593 drflac_uint32 temp1R;
63594 drflac_uint32 temp2R;
63595 drflac_uint32 temp3R;
63596 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63597 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63598 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63599 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63600 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63601 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63602 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63603 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63604 mid0 = (mid0 << 1) | (side0 & 0x01);
63605 mid1 = (mid1 << 1) | (side1 & 0x01);
63606 mid2 = (mid2 << 1) | (side2 & 0x01);
63607 mid3 = (mid3 << 1) | (side3 & 0x01);
63608 temp0L = (mid0 + side0) << shift;
63609 temp1L = (mid1 + side1) << shift;
63610 temp2L = (mid2 + side2) << shift;
63611 temp3L = (mid3 + side3) << shift;
63612 temp0R = (mid0 - side0) << shift;
63613 temp1R = (mid1 - side1) << shift;
63614 temp2R = (mid2 - side2) << shift;
63615 temp3R = (mid3 - side3) << shift;
63616 pOutputSamples[i*8+0] = (drflac_int32)temp0L;
63617 pOutputSamples[i*8+1] = (drflac_int32)temp0R;
63618 pOutputSamples[i*8+2] = (drflac_int32)temp1L;
63619 pOutputSamples[i*8+3] = (drflac_int32)temp1R;
63620 pOutputSamples[i*8+4] = (drflac_int32)temp2L;
63621 pOutputSamples[i*8+5] = (drflac_int32)temp2R;
63622 pOutputSamples[i*8+6] = (drflac_int32)temp3L;
63623 pOutputSamples[i*8+7] = (drflac_int32)temp3R;
63624 }
63625 } else {
63626 for (i = 0; i < frameCount4; ++i) {
63627 drflac_uint32 temp0L;
63628 drflac_uint32 temp1L;
63629 drflac_uint32 temp2L;
63630 drflac_uint32 temp3L;
63631 drflac_uint32 temp0R;
63632 drflac_uint32 temp1R;
63633 drflac_uint32 temp2R;
63634 drflac_uint32 temp3R;
63635 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63636 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63637 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63638 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63639 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63640 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63641 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63642 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63643 mid0 = (mid0 << 1) | (side0 & 0x01);
63644 mid1 = (mid1 << 1) | (side1 & 0x01);
63645 mid2 = (mid2 << 1) | (side2 & 0x01);
63646 mid3 = (mid3 << 1) | (side3 & 0x01);
63647 temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1);
63648 temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1);
63649 temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1);
63650 temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1);
63651 temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1);
63652 temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1);
63653 temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1);
63654 temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1);
63655 pOutputSamples[i*8+0] = (drflac_int32)temp0L;
63656 pOutputSamples[i*8+1] = (drflac_int32)temp0R;
63657 pOutputSamples[i*8+2] = (drflac_int32)temp1L;
63658 pOutputSamples[i*8+3] = (drflac_int32)temp1R;
63659 pOutputSamples[i*8+4] = (drflac_int32)temp2L;
63660 pOutputSamples[i*8+5] = (drflac_int32)temp2R;
63661 pOutputSamples[i*8+6] = (drflac_int32)temp3L;
63662 pOutputSamples[i*8+7] = (drflac_int32)temp3R;
63663 }
63664 }
63665 for (i = (frameCount4 << 2); i < frameCount; ++i) {
63666 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63667 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63668 mid = (mid << 1) | (side & 0x01);
63669 pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample);
63670 pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample);
63671 }
63672}
63673#if defined(DRFLAC_SUPPORT_SSE2)
63674static 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)
63675{
63676 drflac_uint64 i;
63677 drflac_uint64 frameCount4 = frameCount >> 2;
63678 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
63679 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
63680 drflac_int32 shift = unusedBitsPerSample;
63681 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
63682 if (shift == 0) {
63683 for (i = 0; i < frameCount4; ++i) {
63684 __m128i mid;
63685 __m128i side;
63686 __m128i left;
63687 __m128i right;
63688 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
63689 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
63690 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
63691 left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
63692 right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
63693 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
63694 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
63695 }
63696 for (i = (frameCount4 << 2); i < frameCount; ++i) {
63697 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63698 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63699 mid = (mid << 1) | (side & 0x01);
63700 pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1;
63701 pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1;
63702 }
63703 } else {
63704 shift -= 1;
63705 for (i = 0; i < frameCount4; ++i) {
63706 __m128i mid;
63707 __m128i side;
63708 __m128i left;
63709 __m128i right;
63710 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
63711 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
63712 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
63713 left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
63714 right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
63715 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
63716 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
63717 }
63718 for (i = (frameCount4 << 2); i < frameCount; ++i) {
63719 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63720 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63721 mid = (mid << 1) | (side & 0x01);
63722 pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift);
63723 pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift);
63724 }
63725 }
63726}
63727#endif
63728#if defined(DRFLAC_SUPPORT_NEON)
63729static 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)
63730{
63731 drflac_uint64 i;
63732 drflac_uint64 frameCount4 = frameCount >> 2;
63733 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
63734 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
63735 drflac_int32 shift = unusedBitsPerSample;
63736 int32x4_t wbpsShift0_4;
63737 int32x4_t wbpsShift1_4;
63738 uint32x4_t one4;
63739 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
63740 wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
63741 wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
63742 one4 = vdupq_n_u32(1);
63743 if (shift == 0) {
63744 for (i = 0; i < frameCount4; ++i) {
63745 uint32x4_t mid;
63746 uint32x4_t side;
63747 int32x4_t left;
63748 int32x4_t right;
63749 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
63750 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
63751 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4));
63752 left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
63753 right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
63754 drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
63755 }
63756 for (i = (frameCount4 << 2); i < frameCount; ++i) {
63757 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63758 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63759 mid = (mid << 1) | (side & 0x01);
63760 pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1;
63761 pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1;
63762 }
63763 } else {
63764 int32x4_t shift4;
63765 shift -= 1;
63766 shift4 = vdupq_n_s32(shift);
63767 for (i = 0; i < frameCount4; ++i) {
63768 uint32x4_t mid;
63769 uint32x4_t side;
63770 int32x4_t left;
63771 int32x4_t right;
63772 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
63773 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
63774 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4));
63775 left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
63776 right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
63777 drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
63778 }
63779 for (i = (frameCount4 << 2); i < frameCount; ++i) {
63780 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63781 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63782 mid = (mid << 1) | (side & 0x01);
63783 pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift);
63784 pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift);
63785 }
63786 }
63787}
63788#endif
63789static 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)
63790{
63791#if defined(DRFLAC_SUPPORT_SSE2)
63792 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
63793 drflac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
63794 } else
63795#elif defined(DRFLAC_SUPPORT_NEON)
63796 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
63797 drflac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
63798 } else
63799#endif
63800 {
63801#if 0
63802 drflac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
63803#else
63804 drflac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
63805#endif
63806 }
63807}
63808#if 0
63809static 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)
63810{
63811 for (drflac_uint64 i = 0; i < frameCount; ++i) {
63812 pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample));
63813 pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample));
63814 }
63815}
63816#endif
63817static 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)
63818{
63819 drflac_uint64 i;
63820 drflac_uint64 frameCount4 = frameCount >> 2;
63821 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
63822 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
63823 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63824 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63825 for (i = 0; i < frameCount4; ++i) {
63826 drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
63827 drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
63828 drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
63829 drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
63830 drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
63831 drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
63832 drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
63833 drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
63834 pOutputSamples[i*8+0] = (drflac_int32)tempL0;
63835 pOutputSamples[i*8+1] = (drflac_int32)tempR0;
63836 pOutputSamples[i*8+2] = (drflac_int32)tempL1;
63837 pOutputSamples[i*8+3] = (drflac_int32)tempR1;
63838 pOutputSamples[i*8+4] = (drflac_int32)tempL2;
63839 pOutputSamples[i*8+5] = (drflac_int32)tempR2;
63840 pOutputSamples[i*8+6] = (drflac_int32)tempL3;
63841 pOutputSamples[i*8+7] = (drflac_int32)tempR3;
63842 }
63843 for (i = (frameCount4 << 2); i < frameCount; ++i) {
63844 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0);
63845 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1);
63846 }
63847}
63848#if defined(DRFLAC_SUPPORT_SSE2)
63849static 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)
63850{
63851 drflac_uint64 i;
63852 drflac_uint64 frameCount4 = frameCount >> 2;
63853 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
63854 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
63855 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63856 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63857 for (i = 0; i < frameCount4; ++i) {
63858 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
63859 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
63860 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
63861 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
63862 }
63863 for (i = (frameCount4 << 2); i < frameCount; ++i) {
63864 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0);
63865 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1);
63866 }
63867}
63868#endif
63869#if defined(DRFLAC_SUPPORT_NEON)
63870static 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)
63871{
63872 drflac_uint64 i;
63873 drflac_uint64 frameCount4 = frameCount >> 2;
63874 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
63875 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
63876 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
63877 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
63878 int32x4_t shift4_0 = vdupq_n_s32(shift0);
63879 int32x4_t shift4_1 = vdupq_n_s32(shift1);
63880 for (i = 0; i < frameCount4; ++i) {
63881 int32x4_t left;
63882 int32x4_t right;
63883 left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0));
63884 right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1));
63885 drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
63886 }
63887 for (i = (frameCount4 << 2); i < frameCount; ++i) {
63888 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0);
63889 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1);
63890 }
63891}
63892#endif
63893static 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)
63894{
63895#if defined(DRFLAC_SUPPORT_SSE2)
63896 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
63897 drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
63898 } else
63899#elif defined(DRFLAC_SUPPORT_NEON)
63900 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
63901 drflac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
63902 } else
63903#endif
63904 {
63905#if 0
63906 drflac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
63907#else
63908 drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
63909#endif
63910 }
63911}
63912DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut)
63913{
63914 drflac_uint64 framesRead;
63915 drflac_uint32 unusedBitsPerSample;
63916 if (pFlac == NULL || framesToRead == 0) {
63917 return 0;
63918 }
63919 if (pBufferOut == NULL) {
63920 return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
63921 }
63922 DRFLAC_ASSERT(pFlac->bitsPerSample <= 32);
63923 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
63924 framesRead = 0;
63925 while (framesToRead > 0) {
63926 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
63927 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
63928 break;
63929 }
63930 } else {
63931 unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
63932 drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
63933 drflac_uint64 frameCountThisIteration = framesToRead;
63934 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
63935 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
63936 }
63937 if (channelCount == 2) {
63938 const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
63939 const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
63940 switch (pFlac->currentFLACFrame.header.channelAssignment)
63941 {
63942 case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
63943 {
63944 drflac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
63945 } break;
63946 case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
63947 {
63948 drflac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
63949 } break;
63950 case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
63951 {
63952 drflac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
63953 } break;
63954 case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
63955 default:
63956 {
63957 drflac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
63958 } break;
63959 }
63960 } else {
63961 drflac_uint64 i;
63962 for (i = 0; i < frameCountThisIteration; ++i) {
63963 unsigned int j;
63964 for (j = 0; j < channelCount; ++j) {
63965 pBufferOut[(i*channelCount)+j] = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
63966 }
63967 }
63968 }
63969 framesRead += frameCountThisIteration;
63970 pBufferOut += frameCountThisIteration * channelCount;
63971 framesToRead -= frameCountThisIteration;
63972 pFlac->currentPCMFrame += frameCountThisIteration;
63973 pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration;
63974 }
63975 }
63976 return framesRead;
63977}
63978#if 0
63979static 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)
63980{
63981 drflac_uint64 i;
63982 for (i = 0; i < frameCount; ++i) {
63983 drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
63984 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
63985 drflac_uint32 right = left - side;
63986 left >>= 16;
63987 right >>= 16;
63988 pOutputSamples[i*2+0] = (drflac_int16)left;
63989 pOutputSamples[i*2+1] = (drflac_int16)right;
63990 }
63991}
63992#endif
63993static 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)
63994{
63995 drflac_uint64 i;
63996 drflac_uint64 frameCount4 = frameCount >> 2;
63997 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
63998 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
63999 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64000 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64001 for (i = 0; i < frameCount4; ++i) {
64002 drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
64003 drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
64004 drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
64005 drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
64006 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
64007 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
64008 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
64009 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
64010 drflac_uint32 right0 = left0 - side0;
64011 drflac_uint32 right1 = left1 - side1;
64012 drflac_uint32 right2 = left2 - side2;
64013 drflac_uint32 right3 = left3 - side3;
64014 left0 >>= 16;
64015 left1 >>= 16;
64016 left2 >>= 16;
64017 left3 >>= 16;
64018 right0 >>= 16;
64019 right1 >>= 16;
64020 right2 >>= 16;
64021 right3 >>= 16;
64022 pOutputSamples[i*8+0] = (drflac_int16)left0;
64023 pOutputSamples[i*8+1] = (drflac_int16)right0;
64024 pOutputSamples[i*8+2] = (drflac_int16)left1;
64025 pOutputSamples[i*8+3] = (drflac_int16)right1;
64026 pOutputSamples[i*8+4] = (drflac_int16)left2;
64027 pOutputSamples[i*8+5] = (drflac_int16)right2;
64028 pOutputSamples[i*8+6] = (drflac_int16)left3;
64029 pOutputSamples[i*8+7] = (drflac_int16)right3;
64030 }
64031 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64032 drflac_uint32 left = pInputSamples0U32[i] << shift0;
64033 drflac_uint32 side = pInputSamples1U32[i] << shift1;
64034 drflac_uint32 right = left - side;
64035 left >>= 16;
64036 right >>= 16;
64037 pOutputSamples[i*2+0] = (drflac_int16)left;
64038 pOutputSamples[i*2+1] = (drflac_int16)right;
64039 }
64040}
64041#if defined(DRFLAC_SUPPORT_SSE2)
64042static 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)
64043{
64044 drflac_uint64 i;
64045 drflac_uint64 frameCount4 = frameCount >> 2;
64046 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64047 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64048 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64049 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64050 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
64051 for (i = 0; i < frameCount4; ++i) {
64052 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
64053 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
64054 __m128i right = _mm_sub_epi32(left, side);
64055 left = _mm_srai_epi32(left, 16);
64056 right = _mm_srai_epi32(right, 16);
64057 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
64058 }
64059 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64060 drflac_uint32 left = pInputSamples0U32[i] << shift0;
64061 drflac_uint32 side = pInputSamples1U32[i] << shift1;
64062 drflac_uint32 right = left - side;
64063 left >>= 16;
64064 right >>= 16;
64065 pOutputSamples[i*2+0] = (drflac_int16)left;
64066 pOutputSamples[i*2+1] = (drflac_int16)right;
64067 }
64068}
64069#endif
64070#if defined(DRFLAC_SUPPORT_NEON)
64071static 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)
64072{
64073 drflac_uint64 i;
64074 drflac_uint64 frameCount4 = frameCount >> 2;
64075 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64076 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64077 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64078 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64079 int32x4_t shift0_4;
64080 int32x4_t shift1_4;
64081 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
64082 shift0_4 = vdupq_n_s32(shift0);
64083 shift1_4 = vdupq_n_s32(shift1);
64084 for (i = 0; i < frameCount4; ++i) {
64085 uint32x4_t left;
64086 uint32x4_t side;
64087 uint32x4_t right;
64088 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
64089 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
64090 right = vsubq_u32(left, side);
64091 left = vshrq_n_u32(left, 16);
64092 right = vshrq_n_u32(right, 16);
64093 drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right)));
64094 }
64095 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64096 drflac_uint32 left = pInputSamples0U32[i] << shift0;
64097 drflac_uint32 side = pInputSamples1U32[i] << shift1;
64098 drflac_uint32 right = left - side;
64099 left >>= 16;
64100 right >>= 16;
64101 pOutputSamples[i*2+0] = (drflac_int16)left;
64102 pOutputSamples[i*2+1] = (drflac_int16)right;
64103 }
64104}
64105#endif
64106static 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)
64107{
64108#if defined(DRFLAC_SUPPORT_SSE2)
64109 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
64110 drflac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64111 } else
64112#elif defined(DRFLAC_SUPPORT_NEON)
64113 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
64114 drflac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64115 } else
64116#endif
64117 {
64118#if 0
64119 drflac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64120#else
64121 drflac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64122#endif
64123 }
64124}
64125#if 0
64126static 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)
64127{
64128 drflac_uint64 i;
64129 for (i = 0; i < frameCount; ++i) {
64130 drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
64131 drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
64132 drflac_uint32 left = right + side;
64133 left >>= 16;
64134 right >>= 16;
64135 pOutputSamples[i*2+0] = (drflac_int16)left;
64136 pOutputSamples[i*2+1] = (drflac_int16)right;
64137 }
64138}
64139#endif
64140static 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)
64141{
64142 drflac_uint64 i;
64143 drflac_uint64 frameCount4 = frameCount >> 2;
64144 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64145 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64146 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64147 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64148 for (i = 0; i < frameCount4; ++i) {
64149 drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
64150 drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
64151 drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
64152 drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
64153 drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
64154 drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
64155 drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
64156 drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
64157 drflac_uint32 left0 = right0 + side0;
64158 drflac_uint32 left1 = right1 + side1;
64159 drflac_uint32 left2 = right2 + side2;
64160 drflac_uint32 left3 = right3 + side3;
64161 left0 >>= 16;
64162 left1 >>= 16;
64163 left2 >>= 16;
64164 left3 >>= 16;
64165 right0 >>= 16;
64166 right1 >>= 16;
64167 right2 >>= 16;
64168 right3 >>= 16;
64169 pOutputSamples[i*8+0] = (drflac_int16)left0;
64170 pOutputSamples[i*8+1] = (drflac_int16)right0;
64171 pOutputSamples[i*8+2] = (drflac_int16)left1;
64172 pOutputSamples[i*8+3] = (drflac_int16)right1;
64173 pOutputSamples[i*8+4] = (drflac_int16)left2;
64174 pOutputSamples[i*8+5] = (drflac_int16)right2;
64175 pOutputSamples[i*8+6] = (drflac_int16)left3;
64176 pOutputSamples[i*8+7] = (drflac_int16)right3;
64177 }
64178 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64179 drflac_uint32 side = pInputSamples0U32[i] << shift0;
64180 drflac_uint32 right = pInputSamples1U32[i] << shift1;
64181 drflac_uint32 left = right + side;
64182 left >>= 16;
64183 right >>= 16;
64184 pOutputSamples[i*2+0] = (drflac_int16)left;
64185 pOutputSamples[i*2+1] = (drflac_int16)right;
64186 }
64187}
64188#if defined(DRFLAC_SUPPORT_SSE2)
64189static 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)
64190{
64191 drflac_uint64 i;
64192 drflac_uint64 frameCount4 = frameCount >> 2;
64193 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64194 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64195 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64196 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64197 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
64198 for (i = 0; i < frameCount4; ++i) {
64199 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
64200 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
64201 __m128i left = _mm_add_epi32(right, side);
64202 left = _mm_srai_epi32(left, 16);
64203 right = _mm_srai_epi32(right, 16);
64204 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
64205 }
64206 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64207 drflac_uint32 side = pInputSamples0U32[i] << shift0;
64208 drflac_uint32 right = pInputSamples1U32[i] << shift1;
64209 drflac_uint32 left = right + side;
64210 left >>= 16;
64211 right >>= 16;
64212 pOutputSamples[i*2+0] = (drflac_int16)left;
64213 pOutputSamples[i*2+1] = (drflac_int16)right;
64214 }
64215}
64216#endif
64217#if defined(DRFLAC_SUPPORT_NEON)
64218static 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)
64219{
64220 drflac_uint64 i;
64221 drflac_uint64 frameCount4 = frameCount >> 2;
64222 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64223 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64224 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64225 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64226 int32x4_t shift0_4;
64227 int32x4_t shift1_4;
64228 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
64229 shift0_4 = vdupq_n_s32(shift0);
64230 shift1_4 = vdupq_n_s32(shift1);
64231 for (i = 0; i < frameCount4; ++i) {
64232 uint32x4_t side;
64233 uint32x4_t right;
64234 uint32x4_t left;
64235 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
64236 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
64237 left = vaddq_u32(right, side);
64238 left = vshrq_n_u32(left, 16);
64239 right = vshrq_n_u32(right, 16);
64240 drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right)));
64241 }
64242 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64243 drflac_uint32 side = pInputSamples0U32[i] << shift0;
64244 drflac_uint32 right = pInputSamples1U32[i] << shift1;
64245 drflac_uint32 left = right + side;
64246 left >>= 16;
64247 right >>= 16;
64248 pOutputSamples[i*2+0] = (drflac_int16)left;
64249 pOutputSamples[i*2+1] = (drflac_int16)right;
64250 }
64251}
64252#endif
64253static 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)
64254{
64255#if defined(DRFLAC_SUPPORT_SSE2)
64256 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
64257 drflac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64258 } else
64259#elif defined(DRFLAC_SUPPORT_NEON)
64260 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
64261 drflac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64262 } else
64263#endif
64264 {
64265#if 0
64266 drflac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64267#else
64268 drflac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64269#endif
64270 }
64271}
64272#if 0
64273static 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)
64274{
64275 for (drflac_uint64 i = 0; i < frameCount; ++i) {
64276 drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64277 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64278 mid = (mid << 1) | (side & 0x01);
64279 pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16);
64280 pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16);
64281 }
64282}
64283#endif
64284static 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)
64285{
64286 drflac_uint64 i;
64287 drflac_uint64 frameCount4 = frameCount >> 2;
64288 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64289 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64290 drflac_uint32 shift = unusedBitsPerSample;
64291 if (shift > 0) {
64292 shift -= 1;
64293 for (i = 0; i < frameCount4; ++i) {
64294 drflac_uint32 temp0L;
64295 drflac_uint32 temp1L;
64296 drflac_uint32 temp2L;
64297 drflac_uint32 temp3L;
64298 drflac_uint32 temp0R;
64299 drflac_uint32 temp1R;
64300 drflac_uint32 temp2R;
64301 drflac_uint32 temp3R;
64302 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64303 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64304 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64305 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64306 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64307 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64308 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64309 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64310 mid0 = (mid0 << 1) | (side0 & 0x01);
64311 mid1 = (mid1 << 1) | (side1 & 0x01);
64312 mid2 = (mid2 << 1) | (side2 & 0x01);
64313 mid3 = (mid3 << 1) | (side3 & 0x01);
64314 temp0L = (mid0 + side0) << shift;
64315 temp1L = (mid1 + side1) << shift;
64316 temp2L = (mid2 + side2) << shift;
64317 temp3L = (mid3 + side3) << shift;
64318 temp0R = (mid0 - side0) << shift;
64319 temp1R = (mid1 - side1) << shift;
64320 temp2R = (mid2 - side2) << shift;
64321 temp3R = (mid3 - side3) << shift;
64322 temp0L >>= 16;
64323 temp1L >>= 16;
64324 temp2L >>= 16;
64325 temp3L >>= 16;
64326 temp0R >>= 16;
64327 temp1R >>= 16;
64328 temp2R >>= 16;
64329 temp3R >>= 16;
64330 pOutputSamples[i*8+0] = (drflac_int16)temp0L;
64331 pOutputSamples[i*8+1] = (drflac_int16)temp0R;
64332 pOutputSamples[i*8+2] = (drflac_int16)temp1L;
64333 pOutputSamples[i*8+3] = (drflac_int16)temp1R;
64334 pOutputSamples[i*8+4] = (drflac_int16)temp2L;
64335 pOutputSamples[i*8+5] = (drflac_int16)temp2R;
64336 pOutputSamples[i*8+6] = (drflac_int16)temp3L;
64337 pOutputSamples[i*8+7] = (drflac_int16)temp3R;
64338 }
64339 } else {
64340 for (i = 0; i < frameCount4; ++i) {
64341 drflac_uint32 temp0L;
64342 drflac_uint32 temp1L;
64343 drflac_uint32 temp2L;
64344 drflac_uint32 temp3L;
64345 drflac_uint32 temp0R;
64346 drflac_uint32 temp1R;
64347 drflac_uint32 temp2R;
64348 drflac_uint32 temp3R;
64349 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64350 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64351 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64352 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64353 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64354 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64355 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64356 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64357 mid0 = (mid0 << 1) | (side0 & 0x01);
64358 mid1 = (mid1 << 1) | (side1 & 0x01);
64359 mid2 = (mid2 << 1) | (side2 & 0x01);
64360 mid3 = (mid3 << 1) | (side3 & 0x01);
64361 temp0L = ((drflac_int32)(mid0 + side0) >> 1);
64362 temp1L = ((drflac_int32)(mid1 + side1) >> 1);
64363 temp2L = ((drflac_int32)(mid2 + side2) >> 1);
64364 temp3L = ((drflac_int32)(mid3 + side3) >> 1);
64365 temp0R = ((drflac_int32)(mid0 - side0) >> 1);
64366 temp1R = ((drflac_int32)(mid1 - side1) >> 1);
64367 temp2R = ((drflac_int32)(mid2 - side2) >> 1);
64368 temp3R = ((drflac_int32)(mid3 - side3) >> 1);
64369 temp0L >>= 16;
64370 temp1L >>= 16;
64371 temp2L >>= 16;
64372 temp3L >>= 16;
64373 temp0R >>= 16;
64374 temp1R >>= 16;
64375 temp2R >>= 16;
64376 temp3R >>= 16;
64377 pOutputSamples[i*8+0] = (drflac_int16)temp0L;
64378 pOutputSamples[i*8+1] = (drflac_int16)temp0R;
64379 pOutputSamples[i*8+2] = (drflac_int16)temp1L;
64380 pOutputSamples[i*8+3] = (drflac_int16)temp1R;
64381 pOutputSamples[i*8+4] = (drflac_int16)temp2L;
64382 pOutputSamples[i*8+5] = (drflac_int16)temp2R;
64383 pOutputSamples[i*8+6] = (drflac_int16)temp3L;
64384 pOutputSamples[i*8+7] = (drflac_int16)temp3R;
64385 }
64386 }
64387 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64388 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64389 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64390 mid = (mid << 1) | (side & 0x01);
64391 pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16);
64392 pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16);
64393 }
64394}
64395#if defined(DRFLAC_SUPPORT_SSE2)
64396static 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)
64397{
64398 drflac_uint64 i;
64399 drflac_uint64 frameCount4 = frameCount >> 2;
64400 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64401 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64402 drflac_uint32 shift = unusedBitsPerSample;
64403 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
64404 if (shift == 0) {
64405 for (i = 0; i < frameCount4; ++i) {
64406 __m128i mid;
64407 __m128i side;
64408 __m128i left;
64409 __m128i right;
64410 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
64411 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
64412 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
64413 left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
64414 right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
64415 left = _mm_srai_epi32(left, 16);
64416 right = _mm_srai_epi32(right, 16);
64417 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
64418 }
64419 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64420 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64421 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64422 mid = (mid << 1) | (side & 0x01);
64423 pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16);
64424 pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16);
64425 }
64426 } else {
64427 shift -= 1;
64428 for (i = 0; i < frameCount4; ++i) {
64429 __m128i mid;
64430 __m128i side;
64431 __m128i left;
64432 __m128i right;
64433 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
64434 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
64435 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
64436 left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
64437 right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
64438 left = _mm_srai_epi32(left, 16);
64439 right = _mm_srai_epi32(right, 16);
64440 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
64441 }
64442 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64443 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64444 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64445 mid = (mid << 1) | (side & 0x01);
64446 pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16);
64447 pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16);
64448 }
64449 }
64450}
64451#endif
64452#if defined(DRFLAC_SUPPORT_NEON)
64453static 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)
64454{
64455 drflac_uint64 i;
64456 drflac_uint64 frameCount4 = frameCount >> 2;
64457 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64458 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64459 drflac_uint32 shift = unusedBitsPerSample;
64460 int32x4_t wbpsShift0_4;
64461 int32x4_t wbpsShift1_4;
64462 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
64463 wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
64464 wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
64465 if (shift == 0) {
64466 for (i = 0; i < frameCount4; ++i) {
64467 uint32x4_t mid;
64468 uint32x4_t side;
64469 int32x4_t left;
64470 int32x4_t right;
64471 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
64472 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
64473 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
64474 left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
64475 right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
64476 left = vshrq_n_s32(left, 16);
64477 right = vshrq_n_s32(right, 16);
64478 drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
64479 }
64480 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64481 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64482 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64483 mid = (mid << 1) | (side & 0x01);
64484 pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16);
64485 pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16);
64486 }
64487 } else {
64488 int32x4_t shift4;
64489 shift -= 1;
64490 shift4 = vdupq_n_s32(shift);
64491 for (i = 0; i < frameCount4; ++i) {
64492 uint32x4_t mid;
64493 uint32x4_t side;
64494 int32x4_t left;
64495 int32x4_t right;
64496 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
64497 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
64498 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
64499 left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
64500 right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
64501 left = vshrq_n_s32(left, 16);
64502 right = vshrq_n_s32(right, 16);
64503 drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
64504 }
64505 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64506 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64507 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64508 mid = (mid << 1) | (side & 0x01);
64509 pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16);
64510 pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16);
64511 }
64512 }
64513}
64514#endif
64515static 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)
64516{
64517#if defined(DRFLAC_SUPPORT_SSE2)
64518 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
64519 drflac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64520 } else
64521#elif defined(DRFLAC_SUPPORT_NEON)
64522 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
64523 drflac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64524 } else
64525#endif
64526 {
64527#if 0
64528 drflac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64529#else
64530 drflac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64531#endif
64532 }
64533}
64534#if 0
64535static 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)
64536{
64537 for (drflac_uint64 i = 0; i < frameCount; ++i) {
64538 pOutputSamples[i*2+0] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16);
64539 pOutputSamples[i*2+1] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16);
64540 }
64541}
64542#endif
64543static 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)
64544{
64545 drflac_uint64 i;
64546 drflac_uint64 frameCount4 = frameCount >> 2;
64547 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64548 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64549 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64550 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64551 for (i = 0; i < frameCount4; ++i) {
64552 drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
64553 drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
64554 drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
64555 drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
64556 drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
64557 drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
64558 drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
64559 drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
64560 tempL0 >>= 16;
64561 tempL1 >>= 16;
64562 tempL2 >>= 16;
64563 tempL3 >>= 16;
64564 tempR0 >>= 16;
64565 tempR1 >>= 16;
64566 tempR2 >>= 16;
64567 tempR3 >>= 16;
64568 pOutputSamples[i*8+0] = (drflac_int16)tempL0;
64569 pOutputSamples[i*8+1] = (drflac_int16)tempR0;
64570 pOutputSamples[i*8+2] = (drflac_int16)tempL1;
64571 pOutputSamples[i*8+3] = (drflac_int16)tempR1;
64572 pOutputSamples[i*8+4] = (drflac_int16)tempL2;
64573 pOutputSamples[i*8+5] = (drflac_int16)tempR2;
64574 pOutputSamples[i*8+6] = (drflac_int16)tempL3;
64575 pOutputSamples[i*8+7] = (drflac_int16)tempR3;
64576 }
64577 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64578 pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16);
64579 pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16);
64580 }
64581}
64582#if defined(DRFLAC_SUPPORT_SSE2)
64583static 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)
64584{
64585 drflac_uint64 i;
64586 drflac_uint64 frameCount4 = frameCount >> 2;
64587 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64588 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64589 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64590 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64591 for (i = 0; i < frameCount4; ++i) {
64592 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
64593 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
64594 left = _mm_srai_epi32(left, 16);
64595 right = _mm_srai_epi32(right, 16);
64596 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
64597 }
64598 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64599 pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16);
64600 pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16);
64601 }
64602}
64603#endif
64604#if defined(DRFLAC_SUPPORT_NEON)
64605static 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)
64606{
64607 drflac_uint64 i;
64608 drflac_uint64 frameCount4 = frameCount >> 2;
64609 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64610 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64611 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64612 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64613 int32x4_t shift0_4 = vdupq_n_s32(shift0);
64614 int32x4_t shift1_4 = vdupq_n_s32(shift1);
64615 for (i = 0; i < frameCount4; ++i) {
64616 int32x4_t left;
64617 int32x4_t right;
64618 left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));
64619 right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));
64620 left = vshrq_n_s32(left, 16);
64621 right = vshrq_n_s32(right, 16);
64622 drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
64623 }
64624 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64625 pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16);
64626 pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16);
64627 }
64628}
64629#endif
64630static 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)
64631{
64632#if defined(DRFLAC_SUPPORT_SSE2)
64633 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
64634 drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64635 } else
64636#elif defined(DRFLAC_SUPPORT_NEON)
64637 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
64638 drflac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64639 } else
64640#endif
64641 {
64642#if 0
64643 drflac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64644#else
64645 drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64646#endif
64647 }
64648}
64649DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut)
64650{
64651 drflac_uint64 framesRead;
64652 drflac_uint32 unusedBitsPerSample;
64653 if (pFlac == NULL || framesToRead == 0) {
64654 return 0;
64655 }
64656 if (pBufferOut == NULL) {
64657 return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
64658 }
64659 DRFLAC_ASSERT(pFlac->bitsPerSample <= 32);
64660 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
64661 framesRead = 0;
64662 while (framesToRead > 0) {
64663 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
64664 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
64665 break;
64666 }
64667 } else {
64668 unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
64669 drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
64670 drflac_uint64 frameCountThisIteration = framesToRead;
64671 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
64672 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
64673 }
64674 if (channelCount == 2) {
64675 const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
64676 const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
64677 switch (pFlac->currentFLACFrame.header.channelAssignment)
64678 {
64679 case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
64680 {
64681 drflac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
64682 } break;
64683 case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
64684 {
64685 drflac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
64686 } break;
64687 case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
64688 {
64689 drflac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
64690 } break;
64691 case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
64692 default:
64693 {
64694 drflac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
64695 } break;
64696 }
64697 } else {
64698 drflac_uint64 i;
64699 for (i = 0; i < frameCountThisIteration; ++i) {
64700 unsigned int j;
64701 for (j = 0; j < channelCount; ++j) {
64702 drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
64703 pBufferOut[(i*channelCount)+j] = (drflac_int16)(sampleS32 >> 16);
64704 }
64705 }
64706 }
64707 framesRead += frameCountThisIteration;
64708 pBufferOut += frameCountThisIteration * channelCount;
64709 framesToRead -= frameCountThisIteration;
64710 pFlac->currentPCMFrame += frameCountThisIteration;
64711 pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration;
64712 }
64713 }
64714 return framesRead;
64715}
64716#if 0
64717static 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)
64718{
64719 drflac_uint64 i;
64720 for (i = 0; i < frameCount; ++i) {
64721 drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
64722 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
64723 drflac_uint32 right = left - side;
64724 pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0);
64725 pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0);
64726 }
64727}
64728#endif
64729static 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)
64730{
64731 drflac_uint64 i;
64732 drflac_uint64 frameCount4 = frameCount >> 2;
64733 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64734 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64735 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64736 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64737 float factor = 1 / 2147483648.0;
64738 for (i = 0; i < frameCount4; ++i) {
64739 drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
64740 drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
64741 drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
64742 drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
64743 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
64744 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
64745 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
64746 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
64747 drflac_uint32 right0 = left0 - side0;
64748 drflac_uint32 right1 = left1 - side1;
64749 drflac_uint32 right2 = left2 - side2;
64750 drflac_uint32 right3 = left3 - side3;
64751 pOutputSamples[i*8+0] = (drflac_int32)left0 * factor;
64752 pOutputSamples[i*8+1] = (drflac_int32)right0 * factor;
64753 pOutputSamples[i*8+2] = (drflac_int32)left1 * factor;
64754 pOutputSamples[i*8+3] = (drflac_int32)right1 * factor;
64755 pOutputSamples[i*8+4] = (drflac_int32)left2 * factor;
64756 pOutputSamples[i*8+5] = (drflac_int32)right2 * factor;
64757 pOutputSamples[i*8+6] = (drflac_int32)left3 * factor;
64758 pOutputSamples[i*8+7] = (drflac_int32)right3 * factor;
64759 }
64760 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64761 drflac_uint32 left = pInputSamples0U32[i] << shift0;
64762 drflac_uint32 side = pInputSamples1U32[i] << shift1;
64763 drflac_uint32 right = left - side;
64764 pOutputSamples[i*2+0] = (drflac_int32)left * factor;
64765 pOutputSamples[i*2+1] = (drflac_int32)right * factor;
64766 }
64767}
64768#if defined(DRFLAC_SUPPORT_SSE2)
64769static 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)
64770{
64771 drflac_uint64 i;
64772 drflac_uint64 frameCount4 = frameCount >> 2;
64773 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64774 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64775 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
64776 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
64777 __m128 factor;
64778 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
64779 factor = _mm_set1_ps(1.0f / 8388608.0f);
64780 for (i = 0; i < frameCount4; ++i) {
64781 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
64782 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
64783 __m128i right = _mm_sub_epi32(left, side);
64784 __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
64785 __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
64786 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
64787 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
64788 }
64789 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64790 drflac_uint32 left = pInputSamples0U32[i] << shift0;
64791 drflac_uint32 side = pInputSamples1U32[i] << shift1;
64792 drflac_uint32 right = left - side;
64793 pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f;
64794 pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f;
64795 }
64796}
64797#endif
64798#if defined(DRFLAC_SUPPORT_NEON)
64799static 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)
64800{
64801 drflac_uint64 i;
64802 drflac_uint64 frameCount4 = frameCount >> 2;
64803 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64804 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64805 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
64806 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
64807 float32x4_t factor4;
64808 int32x4_t shift0_4;
64809 int32x4_t shift1_4;
64810 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
64811 factor4 = vdupq_n_f32(1.0f / 8388608.0f);
64812 shift0_4 = vdupq_n_s32(shift0);
64813 shift1_4 = vdupq_n_s32(shift1);
64814 for (i = 0; i < frameCount4; ++i) {
64815 uint32x4_t left;
64816 uint32x4_t side;
64817 uint32x4_t right;
64818 float32x4_t leftf;
64819 float32x4_t rightf;
64820 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
64821 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
64822 right = vsubq_u32(left, side);
64823 leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4);
64824 rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4);
64825 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
64826 }
64827 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64828 drflac_uint32 left = pInputSamples0U32[i] << shift0;
64829 drflac_uint32 side = pInputSamples1U32[i] << shift1;
64830 drflac_uint32 right = left - side;
64831 pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f;
64832 pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f;
64833 }
64834}
64835#endif
64836static 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)
64837{
64838#if defined(DRFLAC_SUPPORT_SSE2)
64839 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
64840 drflac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64841 } else
64842#elif defined(DRFLAC_SUPPORT_NEON)
64843 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
64844 drflac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64845 } else
64846#endif
64847 {
64848#if 0
64849 drflac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64850#else
64851 drflac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64852#endif
64853 }
64854}
64855#if 0
64856static 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)
64857{
64858 drflac_uint64 i;
64859 for (i = 0; i < frameCount; ++i) {
64860 drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
64861 drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
64862 drflac_uint32 left = right + side;
64863 pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0);
64864 pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0);
64865 }
64866}
64867#endif
64868static 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)
64869{
64870 drflac_uint64 i;
64871 drflac_uint64 frameCount4 = frameCount >> 2;
64872 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64873 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64874 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64875 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
64876 float factor = 1 / 2147483648.0;
64877 for (i = 0; i < frameCount4; ++i) {
64878 drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
64879 drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
64880 drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
64881 drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
64882 drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
64883 drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
64884 drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
64885 drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
64886 drflac_uint32 left0 = right0 + side0;
64887 drflac_uint32 left1 = right1 + side1;
64888 drflac_uint32 left2 = right2 + side2;
64889 drflac_uint32 left3 = right3 + side3;
64890 pOutputSamples[i*8+0] = (drflac_int32)left0 * factor;
64891 pOutputSamples[i*8+1] = (drflac_int32)right0 * factor;
64892 pOutputSamples[i*8+2] = (drflac_int32)left1 * factor;
64893 pOutputSamples[i*8+3] = (drflac_int32)right1 * factor;
64894 pOutputSamples[i*8+4] = (drflac_int32)left2 * factor;
64895 pOutputSamples[i*8+5] = (drflac_int32)right2 * factor;
64896 pOutputSamples[i*8+6] = (drflac_int32)left3 * factor;
64897 pOutputSamples[i*8+7] = (drflac_int32)right3 * factor;
64898 }
64899 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64900 drflac_uint32 side = pInputSamples0U32[i] << shift0;
64901 drflac_uint32 right = pInputSamples1U32[i] << shift1;
64902 drflac_uint32 left = right + side;
64903 pOutputSamples[i*2+0] = (drflac_int32)left * factor;
64904 pOutputSamples[i*2+1] = (drflac_int32)right * factor;
64905 }
64906}
64907#if defined(DRFLAC_SUPPORT_SSE2)
64908static 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)
64909{
64910 drflac_uint64 i;
64911 drflac_uint64 frameCount4 = frameCount >> 2;
64912 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64913 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64914 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
64915 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
64916 __m128 factor;
64917 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
64918 factor = _mm_set1_ps(1.0f / 8388608.0f);
64919 for (i = 0; i < frameCount4; ++i) {
64920 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
64921 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
64922 __m128i left = _mm_add_epi32(right, side);
64923 __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
64924 __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
64925 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
64926 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
64927 }
64928 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64929 drflac_uint32 side = pInputSamples0U32[i] << shift0;
64930 drflac_uint32 right = pInputSamples1U32[i] << shift1;
64931 drflac_uint32 left = right + side;
64932 pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f;
64933 pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f;
64934 }
64935}
64936#endif
64937#if defined(DRFLAC_SUPPORT_NEON)
64938static 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)
64939{
64940 drflac_uint64 i;
64941 drflac_uint64 frameCount4 = frameCount >> 2;
64942 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
64943 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
64944 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
64945 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
64946 float32x4_t factor4;
64947 int32x4_t shift0_4;
64948 int32x4_t shift1_4;
64949 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
64950 factor4 = vdupq_n_f32(1.0f / 8388608.0f);
64951 shift0_4 = vdupq_n_s32(shift0);
64952 shift1_4 = vdupq_n_s32(shift1);
64953 for (i = 0; i < frameCount4; ++i) {
64954 uint32x4_t side;
64955 uint32x4_t right;
64956 uint32x4_t left;
64957 float32x4_t leftf;
64958 float32x4_t rightf;
64959 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
64960 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
64961 left = vaddq_u32(right, side);
64962 leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4);
64963 rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4);
64964 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
64965 }
64966 for (i = (frameCount4 << 2); i < frameCount; ++i) {
64967 drflac_uint32 side = pInputSamples0U32[i] << shift0;
64968 drflac_uint32 right = pInputSamples1U32[i] << shift1;
64969 drflac_uint32 left = right + side;
64970 pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f;
64971 pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f;
64972 }
64973}
64974#endif
64975static 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)
64976{
64977#if defined(DRFLAC_SUPPORT_SSE2)
64978 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
64979 drflac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64980 } else
64981#elif defined(DRFLAC_SUPPORT_NEON)
64982 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
64983 drflac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64984 } else
64985#endif
64986 {
64987#if 0
64988 drflac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64989#else
64990 drflac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
64991#endif
64992 }
64993}
64994#if 0
64995static 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)
64996{
64997 for (drflac_uint64 i = 0; i < frameCount; ++i) {
64998 drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
64999 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
65000 mid = (mid << 1) | (side & 0x01);
65001 pOutputSamples[i*2+0] = (float)((((drflac_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
65002 pOutputSamples[i*2+1] = (float)((((drflac_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
65003 }
65004}
65005#endif
65006static 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)
65007{
65008 drflac_uint64 i;
65009 drflac_uint64 frameCount4 = frameCount >> 2;
65010 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
65011 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
65012 drflac_uint32 shift = unusedBitsPerSample;
65013 float factor = 1 / 2147483648.0;
65014 if (shift > 0) {
65015 shift -= 1;
65016 for (i = 0; i < frameCount4; ++i) {
65017 drflac_uint32 temp0L;
65018 drflac_uint32 temp1L;
65019 drflac_uint32 temp2L;
65020 drflac_uint32 temp3L;
65021 drflac_uint32 temp0R;
65022 drflac_uint32 temp1R;
65023 drflac_uint32 temp2R;
65024 drflac_uint32 temp3R;
65025 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
65026 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
65027 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
65028 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
65029 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
65030 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
65031 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
65032 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
65033 mid0 = (mid0 << 1) | (side0 & 0x01);
65034 mid1 = (mid1 << 1) | (side1 & 0x01);
65035 mid2 = (mid2 << 1) | (side2 & 0x01);
65036 mid3 = (mid3 << 1) | (side3 & 0x01);
65037 temp0L = (mid0 + side0) << shift;
65038 temp1L = (mid1 + side1) << shift;
65039 temp2L = (mid2 + side2) << shift;
65040 temp3L = (mid3 + side3) << shift;
65041 temp0R = (mid0 - side0) << shift;
65042 temp1R = (mid1 - side1) << shift;
65043 temp2R = (mid2 - side2) << shift;
65044 temp3R = (mid3 - side3) << shift;
65045 pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor;
65046 pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor;
65047 pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor;
65048 pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor;
65049 pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor;
65050 pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor;
65051 pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor;
65052 pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor;
65053 }
65054 } else {
65055 for (i = 0; i < frameCount4; ++i) {
65056 drflac_uint32 temp0L;
65057 drflac_uint32 temp1L;
65058 drflac_uint32 temp2L;
65059 drflac_uint32 temp3L;
65060 drflac_uint32 temp0R;
65061 drflac_uint32 temp1R;
65062 drflac_uint32 temp2R;
65063 drflac_uint32 temp3R;
65064 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
65065 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
65066 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
65067 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
65068 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
65069 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
65070 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
65071 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
65072 mid0 = (mid0 << 1) | (side0 & 0x01);
65073 mid1 = (mid1 << 1) | (side1 & 0x01);
65074 mid2 = (mid2 << 1) | (side2 & 0x01);
65075 mid3 = (mid3 << 1) | (side3 & 0x01);
65076 temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1);
65077 temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1);
65078 temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1);
65079 temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1);
65080 temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1);
65081 temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1);
65082 temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1);
65083 temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1);
65084 pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor;
65085 pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor;
65086 pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor;
65087 pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor;
65088 pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor;
65089 pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor;
65090 pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor;
65091 pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor;
65092 }
65093 }
65094 for (i = (frameCount4 << 2); i < frameCount; ++i) {
65095 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
65096 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
65097 mid = (mid << 1) | (side & 0x01);
65098 pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor;
65099 pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor;
65100 }
65101}
65102#if defined(DRFLAC_SUPPORT_SSE2)
65103static 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)
65104{
65105 drflac_uint64 i;
65106 drflac_uint64 frameCount4 = frameCount >> 2;
65107 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
65108 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
65109 drflac_uint32 shift = unusedBitsPerSample - 8;
65110 float factor;
65111 __m128 factor128;
65112 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
65113 factor = 1.0f / 8388608.0f;
65114 factor128 = _mm_set1_ps(factor);
65115 if (shift == 0) {
65116 for (i = 0; i < frameCount4; ++i) {
65117 __m128i mid;
65118 __m128i side;
65119 __m128i tempL;
65120 __m128i tempR;
65121 __m128 leftf;
65122 __m128 rightf;
65123 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
65124 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
65125 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
65126 tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
65127 tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
65128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
65129 rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
65130 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
65131 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
65132 }
65133 for (i = (frameCount4 << 2); i < frameCount; ++i) {
65134 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
65135 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
65136 mid = (mid << 1) | (side & 0x01);
65137 pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor;
65138 pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor;
65139 }
65140 } else {
65141 shift -= 1;
65142 for (i = 0; i < frameCount4; ++i) {
65143 __m128i mid;
65144 __m128i side;
65145 __m128i tempL;
65146 __m128i tempR;
65147 __m128 leftf;
65148 __m128 rightf;
65149 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
65150 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
65151 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
65152 tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
65153 tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
65154 leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
65155 rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
65156 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
65157 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
65158 }
65159 for (i = (frameCount4 << 2); i < frameCount; ++i) {
65160 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
65161 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
65162 mid = (mid << 1) | (side & 0x01);
65163 pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor;
65164 pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor;
65165 }
65166 }
65167}
65168#endif
65169#if defined(DRFLAC_SUPPORT_NEON)
65170static 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)
65171{
65172 drflac_uint64 i;
65173 drflac_uint64 frameCount4 = frameCount >> 2;
65174 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
65175 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
65176 drflac_uint32 shift = unusedBitsPerSample - 8;
65177 float factor;
65178 float32x4_t factor4;
65179 int32x4_t shift4;
65180 int32x4_t wbps0_4;
65181 int32x4_t wbps1_4;
65182 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
65183 factor = 1.0f / 8388608.0f;
65184 factor4 = vdupq_n_f32(factor);
65185 wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
65186 wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
65187 if (shift == 0) {
65188 for (i = 0; i < frameCount4; ++i) {
65189 int32x4_t lefti;
65190 int32x4_t righti;
65191 float32x4_t leftf;
65192 float32x4_t rightf;
65193 uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4);
65194 uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4);
65195 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
65196 lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
65197 righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
65198 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
65199 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
65200 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
65201 }
65202 for (i = (frameCount4 << 2); i < frameCount; ++i) {
65203 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
65204 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
65205 mid = (mid << 1) | (side & 0x01);
65206 pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor;
65207 pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor;
65208 }
65209 } else {
65210 shift -= 1;
65211 shift4 = vdupq_n_s32(shift);
65212 for (i = 0; i < frameCount4; ++i) {
65213 uint32x4_t mid;
65214 uint32x4_t side;
65215 int32x4_t lefti;
65216 int32x4_t righti;
65217 float32x4_t leftf;
65218 float32x4_t rightf;
65219 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4);
65220 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4);
65221 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
65222 lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
65223 righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
65224 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
65225 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
65226 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
65227 }
65228 for (i = (frameCount4 << 2); i < frameCount; ++i) {
65229 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
65230 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
65231 mid = (mid << 1) | (side & 0x01);
65232 pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor;
65233 pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor;
65234 }
65235 }
65236}
65237#endif
65238static 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)
65239{
65240#if defined(DRFLAC_SUPPORT_SSE2)
65241 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
65242 drflac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
65243 } else
65244#elif defined(DRFLAC_SUPPORT_NEON)
65245 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
65246 drflac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
65247 } else
65248#endif
65249 {
65250#if 0
65251 drflac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
65252#else
65253 drflac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
65254#endif
65255 }
65256}
65257#if 0
65258static 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)
65259{
65260 for (drflac_uint64 i = 0; i < frameCount; ++i) {
65261 pOutputSamples[i*2+0] = (float)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0);
65262 pOutputSamples[i*2+1] = (float)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0);
65263 }
65264}
65265#endif
65266static 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)
65267{
65268 drflac_uint64 i;
65269 drflac_uint64 frameCount4 = frameCount >> 2;
65270 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
65271 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
65272 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
65273 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
65274 float factor = 1 / 2147483648.0;
65275 for (i = 0; i < frameCount4; ++i) {
65276 drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
65277 drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
65278 drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
65279 drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
65280 drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
65281 drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
65282 drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
65283 drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
65284 pOutputSamples[i*8+0] = (drflac_int32)tempL0 * factor;
65285 pOutputSamples[i*8+1] = (drflac_int32)tempR0 * factor;
65286 pOutputSamples[i*8+2] = (drflac_int32)tempL1 * factor;
65287 pOutputSamples[i*8+3] = (drflac_int32)tempR1 * factor;
65288 pOutputSamples[i*8+4] = (drflac_int32)tempL2 * factor;
65289 pOutputSamples[i*8+5] = (drflac_int32)tempR2 * factor;
65290 pOutputSamples[i*8+6] = (drflac_int32)tempL3 * factor;
65291 pOutputSamples[i*8+7] = (drflac_int32)tempR3 * factor;
65292 }
65293 for (i = (frameCount4 << 2); i < frameCount; ++i) {
65294 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor;
65295 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor;
65296 }
65297}
65298#if defined(DRFLAC_SUPPORT_SSE2)
65299static 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)
65300{
65301 drflac_uint64 i;
65302 drflac_uint64 frameCount4 = frameCount >> 2;
65303 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
65304 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
65305 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
65306 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
65307 float factor = 1.0f / 8388608.0f;
65308 __m128 factor128 = _mm_set1_ps(factor);
65309 for (i = 0; i < frameCount4; ++i) {
65310 __m128i lefti;
65311 __m128i righti;
65312 __m128 leftf;
65313 __m128 rightf;
65314 lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
65315 righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
65316 leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128);
65317 rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128);
65318 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
65319 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
65320 }
65321 for (i = (frameCount4 << 2); i < frameCount; ++i) {
65322 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor;
65323 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor;
65324 }
65325}
65326#endif
65327#if defined(DRFLAC_SUPPORT_NEON)
65328static 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)
65329{
65330 drflac_uint64 i;
65331 drflac_uint64 frameCount4 = frameCount >> 2;
65332 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
65333 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
65334 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
65335 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
65336 float factor = 1.0f / 8388608.0f;
65337 float32x4_t factor4 = vdupq_n_f32(factor);
65338 int32x4_t shift0_4 = vdupq_n_s32(shift0);
65339 int32x4_t shift1_4 = vdupq_n_s32(shift1);
65340 for (i = 0; i < frameCount4; ++i) {
65341 int32x4_t lefti;
65342 int32x4_t righti;
65343 float32x4_t leftf;
65344 float32x4_t rightf;
65345 lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));
65346 righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));
65347 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
65348 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
65349 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
65350 }
65351 for (i = (frameCount4 << 2); i < frameCount; ++i) {
65352 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor;
65353 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor;
65354 }
65355}
65356#endif
65357static 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)
65358{
65359#if defined(DRFLAC_SUPPORT_SSE2)
65360 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
65361 drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
65362 } else
65363#elif defined(DRFLAC_SUPPORT_NEON)
65364 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
65365 drflac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
65366 } else
65367#endif
65368 {
65369#if 0
65370 drflac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
65371#else
65372 drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
65373#endif
65374 }
65375}
65376DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut)
65377{
65378 drflac_uint64 framesRead;
65379 drflac_uint32 unusedBitsPerSample;
65380 if (pFlac == NULL || framesToRead == 0) {
65381 return 0;
65382 }
65383 if (pBufferOut == NULL) {
65384 return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
65385 }
65386 DRFLAC_ASSERT(pFlac->bitsPerSample <= 32);
65387 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
65388 framesRead = 0;
65389 while (framesToRead > 0) {
65390 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
65391 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
65392 break;
65393 }
65394 } else {
65395 unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
65396 drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
65397 drflac_uint64 frameCountThisIteration = framesToRead;
65398 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
65399 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
65400 }
65401 if (channelCount == 2) {
65402 const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
65403 const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
65404 switch (pFlac->currentFLACFrame.header.channelAssignment)
65405 {
65406 case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
65407 {
65408 drflac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
65409 } break;
65410 case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
65411 {
65412 drflac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
65413 } break;
65414 case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
65415 {
65416 drflac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
65417 } break;
65418 case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
65419 default:
65420 {
65421 drflac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
65422 } break;
65423 }
65424 } else {
65425 drflac_uint64 i;
65426 for (i = 0; i < frameCountThisIteration; ++i) {
65427 unsigned int j;
65428 for (j = 0; j < channelCount; ++j) {
65429 drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
65430 pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0);
65431 }
65432 }
65433 }
65434 framesRead += frameCountThisIteration;
65435 pBufferOut += frameCountThisIteration * channelCount;
65436 framesToRead -= frameCountThisIteration;
65437 pFlac->currentPCMFrame += frameCountThisIteration;
65438 pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration;
65439 }
65440 }
65441 return framesRead;
65442}
65443DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex)
65444{
65445 if (pFlac == NULL) {
65446 return DRFLAC_FALSE;
65447 }
65448 if (pFlac->currentPCMFrame == pcmFrameIndex) {
65449 return DRFLAC_TRUE;
65450 }
65451 if (pFlac->firstFLACFramePosInBytes == 0) {
65452 return DRFLAC_FALSE;
65453 }
65454 if (pcmFrameIndex == 0) {
65455 pFlac->currentPCMFrame = 0;
65456 return drflac__seek_to_first_frame(pFlac);
65457 } else {
65458 drflac_bool32 wasSuccessful = DRFLAC_FALSE;
65459 drflac_uint64 originalPCMFrame = pFlac->currentPCMFrame;
65460 if (pcmFrameIndex > pFlac->totalPCMFrameCount) {
65461 pcmFrameIndex = pFlac->totalPCMFrameCount;
65462 }
65463 if (pcmFrameIndex > pFlac->currentPCMFrame) {
65464 drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex - pFlac->currentPCMFrame);
65465 if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) {
65466 pFlac->currentFLACFrame.pcmFramesRemaining -= offset;
65467 pFlac->currentPCMFrame = pcmFrameIndex;
65468 return DRFLAC_TRUE;
65469 }
65470 } else {
65471 drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentPCMFrame - pcmFrameIndex);
65472 drflac_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
65473 drflac_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining;
65474 if (currentFLACFramePCMFramesConsumed > offsetAbs) {
65475 pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs;
65476 pFlac->currentPCMFrame = pcmFrameIndex;
65477 return DRFLAC_TRUE;
65478 }
65479 }
65480#ifndef DR_FLAC_NO_OGG
65481 if (pFlac->container == drflac_container_ogg)
65482 {
65483 wasSuccessful = drflac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex);
65484 }
65485 else
65486#endif
65487 {
65488 if (!pFlac->_noSeekTableSeek) {
65489 wasSuccessful = drflac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex);
65490 }
65491#if !defined(DR_FLAC_NO_CRC)
65492 if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) {
65493 wasSuccessful = drflac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex);
65494 }
65495#endif
65496 if (!wasSuccessful && !pFlac->_noBruteForceSeek) {
65497 wasSuccessful = drflac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex);
65498 }
65499 }
65500 if (wasSuccessful) {
65501 pFlac->currentPCMFrame = pcmFrameIndex;
65502 } else {
65503 if (drflac_seek_to_pcm_frame(pFlac, originalPCMFrame) == DRFLAC_FALSE) {
65504 drflac_seek_to_pcm_frame(pFlac, 0);
65505 }
65506 }
65507 return wasSuccessful;
65508 }
65509}
65510#if defined(SIZE_MAX)
65511 #define DRFLAC_SIZE_MAX SIZE_MAX
65512#else
65513 #if defined(DRFLAC_64BIT)
65514 #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF)
65515 #else
65516 #define DRFLAC_SIZE_MAX 0xFFFFFFFF
65517 #endif
65518#endif
65519#define DRFLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \
65520static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)\
65521{ \
65522 type* pSampleData = NULL; \
65523 drflac_uint64 totalPCMFrameCount; \
65524 \
65525 DRFLAC_ASSERT(pFlac != NULL); \
65526 \
65527 totalPCMFrameCount = pFlac->totalPCMFrameCount; \
65528 \
65529 if (totalPCMFrameCount == 0) { \
65530 type buffer[4096]; \
65531 drflac_uint64 pcmFramesRead; \
65532 size_t sampleDataBufferSize = sizeof(buffer); \
65533 \
65534 pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \
65535 if (pSampleData == NULL) { \
65536 goto on_error; \
65537 } \
65538 \
65539 while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \
65540 if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \
65541 type* pNewSampleData; \
65542 size_t newSampleDataBufferSize; \
65543 \
65544 newSampleDataBufferSize = sampleDataBufferSize * 2; \
65545 pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \
65546 if (pNewSampleData == NULL) { \
65547 drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \
65548 goto on_error; \
65549 } \
65550 \
65551 sampleDataBufferSize = newSampleDataBufferSize; \
65552 pSampleData = pNewSampleData; \
65553 } \
65554 \
65555 DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \
65556 totalPCMFrameCount += pcmFramesRead; \
65557 } \
65558 \
65559 \
65560 DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \
65561 } else { \
65562 drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \
65563 if (dataSize > DRFLAC_SIZE_MAX) { \
65564 goto on_error; \
65565 } \
65566 \
65567 pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \
65568 if (pSampleData == NULL) { \
65569 goto on_error; \
65570 } \
65571 \
65572 totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \
65573 } \
65574 \
65575 if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \
65576 if (channelsOut) *channelsOut = pFlac->channels; \
65577 if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \
65578 \
65579 drflac_close(pFlac); \
65580 return pSampleData; \
65581 \
65582on_error: \
65583 drflac_close(pFlac); \
65584 return NULL; \
65585}
65586DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32)
65587DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16)
65588DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float)
65589DRFLAC_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)
65590{
65591 drflac* pFlac;
65592 if (channelsOut) {
65593 *channelsOut = 0;
65594 }
65595 if (sampleRateOut) {
65596 *sampleRateOut = 0;
65597 }
65598 if (totalPCMFrameCountOut) {
65599 *totalPCMFrameCountOut = 0;
65600 }
65601 pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
65602 if (pFlac == NULL) {
65603 return NULL;
65604 }
65605 return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
65606}
65607DRFLAC_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)
65608{
65609 drflac* pFlac;
65610 if (channelsOut) {
65611 *channelsOut = 0;
65612 }
65613 if (sampleRateOut) {
65614 *sampleRateOut = 0;
65615 }
65616 if (totalPCMFrameCountOut) {
65617 *totalPCMFrameCountOut = 0;
65618 }
65619 pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
65620 if (pFlac == NULL) {
65621 return NULL;
65622 }
65623 return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
65624}
65625DRFLAC_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)
65626{
65627 drflac* pFlac;
65628 if (channelsOut) {
65629 *channelsOut = 0;
65630 }
65631 if (sampleRateOut) {
65632 *sampleRateOut = 0;
65633 }
65634 if (totalPCMFrameCountOut) {
65635 *totalPCMFrameCountOut = 0;
65636 }
65637 pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
65638 if (pFlac == NULL) {
65639 return NULL;
65640 }
65641 return drflac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
65642}
65643#ifndef DR_FLAC_NO_STDIO
65644DRFLAC_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)
65645{
65646 drflac* pFlac;
65647 if (sampleRate) {
65648 *sampleRate = 0;
65649 }
65650 if (channels) {
65651 *channels = 0;
65652 }
65653 if (totalPCMFrameCount) {
65654 *totalPCMFrameCount = 0;
65655 }
65656 pFlac = drflac_open_file(filename, pAllocationCallbacks);
65657 if (pFlac == NULL) {
65658 return NULL;
65659 }
65660 return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
65661}
65662DRFLAC_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)
65663{
65664 drflac* pFlac;
65665 if (sampleRate) {
65666 *sampleRate = 0;
65667 }
65668 if (channels) {
65669 *channels = 0;
65670 }
65671 if (totalPCMFrameCount) {
65672 *totalPCMFrameCount = 0;
65673 }
65674 pFlac = drflac_open_file(filename, pAllocationCallbacks);
65675 if (pFlac == NULL) {
65676 return NULL;
65677 }
65678 return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
65679}
65680DRFLAC_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)
65681{
65682 drflac* pFlac;
65683 if (sampleRate) {
65684 *sampleRate = 0;
65685 }
65686 if (channels) {
65687 *channels = 0;
65688 }
65689 if (totalPCMFrameCount) {
65690 *totalPCMFrameCount = 0;
65691 }
65692 pFlac = drflac_open_file(filename, pAllocationCallbacks);
65693 if (pFlac == NULL) {
65694 return NULL;
65695 }
65696 return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
65697}
65698#endif
65699DRFLAC_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)
65700{
65701 drflac* pFlac;
65702 if (sampleRate) {
65703 *sampleRate = 0;
65704 }
65705 if (channels) {
65706 *channels = 0;
65707 }
65708 if (totalPCMFrameCount) {
65709 *totalPCMFrameCount = 0;
65710 }
65711 pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks);
65712 if (pFlac == NULL) {
65713 return NULL;
65714 }
65715 return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
65716}
65717DRFLAC_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)
65718{
65719 drflac* pFlac;
65720 if (sampleRate) {
65721 *sampleRate = 0;
65722 }
65723 if (channels) {
65724 *channels = 0;
65725 }
65726 if (totalPCMFrameCount) {
65727 *totalPCMFrameCount = 0;
65728 }
65729 pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks);
65730 if (pFlac == NULL) {
65731 return NULL;
65732 }
65733 return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
65734}
65735DRFLAC_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)
65736{
65737 drflac* pFlac;
65738 if (sampleRate) {
65739 *sampleRate = 0;
65740 }
65741 if (channels) {
65742 *channels = 0;
65743 }
65744 if (totalPCMFrameCount) {
65745 *totalPCMFrameCount = 0;
65746 }
65747 pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks);
65748 if (pFlac == NULL) {
65749 return NULL;
65750 }
65751 return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
65752}
65753DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks)
65754{
65755 if (pAllocationCallbacks != NULL) {
65756 drflac__free_from_callbacks(p, pAllocationCallbacks);
65757 } else {
65758 drflac__free_default(p, NULL);
65759 }
65760}
65761DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments)
65762{
65763 if (pIter == NULL) {
65764 return;
65765 }
65766 pIter->countRemaining = commentCount;
65767 pIter->pRunningData = (const char*)pComments;
65768}
65769DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut)
65770{
65771 drflac_int32 length;
65772 const char* pComment;
65773 if (pCommentLengthOut) {
65774 *pCommentLengthOut = 0;
65775 }
65776 if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
65777 return NULL;
65778 }
65779 length = drflac__le2host_32(*(const drflac_uint32*)pIter->pRunningData);
65780 pIter->pRunningData += 4;
65781 pComment = pIter->pRunningData;
65782 pIter->pRunningData += length;
65783 pIter->countRemaining -= 1;
65784 if (pCommentLengthOut) {
65785 *pCommentLengthOut = length;
65786 }
65787 return pComment;
65788}
65789DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData)
65790{
65791 if (pIter == NULL) {
65792 return;
65793 }
65794 pIter->countRemaining = trackCount;
65795 pIter->pRunningData = (const char*)pTrackData;
65796}
65797DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack)
65798{
65799 drflac_cuesheet_track cuesheetTrack;
65800 const char* pRunningData;
65801 drflac_uint64 offsetHi;
65802 drflac_uint64 offsetLo;
65803 if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
65804 return DRFLAC_FALSE;
65805 }
65806 pRunningData = pIter->pRunningData;
65807 offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
65808 offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
65809 cuesheetTrack.offset = offsetLo | (offsetHi << 32);
65810 cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1;
65811 DRFLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12;
65812 cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0;
65813 cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14;
65814 cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1;
65815 cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index);
65816 pIter->pRunningData = pRunningData;
65817 pIter->countRemaining -= 1;
65818 if (pCuesheetTrack) {
65819 *pCuesheetTrack = cuesheetTrack;
65820 }
65821 return DRFLAC_TRUE;
65822}
65823#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
65824 #pragma GCC diagnostic pop
65825#endif
65826#endif
65827/* dr_flac_c end */
65828#endif /* DRFLAC_IMPLEMENTATION */
65829#endif /* MA_NO_FLAC */
65830
65831#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
65832#if !defined(DR_MP3_IMPLEMENTATION) && !defined(DRMP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
65833/* dr_mp3_c begin */
65834#ifndef dr_mp3_c
65835#define dr_mp3_c
65836#include <stdlib.h>
65837#include <string.h>
65838#include <limits.h>
65839DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision)
65840{
65841 if (pMajor) {
65842 *pMajor = DRMP3_VERSION_MAJOR;
65843 }
65844 if (pMinor) {
65845 *pMinor = DRMP3_VERSION_MINOR;
65846 }
65847 if (pRevision) {
65848 *pRevision = DRMP3_VERSION_REVISION;
65849 }
65850}
65851DRMP3_API const char* drmp3_version_string(void)
65852{
65853 return DRMP3_VERSION_STRING;
65854}
65855#if defined(__TINYC__)
65856#define DR_MP3_NO_SIMD
65857#endif
65858#define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset)))
65859#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304
65860#ifndef DRMP3_MAX_FRAME_SYNC_MATCHES
65861#define DRMP3_MAX_FRAME_SYNC_MATCHES 10
65862#endif
65863#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE
65864#define DRMP3_MAX_BITRESERVOIR_BYTES 511
65865#define DRMP3_SHORT_BLOCK_TYPE 2
65866#define DRMP3_STOP_BLOCK_TYPE 3
65867#define DRMP3_MODE_MONO 3
65868#define DRMP3_MODE_JOINT_STEREO 1
65869#define DRMP3_HDR_SIZE 4
65870#define DRMP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0)
65871#define DRMP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60)
65872#define DRMP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0)
65873#define DRMP3_HDR_IS_CRC(h) (!((h[1]) & 1))
65874#define DRMP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2)
65875#define DRMP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8)
65876#define DRMP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10)
65877#define DRMP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10)
65878#define DRMP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20)
65879#define DRMP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3)
65880#define DRMP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3)
65881#define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3)
65882#define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4)
65883#define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3)
65884#define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3)
65885#define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2)
65886#define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6)
65887#define DRMP3_BITS_DEQUANTIZER_OUT -1
65888#define DRMP3_MAX_SCF (255 + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210)
65889#define DRMP3_MAX_SCFI ((DRMP3_MAX_SCF + 3) & ~3)
65890#define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a))
65891#define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a))
65892#if !defined(DR_MP3_NO_SIMD)
65893#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64))
65894#define DR_MP3_ONLY_SIMD
65895#endif
65896#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__))
65897#if defined(_MSC_VER)
65898#include <intrin.h>
65899#endif
65900#include <emmintrin.h>
65901#define DRMP3_HAVE_SSE 1
65902#define DRMP3_HAVE_SIMD 1
65903#define DRMP3_VSTORE _mm_storeu_ps
65904#define DRMP3_VLD _mm_loadu_ps
65905#define DRMP3_VSET _mm_set1_ps
65906#define DRMP3_VADD _mm_add_ps
65907#define DRMP3_VSUB _mm_sub_ps
65908#define DRMP3_VMUL _mm_mul_ps
65909#define DRMP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y))
65910#define DRMP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y))
65911#define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s))
65912#define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3))
65913typedef __m128 drmp3_f4;
65914#if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD)
65915#define drmp3_cpuid __cpuid
65916#else
65917static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType)
65918{
65919#if defined(__PIC__)
65920 __asm__ __volatile__(
65921#if defined(__x86_64__)
65922 "push %%rbx\n"
65923 "cpuid\n"
65924 "xchgl %%ebx, %1\n"
65925 "pop %%rbx\n"
65926#else
65927 "xchgl %%ebx, %1\n"
65928 "cpuid\n"
65929 "xchgl %%ebx, %1\n"
65930#endif
65931 : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3])
65932 : "a" (InfoType));
65933#else
65934 __asm__ __volatile__(
65935 "cpuid"
65936 : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3])
65937 : "a" (InfoType));
65938#endif
65939}
65940#endif
65941static int drmp3_have_simd(void)
65942{
65943#ifdef DR_MP3_ONLY_SIMD
65944 return 1;
65945#else
65946 static int g_have_simd;
65947 int CPUInfo[4];
65948#ifdef MINIMP3_TEST
65949 static int g_counter;
65950 if (g_counter++ > 100)
65951 return 0;
65952#endif
65953 if (g_have_simd)
65954 goto end;
65955 drmp3_cpuid(CPUInfo, 0);
65956 if (CPUInfo[0] > 0)
65957 {
65958 drmp3_cpuid(CPUInfo, 1);
65959 g_have_simd = (CPUInfo[3] & (1 << 26)) + 1;
65960 return g_have_simd - 1;
65961 }
65962end:
65963 return g_have_simd - 1;
65964#endif
65965}
65966#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)
65967#include <arm_neon.h>
65968#define DRMP3_HAVE_SSE 0
65969#define DRMP3_HAVE_SIMD 1
65970#define DRMP3_VSTORE vst1q_f32
65971#define DRMP3_VLD vld1q_f32
65972#define DRMP3_VSET vmovq_n_f32
65973#define DRMP3_VADD vaddq_f32
65974#define DRMP3_VSUB vsubq_f32
65975#define DRMP3_VMUL vmulq_f32
65976#define DRMP3_VMAC(a, x, y) vmlaq_f32(a, x, y)
65977#define DRMP3_VMSB(a, x, y) vmlsq_f32(a, x, y)
65978#define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s))
65979#define DRMP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x)))
65980typedef float32x4_t drmp3_f4;
65981static int drmp3_have_simd(void)
65982{
65983 return 1;
65984}
65985#else
65986#define DRMP3_HAVE_SSE 0
65987#define DRMP3_HAVE_SIMD 0
65988#ifdef DR_MP3_ONLY_SIMD
65989#error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled
65990#endif
65991#endif
65992#else
65993#define DRMP3_HAVE_SIMD 0
65994#endif
65995#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64)
65996#define DRMP3_HAVE_ARMV6 1
65997static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(int32_t a)
65998{
65999 drmp3_int32 x = 0;
66000 __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a));
66001 return x;
66002}
66003#else
66004#define DRMP3_HAVE_ARMV6 0
66005#endif
66006typedef struct
66007{
66008 const drmp3_uint8 *buf;
66009 int pos, limit;
66010} drmp3_bs;
66011typedef struct
66012{
66013 float scf[3*64];
66014 drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64];
66015} drmp3_L12_scale_info;
66016typedef struct
66017{
66018 drmp3_uint8 tab_offset, code_tab_width, band_count;
66019} drmp3_L12_subband_alloc;
66020typedef struct
66021{
66022 const drmp3_uint8 *sfbtab;
66023 drmp3_uint16 part_23_length, big_values, scalefac_compress;
66024 drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb;
66025 drmp3_uint8 table_select[3], region_count[3], subblock_gain[3];
66026 drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi;
66027} drmp3_L3_gr_info;
66028typedef struct
66029{
66030 drmp3_bs bs;
66031 drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES];
66032 drmp3_L3_gr_info gr_info[4];
66033 float grbuf[2][576], scf[40], syn[18 + 15][2*32];
66034 drmp3_uint8 ist_pos[2][39];
66035} drmp3dec_scratch;
66036static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes)
66037{
66038 bs->buf = data;
66039 bs->pos = 0;
66040 bs->limit = bytes*8;
66041}
66042static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n)
66043{
66044 drmp3_uint32 next, cache = 0, s = bs->pos & 7;
66045 int shl = n + s;
66046 const drmp3_uint8 *p = bs->buf + (bs->pos >> 3);
66047 if ((bs->pos += n) > bs->limit)
66048 return 0;
66049 next = *p++ & (255 >> s);
66050 while ((shl -= 8) > 0)
66051 {
66052 cache |= next << shl;
66053 next = *p++;
66054 }
66055 return cache | (next >> -shl);
66056}
66057static int drmp3_hdr_valid(const drmp3_uint8 *h)
66058{
66059 return h[0] == 0xff &&
66060 ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) &&
66061 (DRMP3_HDR_GET_LAYER(h) != 0) &&
66062 (DRMP3_HDR_GET_BITRATE(h) != 15) &&
66063 (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3);
66064}
66065static int drmp3_hdr_compare(const drmp3_uint8 *h1, const drmp3_uint8 *h2)
66066{
66067 return drmp3_hdr_valid(h2) &&
66068 ((h1[1] ^ h2[1]) & 0xFE) == 0 &&
66069 ((h1[2] ^ h2[2]) & 0x0C) == 0 &&
66070 !(DRMP3_HDR_IS_FREE_FORMAT(h1) ^ DRMP3_HDR_IS_FREE_FORMAT(h2));
66071}
66072static unsigned drmp3_hdr_bitrate_kbps(const drmp3_uint8 *h)
66073{
66074 static const drmp3_uint8 halfrate[2][3][15] = {
66075 { { 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 } },
66076 { { 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 } },
66077 };
66078 return 2*halfrate[!!DRMP3_HDR_TEST_MPEG1(h)][DRMP3_HDR_GET_LAYER(h) - 1][DRMP3_HDR_GET_BITRATE(h)];
66079}
66080static unsigned drmp3_hdr_sample_rate_hz(const drmp3_uint8 *h)
66081{
66082 static const unsigned g_hz[3] = { 44100, 48000, 32000 };
66083 return g_hz[DRMP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!DRMP3_HDR_TEST_MPEG1(h) >> (int)!DRMP3_HDR_TEST_NOT_MPEG25(h);
66084}
66085static unsigned drmp3_hdr_frame_samples(const drmp3_uint8 *h)
66086{
66087 return DRMP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)DRMP3_HDR_IS_FRAME_576(h));
66088}
66089static int drmp3_hdr_frame_bytes(const drmp3_uint8 *h, int free_format_size)
66090{
66091 int frame_bytes = drmp3_hdr_frame_samples(h)*drmp3_hdr_bitrate_kbps(h)*125/drmp3_hdr_sample_rate_hz(h);
66092 if (DRMP3_HDR_IS_LAYER_1(h))
66093 {
66094 frame_bytes &= ~3;
66095 }
66096 return frame_bytes ? frame_bytes : free_format_size;
66097}
66098static int drmp3_hdr_padding(const drmp3_uint8 *h)
66099{
66100 return DRMP3_HDR_TEST_PADDING(h) ? (DRMP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0;
66101}
66102#ifndef DR_MP3_ONLY_MP3
66103static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_uint8 *hdr, drmp3_L12_scale_info *sci)
66104{
66105 const drmp3_L12_subband_alloc *alloc;
66106 int mode = DRMP3_HDR_GET_STEREO_MODE(hdr);
66107 int nbands, stereo_bands = (mode == DRMP3_MODE_MONO) ? 0 : (mode == DRMP3_MODE_JOINT_STEREO) ? (DRMP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32;
66108 if (DRMP3_HDR_IS_LAYER_1(hdr))
66109 {
66110 static const drmp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } };
66111 alloc = g_alloc_L1;
66112 nbands = 32;
66113 } else if (!DRMP3_HDR_TEST_MPEG1(hdr))
66114 {
66115 static const drmp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } };
66116 alloc = g_alloc_L2M2;
66117 nbands = 30;
66118 } else
66119 {
66120 static const drmp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } };
66121 int sample_rate_idx = DRMP3_HDR_GET_SAMPLE_RATE(hdr);
66122 unsigned kbps = drmp3_hdr_bitrate_kbps(hdr) >> (int)(mode != DRMP3_MODE_MONO);
66123 if (!kbps)
66124 {
66125 kbps = 192;
66126 }
66127 alloc = g_alloc_L2M1;
66128 nbands = 27;
66129 if (kbps < 56)
66130 {
66131 static const drmp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } };
66132 alloc = g_alloc_L2M1_lowrate;
66133 nbands = sample_rate_idx == 2 ? 12 : 8;
66134 } else if (kbps >= 96 && sample_rate_idx != 1)
66135 {
66136 nbands = 30;
66137 }
66138 }
66139 sci->total_bands = (drmp3_uint8)nbands;
66140 sci->stereo_bands = (drmp3_uint8)DRMP3_MIN(stereo_bands, nbands);
66141 return alloc;
66142}
66143static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_uint8 *scfcod, int bands, float *scf)
66144{
66145 static const float g_deq_L12[18*3] = {
66146#define DRMP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x
66147 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)
66148 };
66149 int i, m;
66150 for (i = 0; i < bands; i++)
66151 {
66152 float s = 0;
66153 int ba = *pba++;
66154 int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0;
66155 for (m = 4; m; m >>= 1)
66156 {
66157 if (mask & m)
66158 {
66159 int b = drmp3_bs_get_bits(bs, 6);
66160 s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3);
66161 }
66162 *scf++ = s;
66163 }
66164 }
66165}
66166static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp3_L12_scale_info *sci)
66167{
66168 static const drmp3_uint8 g_bitalloc_code_tab[] = {
66169 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16,
66170 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16,
66171 0,17,18, 3,19,4,5,16,
66172 0,17,18,16,
66173 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15,
66174 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14,
66175 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16
66176 };
66177 const drmp3_L12_subband_alloc *subband_alloc = drmp3_L12_subband_alloc_table(hdr, sci);
66178 int i, k = 0, ba_bits = 0;
66179 const drmp3_uint8 *ba_code_tab = g_bitalloc_code_tab;
66180 for (i = 0; i < sci->total_bands; i++)
66181 {
66182 drmp3_uint8 ba;
66183 if (i == k)
66184 {
66185 k += subband_alloc->band_count;
66186 ba_bits = subband_alloc->code_tab_width;
66187 ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset;
66188 subband_alloc++;
66189 }
66190 ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)];
66191 sci->bitalloc[2*i] = ba;
66192 if (i < sci->stereo_bands)
66193 {
66194 ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)];
66195 }
66196 sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0;
66197 }
66198 for (i = 0; i < 2*sci->total_bands; i++)
66199 {
66200 sci->scfcod[i] = (drmp3_uint8)(sci->bitalloc[i] ? DRMP3_HDR_IS_LAYER_1(hdr) ? 2 : drmp3_bs_get_bits(bs, 2) : 6);
66201 }
66202 drmp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf);
66203 for (i = sci->stereo_bands; i < sci->total_bands; i++)
66204 {
66205 sci->bitalloc[2*i + 1] = 0;
66206 }
66207}
66208static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_scale_info *sci, int group_size)
66209{
66210 int i, j, k, choff = 576;
66211 for (j = 0; j < 4; j++)
66212 {
66213 float *dst = grbuf + group_size*j;
66214 for (i = 0; i < 2*sci->total_bands; i++)
66215 {
66216 int ba = sci->bitalloc[i];
66217 if (ba != 0)
66218 {
66219 if (ba < 17)
66220 {
66221 int half = (1 << (ba - 1)) - 1;
66222 for (k = 0; k < group_size; k++)
66223 {
66224 dst[k] = (float)((int)drmp3_bs_get_bits(bs, ba) - half);
66225 }
66226 } else
66227 {
66228 unsigned mod = (2 << (ba - 17)) + 1;
66229 unsigned code = drmp3_bs_get_bits(bs, mod + 2 - (mod >> 3));
66230 for (k = 0; k < group_size; k++, code /= mod)
66231 {
66232 dst[k] = (float)((int)(code % mod - mod/2));
66233 }
66234 }
66235 }
66236 dst += choff;
66237 choff = 18 - choff;
66238 }
66239 }
66240 return group_size*4;
66241}
66242static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, float *dst)
66243{
66244 int i, k;
66245 memcpy(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float));
66246 for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6)
66247 {
66248 for (k = 0; k < 12; k++)
66249 {
66250 dst[k + 0] *= scf[0];
66251 dst[k + 576] *= scf[3];
66252 }
66253 }
66254}
66255#endif
66256static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr)
66257{
66258 static const drmp3_uint8 g_scf_long[8][23] = {
66259 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
66260 { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 },
66261 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
66262 { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 },
66263 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
66264 { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 },
66265 { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 },
66266 { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 }
66267 };
66268 static const drmp3_uint8 g_scf_short[8][40] = {
66269 { 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 },
66270 { 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 },
66271 { 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 },
66272 { 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 },
66273 { 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 },
66274 { 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 },
66275 { 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 },
66276 { 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 }
66277 };
66278 static const drmp3_uint8 g_scf_mixed[8][40] = {
66279 { 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 },
66280 { 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 },
66281 { 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 },
66282 { 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 },
66283 { 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 },
66284 { 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 },
66285 { 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 },
66286 { 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 }
66287 };
66288 unsigned tables, scfsi = 0;
66289 int main_data_begin, part_23_sum = 0;
66290 int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2;
66291 int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0);
66292 if (DRMP3_HDR_TEST_MPEG1(hdr))
66293 {
66294 gr_count *= 2;
66295 main_data_begin = drmp3_bs_get_bits(bs, 9);
66296 scfsi = drmp3_bs_get_bits(bs, 7 + gr_count);
66297 } else
66298 {
66299 main_data_begin = drmp3_bs_get_bits(bs, 8 + gr_count) >> gr_count;
66300 }
66301 do
66302 {
66303 if (DRMP3_HDR_IS_MONO(hdr))
66304 {
66305 scfsi <<= 4;
66306 }
66307 gr->part_23_length = (drmp3_uint16)drmp3_bs_get_bits(bs, 12);
66308 part_23_sum += gr->part_23_length;
66309 gr->big_values = (drmp3_uint16)drmp3_bs_get_bits(bs, 9);
66310 if (gr->big_values > 288)
66311 {
66312 return -1;
66313 }
66314 gr->global_gain = (drmp3_uint8)drmp3_bs_get_bits(bs, 8);
66315 gr->scalefac_compress = (drmp3_uint16)drmp3_bs_get_bits(bs, DRMP3_HDR_TEST_MPEG1(hdr) ? 4 : 9);
66316 gr->sfbtab = g_scf_long[sr_idx];
66317 gr->n_long_sfb = 22;
66318 gr->n_short_sfb = 0;
66319 if (drmp3_bs_get_bits(bs, 1))
66320 {
66321 gr->block_type = (drmp3_uint8)drmp3_bs_get_bits(bs, 2);
66322 if (!gr->block_type)
66323 {
66324 return -1;
66325 }
66326 gr->mixed_block_flag = (drmp3_uint8)drmp3_bs_get_bits(bs, 1);
66327 gr->region_count[0] = 7;
66328 gr->region_count[1] = 255;
66329 if (gr->block_type == DRMP3_SHORT_BLOCK_TYPE)
66330 {
66331 scfsi &= 0x0F0F;
66332 if (!gr->mixed_block_flag)
66333 {
66334 gr->region_count[0] = 8;
66335 gr->sfbtab = g_scf_short[sr_idx];
66336 gr->n_long_sfb = 0;
66337 gr->n_short_sfb = 39;
66338 } else
66339 {
66340 gr->sfbtab = g_scf_mixed[sr_idx];
66341 gr->n_long_sfb = DRMP3_HDR_TEST_MPEG1(hdr) ? 8 : 6;
66342 gr->n_short_sfb = 30;
66343 }
66344 }
66345 tables = drmp3_bs_get_bits(bs, 10);
66346 tables <<= 5;
66347 gr->subblock_gain[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
66348 gr->subblock_gain[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
66349 gr->subblock_gain[2] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
66350 } else
66351 {
66352 gr->block_type = 0;
66353 gr->mixed_block_flag = 0;
66354 tables = drmp3_bs_get_bits(bs, 15);
66355 gr->region_count[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 4);
66356 gr->region_count[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
66357 gr->region_count[2] = 255;
66358 }
66359 gr->table_select[0] = (drmp3_uint8)(tables >> 10);
66360 gr->table_select[1] = (drmp3_uint8)((tables >> 5) & 31);
66361 gr->table_select[2] = (drmp3_uint8)((tables) & 31);
66362 gr->preflag = (drmp3_uint8)(DRMP3_HDR_TEST_MPEG1(hdr) ? drmp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500));
66363 gr->scalefac_scale = (drmp3_uint8)drmp3_bs_get_bits(bs, 1);
66364 gr->count1_table = (drmp3_uint8)drmp3_bs_get_bits(bs, 1);
66365 gr->scfsi = (drmp3_uint8)((scfsi >> 12) & 15);
66366 scfsi <<= 4;
66367 gr++;
66368 } while(--gr_count);
66369 if (part_23_sum + bs->pos > bs->limit + main_data_begin*8)
66370 {
66371 return -1;
66372 }
66373 return main_data_begin;
66374}
66375static 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)
66376{
66377 int i, k;
66378 for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2)
66379 {
66380 int cnt = scf_count[i];
66381 if (scfsi & 8)
66382 {
66383 memcpy(scf, ist_pos, cnt);
66384 } else
66385 {
66386 int bits = scf_size[i];
66387 if (!bits)
66388 {
66389 memset(scf, 0, cnt);
66390 memset(ist_pos, 0, cnt);
66391 } else
66392 {
66393 int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1;
66394 for (k = 0; k < cnt; k++)
66395 {
66396 int s = drmp3_bs_get_bits(bitbuf, bits);
66397 ist_pos[k] = (drmp3_uint8)(s == max_scf ? -1 : s);
66398 scf[k] = (drmp3_uint8)s;
66399 }
66400 }
66401 }
66402 ist_pos += cnt;
66403 scf += cnt;
66404 }
66405 scf[0] = scf[1] = scf[2] = 0;
66406}
66407static float drmp3_L3_ldexp_q2(float y, int exp_q2)
66408{
66409 static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f };
66410 int e;
66411 do
66412 {
66413 e = DRMP3_MIN(30*4, exp_q2);
66414 y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2));
66415 } while ((exp_q2 -= e) > 0);
66416 return y;
66417}
66418static 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)
66419{
66420 static const drmp3_uint8 g_scf_partitions[3][28] = {
66421 { 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 },
66422 { 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 },
66423 { 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 }
66424 };
66425 const drmp3_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb];
66426 drmp3_uint8 scf_size[4], iscf[40];
66427 int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi;
66428 float gain;
66429 if (DRMP3_HDR_TEST_MPEG1(hdr))
66430 {
66431 static const drmp3_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 };
66432 int part = g_scfc_decode[gr->scalefac_compress];
66433 scf_size[1] = scf_size[0] = (drmp3_uint8)(part >> 2);
66434 scf_size[3] = scf_size[2] = (drmp3_uint8)(part & 3);
66435 } else
66436 {
66437 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 };
66438 int k, modprod, sfc, ist = DRMP3_HDR_TEST_I_STEREO(hdr) && ch;
66439 sfc = gr->scalefac_compress >> ist;
66440 for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4)
66441 {
66442 for (modprod = 1, i = 3; i >= 0; i--)
66443 {
66444 scf_size[i] = (drmp3_uint8)(sfc / modprod % g_mod[k + i]);
66445 modprod *= g_mod[k + i];
66446 }
66447 }
66448 scf_partition += k;
66449 scfsi = -16;
66450 }
66451 drmp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi);
66452 if (gr->n_short_sfb)
66453 {
66454 int sh = 3 - scf_shift;
66455 for (i = 0; i < gr->n_short_sfb; i += 3)
66456 {
66457 iscf[gr->n_long_sfb + i + 0] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh));
66458 iscf[gr->n_long_sfb + i + 1] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh));
66459 iscf[gr->n_long_sfb + i + 2] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh));
66460 }
66461 } else if (gr->preflag)
66462 {
66463 static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 };
66464 for (i = 0; i < 10; i++)
66465 {
66466 iscf[11 + i] = (drmp3_uint8)(iscf[11 + i] + g_preamp[i]);
66467 }
66468 }
66469 gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210 - (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0);
66470 gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI/4), DRMP3_MAX_SCFI - gain_exp);
66471 for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++)
66472 {
66473 scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift);
66474 }
66475}
66476static const float g_drmp3_pow43[129 + 16] = {
66477 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,
66478 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
66479};
66480static float drmp3_L3_pow_43(int x)
66481{
66482 float frac;
66483 int sign, mult = 256;
66484 if (x < 129)
66485 {
66486 return g_drmp3_pow43[16 + x];
66487 }
66488 if (x < 1024)
66489 {
66490 mult = 16;
66491 x <<= 3;
66492 }
66493 sign = 2*x & 64;
66494 frac = (float)((x & 63) - sign) / ((x & ~63) + sign);
66495 return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult;
66496}
66497static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit)
66498{
66499 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,
66500 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,
66501 -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,
66502 -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,
66503 -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,
66504 -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,
66505 -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,
66506 -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,
66507 -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,
66508 -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,
66509 -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,
66510 -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,
66511 -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,
66512 -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,
66513 -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,
66514 -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 };
66515 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};
66516 static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 };
66517 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 };
66518 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 };
66519#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - n))
66520#define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); }
66521#define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; }
66522#define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh)
66523 float one = 0.0f;
66524 int ireg = 0, big_val_cnt = gr_info->big_values;
66525 const drmp3_uint8 *sfb = gr_info->sfbtab;
66526 const drmp3_uint8 *bs_next_ptr = bs->buf + bs->pos/8;
66527 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);
66528 int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8;
66529 bs_next_ptr += 4;
66530 while (big_val_cnt > 0)
66531 {
66532 int tab_num = gr_info->table_select[ireg];
66533 int sfb_cnt = gr_info->region_count[ireg++];
66534 const drmp3_int16 *codebook = tabs + tabindex[tab_num];
66535 int linbits = g_linbits[tab_num];
66536 if (linbits)
66537 {
66538 do
66539 {
66540 np = *sfb++ / 2;
66541 pairs_to_decode = DRMP3_MIN(big_val_cnt, np);
66542 one = *scf++;
66543 do
66544 {
66545 int j, w = 5;
66546 int leaf = codebook[DRMP3_PEEK_BITS(w)];
66547 while (leaf < 0)
66548 {
66549 DRMP3_FLUSH_BITS(w);
66550 w = leaf & 7;
66551 leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)];
66552 }
66553 DRMP3_FLUSH_BITS(leaf >> 8);
66554 for (j = 0; j < 2; j++, dst++, leaf >>= 4)
66555 {
66556 int lsb = leaf & 0x0F;
66557 if (lsb == 15)
66558 {
66559 lsb += DRMP3_PEEK_BITS(linbits);
66560 DRMP3_FLUSH_BITS(linbits);
66561 DRMP3_CHECK_BITS;
66562 *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1);
66563 } else
66564 {
66565 *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
66566 }
66567 DRMP3_FLUSH_BITS(lsb ? 1 : 0);
66568 }
66569 DRMP3_CHECK_BITS;
66570 } while (--pairs_to_decode);
66571 } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
66572 } else
66573 {
66574 do
66575 {
66576 np = *sfb++ / 2;
66577 pairs_to_decode = DRMP3_MIN(big_val_cnt, np);
66578 one = *scf++;
66579 do
66580 {
66581 int j, w = 5;
66582 int leaf = codebook[DRMP3_PEEK_BITS(w)];
66583 while (leaf < 0)
66584 {
66585 DRMP3_FLUSH_BITS(w);
66586 w = leaf & 7;
66587 leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)];
66588 }
66589 DRMP3_FLUSH_BITS(leaf >> 8);
66590 for (j = 0; j < 2; j++, dst++, leaf >>= 4)
66591 {
66592 int lsb = leaf & 0x0F;
66593 *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
66594 DRMP3_FLUSH_BITS(lsb ? 1 : 0);
66595 }
66596 DRMP3_CHECK_BITS;
66597 } while (--pairs_to_decode);
66598 } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
66599 }
66600 }
66601 for (np = 1 - big_val_cnt;; dst += 4)
66602 {
66603 const drmp3_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32;
66604 int leaf = codebook_count1[DRMP3_PEEK_BITS(4)];
66605 if (!(leaf & 8))
66606 {
66607 leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))];
66608 }
66609 DRMP3_FLUSH_BITS(leaf & 7);
66610 if (DRMP3_BSPOS > layer3gr_limit)
66611 {
66612 break;
66613 }
66614#define DRMP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; }
66615#define DRMP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((drmp3_int32)bs_cache < 0) ? -one : one; DRMP3_FLUSH_BITS(1) }
66616 DRMP3_RELOAD_SCALEFACTOR;
66617 DRMP3_DEQ_COUNT1(0);
66618 DRMP3_DEQ_COUNT1(1);
66619 DRMP3_RELOAD_SCALEFACTOR;
66620 DRMP3_DEQ_COUNT1(2);
66621 DRMP3_DEQ_COUNT1(3);
66622 DRMP3_CHECK_BITS;
66623 }
66624 bs->pos = layer3gr_limit;
66625}
66626static void drmp3_L3_midside_stereo(float *left, int n)
66627{
66628 int i = 0;
66629 float *right = left + 576;
66630#if DRMP3_HAVE_SIMD
66631 if (drmp3_have_simd()) for (; i < n - 3; i += 4)
66632 {
66633 drmp3_f4 vl = DRMP3_VLD(left + i);
66634 drmp3_f4 vr = DRMP3_VLD(right + i);
66635 DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr));
66636 DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr));
66637 }
66638#endif
66639 for (; i < n; i++)
66640 {
66641 float a = left[i];
66642 float b = right[i];
66643 left[i] = a + b;
66644 right[i] = a - b;
66645 }
66646}
66647static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr)
66648{
66649 int i;
66650 for (i = 0; i < n; i++)
66651 {
66652 left[i + 576] = left[i]*kr;
66653 left[i] = left[i]*kl;
66654 }
66655}
66656static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, int nbands, int max_band[3])
66657{
66658 int i, k;
66659 max_band[0] = max_band[1] = max_band[2] = -1;
66660 for (i = 0; i < nbands; i++)
66661 {
66662 for (k = 0; k < sfb[i]; k += 2)
66663 {
66664 if (right[k] != 0 || right[k + 1] != 0)
66665 {
66666 max_band[i % 3] = i;
66667 break;
66668 }
66669 }
66670 right += sfb[i];
66671 }
66672}
66673static 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)
66674{
66675 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 };
66676 unsigned i, max_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 7 : 64;
66677 for (i = 0; sfb[i]; i++)
66678 {
66679 unsigned ipos = ist_pos[i];
66680 if ((int)i > max_band[i % 3] && ipos < max_pos)
66681 {
66682 float kl, kr, s = DRMP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1;
66683 if (DRMP3_HDR_TEST_MPEG1(hdr))
66684 {
66685 kl = g_pan[2*ipos];
66686 kr = g_pan[2*ipos + 1];
66687 } else
66688 {
66689 kl = 1;
66690 kr = drmp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh);
66691 if (ipos & 1)
66692 {
66693 kl = kr;
66694 kr = 1;
66695 }
66696 }
66697 drmp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s);
66698 } else if (DRMP3_HDR_TEST_MS_STEREO(hdr))
66699 {
66700 drmp3_L3_midside_stereo(left, sfb[i]);
66701 }
66702 left += sfb[i];
66703 }
66704}
66705static void drmp3_L3_intensity_stereo(float *left, drmp3_uint8 *ist_pos, const drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr)
66706{
66707 int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb;
66708 int i, max_blocks = gr->n_short_sfb ? 3 : 1;
66709 drmp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band);
66710 if (gr->n_long_sfb)
66711 {
66712 max_band[0] = max_band[1] = max_band[2] = DRMP3_MAX(DRMP3_MAX(max_band[0], max_band[1]), max_band[2]);
66713 }
66714 for (i = 0; i < max_blocks; i++)
66715 {
66716 int default_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 3 : 0;
66717 int itop = n_sfb - max_blocks + i;
66718 int prev = itop - max_blocks;
66719 ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]);
66720 }
66721 drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1);
66722}
66723static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sfb)
66724{
66725 int i, len;
66726 float *src = grbuf, *dst = scratch;
66727 for (;0 != (len = *sfb); sfb += 3, src += 2*len)
66728 {
66729 for (i = 0; i < len; i++, src++)
66730 {
66731 *dst++ = src[0*len];
66732 *dst++ = src[1*len];
66733 *dst++ = src[2*len];
66734 }
66735 }
66736 memcpy(grbuf, scratch, (dst - scratch)*sizeof(float));
66737}
66738static void drmp3_L3_antialias(float *grbuf, int nbands)
66739{
66740 static const float g_aa[2][8] = {
66741 {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f},
66742 {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f}
66743 };
66744 for (; nbands > 0; nbands--, grbuf += 18)
66745 {
66746 int i = 0;
66747#if DRMP3_HAVE_SIMD
66748 if (drmp3_have_simd()) for (; i < 8; i += 4)
66749 {
66750 drmp3_f4 vu = DRMP3_VLD(grbuf + 18 + i);
66751 drmp3_f4 vd = DRMP3_VLD(grbuf + 14 - i);
66752 drmp3_f4 vc0 = DRMP3_VLD(g_aa[0] + i);
66753 drmp3_f4 vc1 = DRMP3_VLD(g_aa[1] + i);
66754 vd = DRMP3_VREV(vd);
66755 DRMP3_VSTORE(grbuf + 18 + i, DRMP3_VSUB(DRMP3_VMUL(vu, vc0), DRMP3_VMUL(vd, vc1)));
66756 vd = DRMP3_VADD(DRMP3_VMUL(vu, vc1), DRMP3_VMUL(vd, vc0));
66757 DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vd));
66758 }
66759#endif
66760#ifndef DR_MP3_ONLY_SIMD
66761 for(; i < 8; i++)
66762 {
66763 float u = grbuf[18 + i];
66764 float d = grbuf[17 - i];
66765 grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i];
66766 grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i];
66767 }
66768#endif
66769 }
66770}
66771static void drmp3_L3_dct3_9(float *y)
66772{
66773 float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4;
66774 s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8];
66775 t0 = s0 + s6*0.5f;
66776 s0 -= s6;
66777 t4 = (s4 + s2)*0.93969262f;
66778 t2 = (s8 + s2)*0.76604444f;
66779 s6 = (s4 - s8)*0.17364818f;
66780 s4 += s8 - s2;
66781 s2 = s0 - s4*0.5f;
66782 y[4] = s4 + s0;
66783 s8 = t0 - t2 + s6;
66784 s0 = t0 - t4 + t2;
66785 s4 = t0 + t4 - s6;
66786 s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7];
66787 s3 *= 0.86602540f;
66788 t0 = (s5 + s1)*0.98480775f;
66789 t4 = (s5 - s7)*0.34202014f;
66790 t2 = (s1 + s7)*0.64278761f;
66791 s1 = (s1 - s5 - s7)*0.86602540f;
66792 s5 = t0 - s3 - t2;
66793 s7 = t4 - s3 - t0;
66794 s3 = t4 + s3 - t2;
66795 y[0] = s4 - s7;
66796 y[1] = s2 + s1;
66797 y[2] = s0 - s3;
66798 y[3] = s8 + s5;
66799 y[5] = s8 - s5;
66800 y[6] = s0 + s3;
66801 y[7] = s2 - s1;
66802 y[8] = s4 + s7;
66803}
66804static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands)
66805{
66806 int i, j;
66807 static const float g_twid9[18] = {
66808 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
66809 };
66810 for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9)
66811 {
66812 float co[9], si[9];
66813 co[0] = -grbuf[0];
66814 si[0] = grbuf[17];
66815 for (i = 0; i < 4; i++)
66816 {
66817 si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2];
66818 co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2];
66819 si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3];
66820 co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]);
66821 }
66822 drmp3_L3_dct3_9(co);
66823 drmp3_L3_dct3_9(si);
66824 si[1] = -si[1];
66825 si[3] = -si[3];
66826 si[5] = -si[5];
66827 si[7] = -si[7];
66828 i = 0;
66829#if DRMP3_HAVE_SIMD
66830 if (drmp3_have_simd()) for (; i < 8; i += 4)
66831 {
66832 drmp3_f4 vovl = DRMP3_VLD(overlap + i);
66833 drmp3_f4 vc = DRMP3_VLD(co + i);
66834 drmp3_f4 vs = DRMP3_VLD(si + i);
66835 drmp3_f4 vr0 = DRMP3_VLD(g_twid9 + i);
66836 drmp3_f4 vr1 = DRMP3_VLD(g_twid9 + 9 + i);
66837 drmp3_f4 vw0 = DRMP3_VLD(window + i);
66838 drmp3_f4 vw1 = DRMP3_VLD(window + 9 + i);
66839 drmp3_f4 vsum = DRMP3_VADD(DRMP3_VMUL(vc, vr1), DRMP3_VMUL(vs, vr0));
66840 DRMP3_VSTORE(overlap + i, DRMP3_VSUB(DRMP3_VMUL(vc, vr0), DRMP3_VMUL(vs, vr1)));
66841 DRMP3_VSTORE(grbuf + i, DRMP3_VSUB(DRMP3_VMUL(vovl, vw0), DRMP3_VMUL(vsum, vw1)));
66842 vsum = DRMP3_VADD(DRMP3_VMUL(vovl, vw1), DRMP3_VMUL(vsum, vw0));
66843 DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vsum));
66844 }
66845#endif
66846 for (; i < 9; i++)
66847 {
66848 float ovl = overlap[i];
66849 float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i];
66850 overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i];
66851 grbuf[i] = ovl*window[0 + i] - sum*window[9 + i];
66852 grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i];
66853 }
66854 }
66855}
66856static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst)
66857{
66858 float m1 = x1*0.86602540f;
66859 float a1 = x0 - x2*0.5f;
66860 dst[1] = x0 + x2;
66861 dst[0] = a1 + m1;
66862 dst[2] = a1 - m1;
66863}
66864static void drmp3_L3_imdct12(float *x, float *dst, float *overlap)
66865{
66866 static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f };
66867 float co[3], si[3];
66868 int i;
66869 drmp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co);
66870 drmp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si);
66871 si[1] = -si[1];
66872 for (i = 0; i < 3; i++)
66873 {
66874 float ovl = overlap[i];
66875 float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i];
66876 overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i];
66877 dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i];
66878 dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i];
66879 }
66880}
66881static void drmp3_L3_imdct_short(float *grbuf, float *overlap, int nbands)
66882{
66883 for (;nbands > 0; nbands--, overlap += 9, grbuf += 18)
66884 {
66885 float tmp[18];
66886 memcpy(tmp, grbuf, sizeof(tmp));
66887 memcpy(grbuf, overlap, 6*sizeof(float));
66888 drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6);
66889 drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6);
66890 drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6);
66891 }
66892}
66893static void drmp3_L3_change_sign(float *grbuf)
66894{
66895 int b, i;
66896 for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36)
66897 for (i = 1; i < 18; i += 2)
66898 grbuf[i] = -grbuf[i];
66899}
66900static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands)
66901{
66902 static const float g_mdct_window[2][18] = {
66903 { 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 },
66904 { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f }
66905 };
66906 if (n_long_bands)
66907 {
66908 drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands);
66909 grbuf += 18*n_long_bands;
66910 overlap += 9*n_long_bands;
66911 }
66912 if (block_type == DRMP3_SHORT_BLOCK_TYPE)
66913 drmp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands);
66914 else
66915 drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == DRMP3_STOP_BLOCK_TYPE], 32 - n_long_bands);
66916}
66917static void drmp3_L3_save_reservoir(drmp3dec *h, drmp3dec_scratch *s)
66918{
66919 int pos = (s->bs.pos + 7)/8u;
66920 int remains = s->bs.limit/8u - pos;
66921 if (remains > DRMP3_MAX_BITRESERVOIR_BYTES)
66922 {
66923 pos += remains - DRMP3_MAX_BITRESERVOIR_BYTES;
66924 remains = DRMP3_MAX_BITRESERVOIR_BYTES;
66925 }
66926 if (remains > 0)
66927 {
66928 memmove(h->reserv_buf, s->maindata + pos, remains);
66929 }
66930 h->reserv = remains;
66931}
66932static int drmp3_L3_restore_reservoir(drmp3dec *h, drmp3_bs *bs, drmp3dec_scratch *s, int main_data_begin)
66933{
66934 int frame_bytes = (bs->limit - bs->pos)/8;
66935 int bytes_have = DRMP3_MIN(h->reserv, main_data_begin);
66936 memcpy(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), DRMP3_MIN(h->reserv, main_data_begin));
66937 memcpy(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes);
66938 drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes);
66939 return h->reserv >= main_data_begin;
66940}
66941static void drmp3_L3_decode(drmp3dec *h, drmp3dec_scratch *s, drmp3_L3_gr_info *gr_info, int nch)
66942{
66943 int ch;
66944 for (ch = 0; ch < nch; ch++)
66945 {
66946 int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length;
66947 drmp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch);
66948 drmp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit);
66949 }
66950 if (DRMP3_HDR_TEST_I_STEREO(h->header))
66951 {
66952 drmp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header);
66953 } else if (DRMP3_HDR_IS_MS_STEREO(h->header))
66954 {
66955 drmp3_L3_midside_stereo(s->grbuf[0], 576);
66956 }
66957 for (ch = 0; ch < nch; ch++, gr_info++)
66958 {
66959 int aa_bands = 31;
66960 int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(DRMP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2);
66961 if (gr_info->n_short_sfb)
66962 {
66963 aa_bands = n_long_bands - 1;
66964 drmp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb);
66965 }
66966 drmp3_L3_antialias(s->grbuf[ch], aa_bands);
66967 drmp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands);
66968 drmp3_L3_change_sign(s->grbuf[ch]);
66969 }
66970}
66971static void drmp3d_DCT_II(float *grbuf, int n)
66972{
66973 static const float g_sec[24] = {
66974 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
66975 };
66976 int i, k = 0;
66977#if DRMP3_HAVE_SIMD
66978 if (drmp3_have_simd()) for (; k < n; k += 4)
66979 {
66980 drmp3_f4 t[4][8], *x;
66981 float *y = grbuf + k;
66982 for (x = t[0], i = 0; i < 8; i++, x++)
66983 {
66984 drmp3_f4 x0 = DRMP3_VLD(&y[i*18]);
66985 drmp3_f4 x1 = DRMP3_VLD(&y[(15 - i)*18]);
66986 drmp3_f4 x2 = DRMP3_VLD(&y[(16 + i)*18]);
66987 drmp3_f4 x3 = DRMP3_VLD(&y[(31 - i)*18]);
66988 drmp3_f4 t0 = DRMP3_VADD(x0, x3);
66989 drmp3_f4 t1 = DRMP3_VADD(x1, x2);
66990 drmp3_f4 t2 = DRMP3_VMUL_S(DRMP3_VSUB(x1, x2), g_sec[3*i + 0]);
66991 drmp3_f4 t3 = DRMP3_VMUL_S(DRMP3_VSUB(x0, x3), g_sec[3*i + 1]);
66992 x[0] = DRMP3_VADD(t0, t1);
66993 x[8] = DRMP3_VMUL_S(DRMP3_VSUB(t0, t1), g_sec[3*i + 2]);
66994 x[16] = DRMP3_VADD(t3, t2);
66995 x[24] = DRMP3_VMUL_S(DRMP3_VSUB(t3, t2), g_sec[3*i + 2]);
66996 }
66997 for (x = t[0], i = 0; i < 4; i++, x += 8)
66998 {
66999 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;
67000 xt = DRMP3_VSUB(x0, x7); x0 = DRMP3_VADD(x0, x7);
67001 x7 = DRMP3_VSUB(x1, x6); x1 = DRMP3_VADD(x1, x6);
67002 x6 = DRMP3_VSUB(x2, x5); x2 = DRMP3_VADD(x2, x5);
67003 x5 = DRMP3_VSUB(x3, x4); x3 = DRMP3_VADD(x3, x4);
67004 x4 = DRMP3_VSUB(x0, x3); x0 = DRMP3_VADD(x0, x3);
67005 x3 = DRMP3_VSUB(x1, x2); x1 = DRMP3_VADD(x1, x2);
67006 x[0] = DRMP3_VADD(x0, x1);
67007 x[4] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x1), 0.70710677f);
67008 x5 = DRMP3_VADD(x5, x6);
67009 x6 = DRMP3_VMUL_S(DRMP3_VADD(x6, x7), 0.70710677f);
67010 x7 = DRMP3_VADD(x7, xt);
67011 x3 = DRMP3_VMUL_S(DRMP3_VADD(x3, x4), 0.70710677f);
67012 x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f));
67013 x7 = DRMP3_VADD(x7, DRMP3_VMUL_S(x5, 0.382683432f));
67014 x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f));
67015 x0 = DRMP3_VSUB(xt, x6); xt = DRMP3_VADD(xt, x6);
67016 x[1] = DRMP3_VMUL_S(DRMP3_VADD(xt, x7), 0.50979561f);
67017 x[2] = DRMP3_VMUL_S(DRMP3_VADD(x4, x3), 0.54119611f);
67018 x[3] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x5), 0.60134488f);
67019 x[5] = DRMP3_VMUL_S(DRMP3_VADD(x0, x5), 0.89997619f);
67020 x[6] = DRMP3_VMUL_S(DRMP3_VSUB(x4, x3), 1.30656302f);
67021 x[7] = DRMP3_VMUL_S(DRMP3_VSUB(xt, x7), 2.56291556f);
67022 }
67023 if (k > n - 3)
67024 {
67025#if DRMP3_HAVE_SSE
67026#define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v)
67027#else
67028#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[i*18], vget_low_f32(v))
67029#endif
67030 for (i = 0; i < 7; i++, y += 4*18)
67031 {
67032 drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]);
67033 DRMP3_VSAVE2(0, t[0][i]);
67034 DRMP3_VSAVE2(1, DRMP3_VADD(t[2][i], s));
67035 DRMP3_VSAVE2(2, DRMP3_VADD(t[1][i], t[1][i + 1]));
67036 DRMP3_VSAVE2(3, DRMP3_VADD(t[2][1 + i], s));
67037 }
67038 DRMP3_VSAVE2(0, t[0][7]);
67039 DRMP3_VSAVE2(1, DRMP3_VADD(t[2][7], t[3][7]));
67040 DRMP3_VSAVE2(2, t[1][7]);
67041 DRMP3_VSAVE2(3, t[3][7]);
67042 } else
67043 {
67044#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[i*18], v)
67045 for (i = 0; i < 7; i++, y += 4*18)
67046 {
67047 drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]);
67048 DRMP3_VSAVE4(0, t[0][i]);
67049 DRMP3_VSAVE4(1, DRMP3_VADD(t[2][i], s));
67050 DRMP3_VSAVE4(2, DRMP3_VADD(t[1][i], t[1][i + 1]));
67051 DRMP3_VSAVE4(3, DRMP3_VADD(t[2][1 + i], s));
67052 }
67053 DRMP3_VSAVE4(0, t[0][7]);
67054 DRMP3_VSAVE4(1, DRMP3_VADD(t[2][7], t[3][7]));
67055 DRMP3_VSAVE4(2, t[1][7]);
67056 DRMP3_VSAVE4(3, t[3][7]);
67057 }
67058 } else
67059#endif
67060#ifdef DR_MP3_ONLY_SIMD
67061 {}
67062#else
67063 for (; k < n; k++)
67064 {
67065 float t[4][8], *x, *y = grbuf + k;
67066 for (x = t[0], i = 0; i < 8; i++, x++)
67067 {
67068 float x0 = y[i*18];
67069 float x1 = y[(15 - i)*18];
67070 float x2 = y[(16 + i)*18];
67071 float x3 = y[(31 - i)*18];
67072 float t0 = x0 + x3;
67073 float t1 = x1 + x2;
67074 float t2 = (x1 - x2)*g_sec[3*i + 0];
67075 float t3 = (x0 - x3)*g_sec[3*i + 1];
67076 x[0] = t0 + t1;
67077 x[8] = (t0 - t1)*g_sec[3*i + 2];
67078 x[16] = t3 + t2;
67079 x[24] = (t3 - t2)*g_sec[3*i + 2];
67080 }
67081 for (x = t[0], i = 0; i < 4; i++, x += 8)
67082 {
67083 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;
67084 xt = x0 - x7; x0 += x7;
67085 x7 = x1 - x6; x1 += x6;
67086 x6 = x2 - x5; x2 += x5;
67087 x5 = x3 - x4; x3 += x4;
67088 x4 = x0 - x3; x0 += x3;
67089 x3 = x1 - x2; x1 += x2;
67090 x[0] = x0 + x1;
67091 x[4] = (x0 - x1)*0.70710677f;
67092 x5 = x5 + x6;
67093 x6 = (x6 + x7)*0.70710677f;
67094 x7 = x7 + xt;
67095 x3 = (x3 + x4)*0.70710677f;
67096 x5 -= x7*0.198912367f;
67097 x7 += x5*0.382683432f;
67098 x5 -= x7*0.198912367f;
67099 x0 = xt - x6; xt += x6;
67100 x[1] = (xt + x7)*0.50979561f;
67101 x[2] = (x4 + x3)*0.54119611f;
67102 x[3] = (x0 - x5)*0.60134488f;
67103 x[5] = (x0 + x5)*0.89997619f;
67104 x[6] = (x4 - x3)*1.30656302f;
67105 x[7] = (xt - x7)*2.56291556f;
67106 }
67107 for (i = 0; i < 7; i++, y += 4*18)
67108 {
67109 y[0*18] = t[0][i];
67110 y[1*18] = t[2][i] + t[3][i] + t[3][i + 1];
67111 y[2*18] = t[1][i] + t[1][i + 1];
67112 y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1];
67113 }
67114 y[0*18] = t[0][7];
67115 y[1*18] = t[2][7] + t[3][7];
67116 y[2*18] = t[1][7];
67117 y[3*18] = t[3][7];
67118 }
67119#endif
67120}
67121#ifndef DR_MP3_FLOAT_OUTPUT
67122typedef drmp3_int16 drmp3d_sample_t;
67123static drmp3_int16 drmp3d_scale_pcm(float sample)
67124{
67125 drmp3_int16 s;
67126#if DRMP3_HAVE_ARMV6
67127 drmp3_int32 s32 = (drmp3_int32)(sample + .5f);
67128 s32 -= (s32 < 0);
67129 s = (drmp3_int16)drmp3_clip_int16_arm(s32);
67130#else
67131 if (sample >= 32766.5) return (drmp3_int16) 32767;
67132 if (sample <= -32767.5) return (drmp3_int16)-32768;
67133 s = (drmp3_int16)(sample + .5f);
67134 s -= (s < 0);
67135#endif
67136 return s;
67137}
67138#else
67139typedef float drmp3d_sample_t;
67140static float drmp3d_scale_pcm(float sample)
67141{
67142 return sample*(1.f/32768.f);
67143}
67144#endif
67145static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z)
67146{
67147 float a;
67148 a = (z[14*64] - z[ 0]) * 29;
67149 a += (z[ 1*64] + z[13*64]) * 213;
67150 a += (z[12*64] - z[ 2*64]) * 459;
67151 a += (z[ 3*64] + z[11*64]) * 2037;
67152 a += (z[10*64] - z[ 4*64]) * 5153;
67153 a += (z[ 5*64] + z[ 9*64]) * 6574;
67154 a += (z[ 8*64] - z[ 6*64]) * 37489;
67155 a += z[ 7*64] * 75038;
67156 pcm[0] = drmp3d_scale_pcm(a);
67157 z += 2;
67158 a = z[14*64] * 104;
67159 a += z[12*64] * 1567;
67160 a += z[10*64] * 9727;
67161 a += z[ 8*64] * 64019;
67162 a += z[ 6*64] * -9975;
67163 a += z[ 4*64] * -45;
67164 a += z[ 2*64] * 146;
67165 a += z[ 0*64] * -5;
67166 pcm[16*nch] = drmp3d_scale_pcm(a);
67167}
67168static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins)
67169{
67170 int i;
67171 float *xr = xl + 576*(nch - 1);
67172 drmp3d_sample_t *dstr = dstl + (nch - 1);
67173 static const float g_win[] = {
67174 -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992,
67175 -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856,
67176 -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630,
67177 -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313,
67178 -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908,
67179 -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415,
67180 -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835,
67181 -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169,
67182 -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420,
67183 -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590,
67184 -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679,
67185 -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692,
67186 -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629,
67187 -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494,
67188 -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290
67189 };
67190 float *zlin = lins + 15*64;
67191 const float *w = g_win;
67192 zlin[4*15] = xl[18*16];
67193 zlin[4*15 + 1] = xr[18*16];
67194 zlin[4*15 + 2] = xl[0];
67195 zlin[4*15 + 3] = xr[0];
67196 zlin[4*31] = xl[1 + 18*16];
67197 zlin[4*31 + 1] = xr[1 + 18*16];
67198 zlin[4*31 + 2] = xl[1];
67199 zlin[4*31 + 3] = xr[1];
67200 drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1);
67201 drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1);
67202 drmp3d_synth_pair(dstl, nch, lins + 4*15);
67203 drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64);
67204#if DRMP3_HAVE_SIMD
67205 if (drmp3_have_simd()) for (i = 14; i >= 0; i--)
67206 {
67207#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)]);
67208#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)); }
67209#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))); }
67210#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))); }
67211 drmp3_f4 a, b;
67212 zlin[4*i] = xl[18*(31 - i)];
67213 zlin[4*i + 1] = xr[18*(31 - i)];
67214 zlin[4*i + 2] = xl[1 + 18*(31 - i)];
67215 zlin[4*i + 3] = xr[1 + 18*(31 - i)];
67216 zlin[4*i + 64] = xl[1 + 18*(1 + i)];
67217 zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)];
67218 zlin[4*i - 64 + 2] = xl[18*(1 + i)];
67219 zlin[4*i - 64 + 3] = xr[18*(1 + i)];
67220 DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7)
67221 {
67222#ifndef DR_MP3_FLOAT_OUTPUT
67223#if DRMP3_HAVE_SSE
67224 static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
67225 static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
67226 __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
67227 _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
67228 dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
67229 dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
67230 dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
67231 dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
67232 dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
67233 dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
67234 dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
67235 dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6);
67236#else
67237 int16x4_t pcma, pcmb;
67238 a = DRMP3_VADD(a, DRMP3_VSET(0.5f));
67239 b = DRMP3_VADD(b, DRMP3_VSET(0.5f));
67240 pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0)))));
67241 pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0)))));
67242 vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1);
67243 vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1);
67244 vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0);
67245 vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0);
67246 vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3);
67247 vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3);
67248 vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2);
67249 vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2);
67250#endif
67251#else
67252 static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f };
67253 a = DRMP3_VMUL(a, g_scale);
67254 b = DRMP3_VMUL(b, g_scale);
67255#if DRMP3_HAVE_SSE
67256 _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)));
67257 _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1)));
67258 _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)));
67259 _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0)));
67260 _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)));
67261 _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3)));
67262 _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)));
67263 _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2)));
67264#else
67265 vst1q_lane_f32(dstr + (15 - i)*nch, a, 1);
67266 vst1q_lane_f32(dstr + (17 + i)*nch, b, 1);
67267 vst1q_lane_f32(dstl + (15 - i)*nch, a, 0);
67268 vst1q_lane_f32(dstl + (17 + i)*nch, b, 0);
67269 vst1q_lane_f32(dstr + (47 - i)*nch, a, 3);
67270 vst1q_lane_f32(dstr + (49 + i)*nch, b, 3);
67271 vst1q_lane_f32(dstl + (47 - i)*nch, a, 2);
67272 vst1q_lane_f32(dstl + (49 + i)*nch, b, 2);
67273#endif
67274#endif
67275 }
67276 } else
67277#endif
67278#ifdef DR_MP3_ONLY_SIMD
67279 {}
67280#else
67281 for (i = 14; i >= 0; i--)
67282 {
67283#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];
67284#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; }
67285#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; }
67286#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; }
67287 float a[4], b[4];
67288 zlin[4*i] = xl[18*(31 - i)];
67289 zlin[4*i + 1] = xr[18*(31 - i)];
67290 zlin[4*i + 2] = xl[1 + 18*(31 - i)];
67291 zlin[4*i + 3] = xr[1 + 18*(31 - i)];
67292 zlin[4*(i + 16)] = xl[1 + 18*(1 + i)];
67293 zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)];
67294 zlin[4*(i - 16) + 2] = xl[18*(1 + i)];
67295 zlin[4*(i - 16) + 3] = xr[18*(1 + i)];
67296 DRMP3_S0(0) DRMP3_S2(1) DRMP3_S1(2) DRMP3_S2(3) DRMP3_S1(4) DRMP3_S2(5) DRMP3_S1(6) DRMP3_S2(7)
67297 dstr[(15 - i)*nch] = drmp3d_scale_pcm(a[1]);
67298 dstr[(17 + i)*nch] = drmp3d_scale_pcm(b[1]);
67299 dstl[(15 - i)*nch] = drmp3d_scale_pcm(a[0]);
67300 dstl[(17 + i)*nch] = drmp3d_scale_pcm(b[0]);
67301 dstr[(47 - i)*nch] = drmp3d_scale_pcm(a[3]);
67302 dstr[(49 + i)*nch] = drmp3d_scale_pcm(b[3]);
67303 dstl[(47 - i)*nch] = drmp3d_scale_pcm(a[2]);
67304 dstl[(49 + i)*nch] = drmp3d_scale_pcm(b[2]);
67305 }
67306#endif
67307}
67308static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, drmp3d_sample_t *pcm, float *lins)
67309{
67310 int i;
67311 for (i = 0; i < nch; i++)
67312 {
67313 drmp3d_DCT_II(grbuf + 576*i, nbands);
67314 }
67315 memcpy(lins, qmf_state, sizeof(float)*15*64);
67316 for (i = 0; i < nbands; i += 2)
67317 {
67318 drmp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64);
67319 }
67320#ifndef DR_MP3_NONSTANDARD_BUT_LOGICAL
67321 if (nch == 1)
67322 {
67323 for (i = 0; i < 15*64; i += 2)
67324 {
67325 qmf_state[i] = lins[nbands*64 + i];
67326 }
67327 } else
67328#endif
67329 {
67330 memcpy(qmf_state, lins + nbands*64, sizeof(float)*15*64);
67331 }
67332}
67333static int drmp3d_match_frame(const drmp3_uint8 *hdr, int mp3_bytes, int frame_bytes)
67334{
67335 int i, nmatch;
67336 for (i = 0, nmatch = 0; nmatch < DRMP3_MAX_FRAME_SYNC_MATCHES; nmatch++)
67337 {
67338 i += drmp3_hdr_frame_bytes(hdr + i, frame_bytes) + drmp3_hdr_padding(hdr + i);
67339 if (i + DRMP3_HDR_SIZE > mp3_bytes)
67340 return nmatch > 0;
67341 if (!drmp3_hdr_compare(hdr, hdr + i))
67342 return 0;
67343 }
67344 return 1;
67345}
67346static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes)
67347{
67348 int i, k;
67349 for (i = 0; i < mp3_bytes - DRMP3_HDR_SIZE; i++, mp3++)
67350 {
67351 if (drmp3_hdr_valid(mp3))
67352 {
67353 int frame_bytes = drmp3_hdr_frame_bytes(mp3, *free_format_bytes);
67354 int frame_and_padding = frame_bytes + drmp3_hdr_padding(mp3);
67355 for (k = DRMP3_HDR_SIZE; !frame_bytes && k < DRMP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - DRMP3_HDR_SIZE; k++)
67356 {
67357 if (drmp3_hdr_compare(mp3, mp3 + k))
67358 {
67359 int fb = k - drmp3_hdr_padding(mp3);
67360 int nextfb = fb + drmp3_hdr_padding(mp3 + k);
67361 if (i + k + nextfb + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + k + nextfb))
67362 continue;
67363 frame_and_padding = k;
67364 frame_bytes = fb;
67365 *free_format_bytes = fb;
67366 }
67367 }
67368 if ((frame_bytes && i + frame_and_padding <= mp3_bytes &&
67369 drmp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) ||
67370 (!i && frame_and_padding == mp3_bytes))
67371 {
67372 *ptr_frame_bytes = frame_and_padding;
67373 return i;
67374 }
67375 *free_format_bytes = 0;
67376 }
67377 }
67378 *ptr_frame_bytes = 0;
67379 return mp3_bytes;
67380}
67381DRMP3_API void drmp3dec_init(drmp3dec *dec)
67382{
67383 dec->header[0] = 0;
67384}
67385DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info)
67386{
67387 int i = 0, igr, frame_size = 0, success = 1;
67388 const drmp3_uint8 *hdr;
67389 drmp3_bs bs_frame[1];
67390 drmp3dec_scratch scratch;
67391 if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3))
67392 {
67393 frame_size = drmp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + drmp3_hdr_padding(mp3);
67394 if (frame_size != mp3_bytes && (frame_size + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + frame_size)))
67395 {
67396 frame_size = 0;
67397 }
67398 }
67399 if (!frame_size)
67400 {
67401 memset(dec, 0, sizeof(drmp3dec));
67402 i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size);
67403 if (!frame_size || i + frame_size > mp3_bytes)
67404 {
67405 info->frame_bytes = i;
67406 return 0;
67407 }
67408 }
67409 hdr = mp3 + i;
67410 memcpy(dec->header, hdr, DRMP3_HDR_SIZE);
67411 info->frame_bytes = i + frame_size;
67412 info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2;
67413 info->hz = drmp3_hdr_sample_rate_hz(hdr);
67414 info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr);
67415 info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr);
67416 drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE);
67417 if (DRMP3_HDR_IS_CRC(hdr))
67418 {
67419 drmp3_bs_get_bits(bs_frame, 16);
67420 }
67421 if (info->layer == 3)
67422 {
67423 int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr);
67424 if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit)
67425 {
67426 drmp3dec_init(dec);
67427 return 0;
67428 }
67429 success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin);
67430 if (success && pcm != NULL)
67431 {
67432 for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels))
67433 {
67434 memset(scratch.grbuf[0], 0, 576*2*sizeof(float));
67435 drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels);
67436 drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]);
67437 }
67438 }
67439 drmp3_L3_save_reservoir(dec, &scratch);
67440 } else
67441 {
67442#ifdef DR_MP3_ONLY_MP3
67443 return 0;
67444#else
67445 drmp3_L12_scale_info sci[1];
67446 if (pcm == NULL) {
67447 return drmp3_hdr_frame_samples(hdr);
67448 }
67449 drmp3_L12_read_scale_info(hdr, bs_frame, sci);
67450 memset(scratch.grbuf[0], 0, 576*2*sizeof(float));
67451 for (i = 0, igr = 0; igr < 3; igr++)
67452 {
67453 if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1)))
67454 {
67455 i = 0;
67456 drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]);
67457 drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]);
67458 memset(scratch.grbuf[0], 0, 576*2*sizeof(float));
67459 pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels);
67460 }
67461 if (bs_frame->pos > bs_frame->limit)
67462 {
67463 drmp3dec_init(dec);
67464 return 0;
67465 }
67466 }
67467#endif
67468 }
67469 return success*drmp3_hdr_frame_samples(dec->header);
67470}
67471DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples)
67472{
67473 size_t i = 0;
67474#if DRMP3_HAVE_SIMD
67475 size_t aligned_count = num_samples & ~7;
67476 for(; i < aligned_count; i+=8)
67477 {
67478 drmp3_f4 scale = DRMP3_VSET(32768.0f);
67479 drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), scale);
67480 drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), scale);
67481#if DRMP3_HAVE_SSE
67482 drmp3_f4 s16max = DRMP3_VSET( 32767.0f);
67483 drmp3_f4 s16min = DRMP3_VSET(-32768.0f);
67484 __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)),
67485 _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min)));
67486 out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
67487 out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
67488 out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
67489 out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
67490 out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
67491 out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
67492 out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6);
67493 out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
67494#else
67495 int16x4_t pcma, pcmb;
67496 a = DRMP3_VADD(a, DRMP3_VSET(0.5f));
67497 b = DRMP3_VADD(b, DRMP3_VSET(0.5f));
67498 pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0)))));
67499 pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0)))));
67500 vst1_lane_s16(out+i , pcma, 0);
67501 vst1_lane_s16(out+i+1, pcma, 1);
67502 vst1_lane_s16(out+i+2, pcma, 2);
67503 vst1_lane_s16(out+i+3, pcma, 3);
67504 vst1_lane_s16(out+i+4, pcmb, 0);
67505 vst1_lane_s16(out+i+5, pcmb, 1);
67506 vst1_lane_s16(out+i+6, pcmb, 2);
67507 vst1_lane_s16(out+i+7, pcmb, 3);
67508#endif
67509 }
67510#endif
67511 for(; i < num_samples; i++)
67512 {
67513 float sample = in[i] * 32768.0f;
67514 if (sample >= 32766.5)
67515 out[i] = (drmp3_int16) 32767;
67516 else if (sample <= -32767.5)
67517 out[i] = (drmp3_int16)-32768;
67518 else
67519 {
67520 short s = (drmp3_int16)(sample + .5f);
67521 s -= (s < 0);
67522 out[i] = s;
67523 }
67524 }
67525}
67526#include <math.h>
67527#if defined(SIZE_MAX)
67528 #define DRMP3_SIZE_MAX SIZE_MAX
67529#else
67530 #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
67531 #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF)
67532 #else
67533 #define DRMP3_SIZE_MAX 0xFFFFFFFF
67534 #endif
67535#endif
67536#ifndef DRMP3_SEEK_LEADING_MP3_FRAMES
67537#define DRMP3_SEEK_LEADING_MP3_FRAMES 2
67538#endif
67539#define DRMP3_MIN_DATA_CHUNK_SIZE 16384
67540#ifndef DRMP3_DATA_CHUNK_SIZE
67541#define DRMP3_DATA_CHUNK_SIZE DRMP3_MIN_DATA_CHUNK_SIZE*4
67542#endif
67543#ifndef DRMP3_ASSERT
67544#include <assert.h>
67545#define DRMP3_ASSERT(expression) assert(expression)
67546#endif
67547#ifndef DRMP3_COPY_MEMORY
67548#define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
67549#endif
67550#ifndef DRMP3_ZERO_MEMORY
67551#define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
67552#endif
67553#define DRMP3_ZERO_OBJECT(p) DRMP3_ZERO_MEMORY((p), sizeof(*(p)))
67554#ifndef DRMP3_MALLOC
67555#define DRMP3_MALLOC(sz) malloc((sz))
67556#endif
67557#ifndef DRMP3_REALLOC
67558#define DRMP3_REALLOC(p, sz) realloc((p), (sz))
67559#endif
67560#ifndef DRMP3_FREE
67561#define DRMP3_FREE(p) free((p))
67562#endif
67563#define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0]))
67564#define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi)))
67565#ifndef DRMP3_PI_D
67566#define DRMP3_PI_D 3.14159265358979323846264
67567#endif
67568#define DRMP3_DEFAULT_RESAMPLER_LPF_ORDER 2
67569static DRMP3_INLINE float drmp3_mix_f32(float x, float y, float a)
67570{
67571 return x*(1-a) + y*a;
67572}
67573static DRMP3_INLINE float drmp3_mix_f32_fast(float x, float y, float a)
67574{
67575 float r0 = (y - x);
67576 float r1 = r0*a;
67577 return x + r1;
67578}
67579static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b)
67580{
67581 for (;;) {
67582 if (b == 0) {
67583 break;
67584 } else {
67585 drmp3_uint32 t = a;
67586 a = b;
67587 b = t % a;
67588 }
67589 }
67590 return a;
67591}
67592static DRMP3_INLINE double drmp3_sin(double x)
67593{
67594 return sin(x);
67595}
67596static DRMP3_INLINE double drmp3_exp(double x)
67597{
67598 return exp(x);
67599}
67600static DRMP3_INLINE double drmp3_cos(double x)
67601{
67602 return drmp3_sin((DRMP3_PI_D*0.5) - x);
67603}
67604static void* drmp3__malloc_default(size_t sz, void* pUserData)
67605{
67606 (void)pUserData;
67607 return DRMP3_MALLOC(sz);
67608}
67609static void* drmp3__realloc_default(void* p, size_t sz, void* pUserData)
67610{
67611 (void)pUserData;
67612 return DRMP3_REALLOC(p, sz);
67613}
67614static void drmp3__free_default(void* p, void* pUserData)
67615{
67616 (void)pUserData;
67617 DRMP3_FREE(p);
67618}
67619static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks)
67620{
67621 if (pAllocationCallbacks == NULL) {
67622 return NULL;
67623 }
67624 if (pAllocationCallbacks->onMalloc != NULL) {
67625 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
67626 }
67627 if (pAllocationCallbacks->onRealloc != NULL) {
67628 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
67629 }
67630 return NULL;
67631}
67632static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drmp3_allocation_callbacks* pAllocationCallbacks)
67633{
67634 if (pAllocationCallbacks == NULL) {
67635 return NULL;
67636 }
67637 if (pAllocationCallbacks->onRealloc != NULL) {
67638 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
67639 }
67640 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
67641 void* p2;
67642 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
67643 if (p2 == NULL) {
67644 return NULL;
67645 }
67646 if (p != NULL) {
67647 DRMP3_COPY_MEMORY(p2, p, szOld);
67648 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
67649 }
67650 return p2;
67651 }
67652 return NULL;
67653}
67654static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks)
67655{
67656 if (p == NULL || pAllocationCallbacks == NULL) {
67657 return;
67658 }
67659 if (pAllocationCallbacks->onFree != NULL) {
67660 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
67661 }
67662}
67663static drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drmp3_allocation_callbacks* pAllocationCallbacks)
67664{
67665 if (pAllocationCallbacks != NULL) {
67666 return *pAllocationCallbacks;
67667 } else {
67668 drmp3_allocation_callbacks allocationCallbacks;
67669 allocationCallbacks.pUserData = NULL;
67670 allocationCallbacks.onMalloc = drmp3__malloc_default;
67671 allocationCallbacks.onRealloc = drmp3__realloc_default;
67672 allocationCallbacks.onFree = drmp3__free_default;
67673 return allocationCallbacks;
67674 }
67675}
67676static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead)
67677{
67678 size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead);
67679 pMP3->streamCursor += bytesRead;
67680 return bytesRead;
67681}
67682static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin)
67683{
67684 DRMP3_ASSERT(offset >= 0);
67685 if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) {
67686 return DRMP3_FALSE;
67687 }
67688 if (origin == drmp3_seek_origin_start) {
67689 pMP3->streamCursor = (drmp3_uint64)offset;
67690 } else {
67691 pMP3->streamCursor += offset;
67692 }
67693 return DRMP3_TRUE;
67694}
67695static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin)
67696{
67697 if (offset <= 0x7FFFFFFF) {
67698 return drmp3__on_seek(pMP3, (int)offset, origin);
67699 }
67700 if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) {
67701 return DRMP3_FALSE;
67702 }
67703 offset -= 0x7FFFFFFF;
67704 while (offset > 0) {
67705 if (offset <= 0x7FFFFFFF) {
67706 if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) {
67707 return DRMP3_FALSE;
67708 }
67709 offset = 0;
67710 } else {
67711 if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) {
67712 return DRMP3_FALSE;
67713 }
67714 offset -= 0x7FFFFFFF;
67715 }
67716 }
67717 return DRMP3_TRUE;
67718}
67719static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
67720{
67721 drmp3_uint32 pcmFramesRead = 0;
67722 DRMP3_ASSERT(pMP3 != NULL);
67723 DRMP3_ASSERT(pMP3->onRead != NULL);
67724 if (pMP3->atEnd) {
67725 return 0;
67726 }
67727 for (;;) {
67728 drmp3dec_frame_info info;
67729 if (pMP3->dataSize < DRMP3_MIN_DATA_CHUNK_SIZE) {
67730 size_t bytesRead;
67731 if (pMP3->pData != NULL) {
67732 memmove(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
67733 }
67734 pMP3->dataConsumed = 0;
67735 if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) {
67736 drmp3_uint8* pNewData;
67737 size_t newDataCap;
67738 newDataCap = DRMP3_DATA_CHUNK_SIZE;
67739 pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
67740 if (pNewData == NULL) {
67741 return 0;
67742 }
67743 pMP3->pData = pNewData;
67744 pMP3->dataCapacity = newDataCap;
67745 }
67746 bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
67747 if (bytesRead == 0) {
67748 if (pMP3->dataSize == 0) {
67749 pMP3->atEnd = DRMP3_TRUE;
67750 return 0;
67751 }
67752 }
67753 pMP3->dataSize += bytesRead;
67754 }
67755 if (pMP3->dataSize > INT_MAX) {
67756 pMP3->atEnd = DRMP3_TRUE;
67757 return 0;
67758 }
67759 DRMP3_ASSERT(pMP3->pData != NULL);
67760 DRMP3_ASSERT(pMP3->dataCapacity > 0);
67761 pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info);
67762 if (info.frame_bytes > 0) {
67763 pMP3->dataConsumed += (size_t)info.frame_bytes;
67764 pMP3->dataSize -= (size_t)info.frame_bytes;
67765 }
67766 if (pcmFramesRead > 0) {
67767 pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header);
67768 pMP3->pcmFramesConsumedInMP3Frame = 0;
67769 pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
67770 pMP3->mp3FrameChannels = info.channels;
67771 pMP3->mp3FrameSampleRate = info.hz;
67772 break;
67773 } else if (info.frame_bytes == 0) {
67774 size_t bytesRead;
67775 memmove(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
67776 pMP3->dataConsumed = 0;
67777 if (pMP3->dataCapacity == pMP3->dataSize) {
67778 drmp3_uint8* pNewData;
67779 size_t newDataCap;
67780 newDataCap = pMP3->dataCapacity + DRMP3_DATA_CHUNK_SIZE;
67781 pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
67782 if (pNewData == NULL) {
67783 return 0;
67784 }
67785 pMP3->pData = pNewData;
67786 pMP3->dataCapacity = newDataCap;
67787 }
67788 bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
67789 if (bytesRead == 0) {
67790 pMP3->atEnd = DRMP3_TRUE;
67791 return 0;
67792 }
67793 pMP3->dataSize += bytesRead;
67794 }
67795 };
67796 return pcmFramesRead;
67797}
67798static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
67799{
67800 drmp3_uint32 pcmFramesRead = 0;
67801 drmp3dec_frame_info info;
67802 DRMP3_ASSERT(pMP3 != NULL);
67803 DRMP3_ASSERT(pMP3->memory.pData != NULL);
67804 if (pMP3->atEnd) {
67805 return 0;
67806 }
67807 pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info);
67808 if (pcmFramesRead > 0) {
67809 pMP3->pcmFramesConsumedInMP3Frame = 0;
67810 pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
67811 pMP3->mp3FrameChannels = info.channels;
67812 pMP3->mp3FrameSampleRate = info.hz;
67813 }
67814 pMP3->memory.currentReadPos += (size_t)info.frame_bytes;
67815 return pcmFramesRead;
67816}
67817static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
67818{
67819 if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) {
67820 return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames);
67821 } else {
67822 return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames);
67823 }
67824}
67825static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3)
67826{
67827 DRMP3_ASSERT(pMP3 != NULL);
67828 return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames);
67829}
67830#if 0
67831static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3)
67832{
67833 drmp3_uint32 pcmFrameCount;
67834 DRMP3_ASSERT(pMP3 != NULL);
67835 pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL);
67836 if (pcmFrameCount == 0) {
67837 return 0;
67838 }
67839 pMP3->currentPCMFrame += pcmFrameCount;
67840 pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount;
67841 pMP3->pcmFramesRemainingInMP3Frame = 0;
67842 return pcmFrameCount;
67843}
67844#endif
67845static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks)
67846{
67847 DRMP3_ASSERT(pMP3 != NULL);
67848 DRMP3_ASSERT(onRead != NULL);
67849 drmp3dec_init(&pMP3->decoder);
67850 pMP3->onRead = onRead;
67851 pMP3->onSeek = onSeek;
67852 pMP3->pUserData = pUserData;
67853 pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
67854 if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) {
67855 return DRMP3_FALSE;
67856 }
67857 if (!drmp3_decode_next_frame(pMP3)) {
67858 drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
67859 return DRMP3_FALSE;
67860 }
67861 pMP3->channels = pMP3->mp3FrameChannels;
67862 pMP3->sampleRate = pMP3->mp3FrameSampleRate;
67863 return DRMP3_TRUE;
67864}
67865DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks)
67866{
67867 if (pMP3 == NULL || onRead == NULL) {
67868 return DRMP3_FALSE;
67869 }
67870 DRMP3_ZERO_OBJECT(pMP3);
67871 return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks);
67872}
67873static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
67874{
67875 drmp3* pMP3 = (drmp3*)pUserData;
67876 size_t bytesRemaining;
67877 DRMP3_ASSERT(pMP3 != NULL);
67878 DRMP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos);
67879 bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos;
67880 if (bytesToRead > bytesRemaining) {
67881 bytesToRead = bytesRemaining;
67882 }
67883 if (bytesToRead > 0) {
67884 DRMP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead);
67885 pMP3->memory.currentReadPos += bytesToRead;
67886 }
67887 return bytesToRead;
67888}
67889static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin)
67890{
67891 drmp3* pMP3 = (drmp3*)pUserData;
67892 DRMP3_ASSERT(pMP3 != NULL);
67893 if (origin == drmp3_seek_origin_current) {
67894 if (byteOffset > 0) {
67895 if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) {
67896 byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos);
67897 }
67898 } else {
67899 if (pMP3->memory.currentReadPos < (size_t)-byteOffset) {
67900 byteOffset = -(int)pMP3->memory.currentReadPos;
67901 }
67902 }
67903 pMP3->memory.currentReadPos += byteOffset;
67904 } else {
67905 if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) {
67906 pMP3->memory.currentReadPos = byteOffset;
67907 } else {
67908 pMP3->memory.currentReadPos = pMP3->memory.dataSize;
67909 }
67910 }
67911 return DRMP3_TRUE;
67912}
67913DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks)
67914{
67915 if (pMP3 == NULL) {
67916 return DRMP3_FALSE;
67917 }
67918 DRMP3_ZERO_OBJECT(pMP3);
67919 if (pData == NULL || dataSize == 0) {
67920 return DRMP3_FALSE;
67921 }
67922 pMP3->memory.pData = (const drmp3_uint8*)pData;
67923 pMP3->memory.dataSize = dataSize;
67924 pMP3->memory.currentReadPos = 0;
67925 return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pAllocationCallbacks);
67926}
67927#ifndef DR_MP3_NO_STDIO
67928#include <stdio.h>
67929#include <wchar.h>
67930#include <errno.h>
67931static drmp3_result drmp3_result_from_errno(int e)
67932{
67933 switch (e)
67934 {
67935 case 0: return DRMP3_SUCCESS;
67936 #ifdef EPERM
67937 case EPERM: return DRMP3_INVALID_OPERATION;
67938 #endif
67939 #ifdef ENOENT
67940 case ENOENT: return DRMP3_DOES_NOT_EXIST;
67941 #endif
67942 #ifdef ESRCH
67943 case ESRCH: return DRMP3_DOES_NOT_EXIST;
67944 #endif
67945 #ifdef EINTR
67946 case EINTR: return DRMP3_INTERRUPT;
67947 #endif
67948 #ifdef EIO
67949 case EIO: return DRMP3_IO_ERROR;
67950 #endif
67951 #ifdef ENXIO
67952 case ENXIO: return DRMP3_DOES_NOT_EXIST;
67953 #endif
67954 #ifdef E2BIG
67955 case E2BIG: return DRMP3_INVALID_ARGS;
67956 #endif
67957 #ifdef ENOEXEC
67958 case ENOEXEC: return DRMP3_INVALID_FILE;
67959 #endif
67960 #ifdef EBADF
67961 case EBADF: return DRMP3_INVALID_FILE;
67962 #endif
67963 #ifdef ECHILD
67964 case ECHILD: return DRMP3_ERROR;
67965 #endif
67966 #ifdef EAGAIN
67967 case EAGAIN: return DRMP3_UNAVAILABLE;
67968 #endif
67969 #ifdef ENOMEM
67970 case ENOMEM: return DRMP3_OUT_OF_MEMORY;
67971 #endif
67972 #ifdef EACCES
67973 case EACCES: return DRMP3_ACCESS_DENIED;
67974 #endif
67975 #ifdef EFAULT
67976 case EFAULT: return DRMP3_BAD_ADDRESS;
67977 #endif
67978 #ifdef ENOTBLK
67979 case ENOTBLK: return DRMP3_ERROR;
67980 #endif
67981 #ifdef EBUSY
67982 case EBUSY: return DRMP3_BUSY;
67983 #endif
67984 #ifdef EEXIST
67985 case EEXIST: return DRMP3_ALREADY_EXISTS;
67986 #endif
67987 #ifdef EXDEV
67988 case EXDEV: return DRMP3_ERROR;
67989 #endif
67990 #ifdef ENODEV
67991 case ENODEV: return DRMP3_DOES_NOT_EXIST;
67992 #endif
67993 #ifdef ENOTDIR
67994 case ENOTDIR: return DRMP3_NOT_DIRECTORY;
67995 #endif
67996 #ifdef EISDIR
67997 case EISDIR: return DRMP3_IS_DIRECTORY;
67998 #endif
67999 #ifdef EINVAL
68000 case EINVAL: return DRMP3_INVALID_ARGS;
68001 #endif
68002 #ifdef ENFILE
68003 case ENFILE: return DRMP3_TOO_MANY_OPEN_FILES;
68004 #endif
68005 #ifdef EMFILE
68006 case EMFILE: return DRMP3_TOO_MANY_OPEN_FILES;
68007 #endif
68008 #ifdef ENOTTY
68009 case ENOTTY: return DRMP3_INVALID_OPERATION;
68010 #endif
68011 #ifdef ETXTBSY
68012 case ETXTBSY: return DRMP3_BUSY;
68013 #endif
68014 #ifdef EFBIG
68015 case EFBIG: return DRMP3_TOO_BIG;
68016 #endif
68017 #ifdef ENOSPC
68018 case ENOSPC: return DRMP3_NO_SPACE;
68019 #endif
68020 #ifdef ESPIPE
68021 case ESPIPE: return DRMP3_BAD_SEEK;
68022 #endif
68023 #ifdef EROFS
68024 case EROFS: return DRMP3_ACCESS_DENIED;
68025 #endif
68026 #ifdef EMLINK
68027 case EMLINK: return DRMP3_TOO_MANY_LINKS;
68028 #endif
68029 #ifdef EPIPE
68030 case EPIPE: return DRMP3_BAD_PIPE;
68031 #endif
68032 #ifdef EDOM
68033 case EDOM: return DRMP3_OUT_OF_RANGE;
68034 #endif
68035 #ifdef ERANGE
68036 case ERANGE: return DRMP3_OUT_OF_RANGE;
68037 #endif
68038 #ifdef EDEADLK
68039 case EDEADLK: return DRMP3_DEADLOCK;
68040 #endif
68041 #ifdef ENAMETOOLONG
68042 case ENAMETOOLONG: return DRMP3_PATH_TOO_LONG;
68043 #endif
68044 #ifdef ENOLCK
68045 case ENOLCK: return DRMP3_ERROR;
68046 #endif
68047 #ifdef ENOSYS
68048 case ENOSYS: return DRMP3_NOT_IMPLEMENTED;
68049 #endif
68050 #ifdef ENOTEMPTY
68051 case ENOTEMPTY: return DRMP3_DIRECTORY_NOT_EMPTY;
68052 #endif
68053 #ifdef ELOOP
68054 case ELOOP: return DRMP3_TOO_MANY_LINKS;
68055 #endif
68056 #ifdef ENOMSG
68057 case ENOMSG: return DRMP3_NO_MESSAGE;
68058 #endif
68059 #ifdef EIDRM
68060 case EIDRM: return DRMP3_ERROR;
68061 #endif
68062 #ifdef ECHRNG
68063 case ECHRNG: return DRMP3_ERROR;
68064 #endif
68065 #ifdef EL2NSYNC
68066 case EL2NSYNC: return DRMP3_ERROR;
68067 #endif
68068 #ifdef EL3HLT
68069 case EL3HLT: return DRMP3_ERROR;
68070 #endif
68071 #ifdef EL3RST
68072 case EL3RST: return DRMP3_ERROR;
68073 #endif
68074 #ifdef ELNRNG
68075 case ELNRNG: return DRMP3_OUT_OF_RANGE;
68076 #endif
68077 #ifdef EUNATCH
68078 case EUNATCH: return DRMP3_ERROR;
68079 #endif
68080 #ifdef ENOCSI
68081 case ENOCSI: return DRMP3_ERROR;
68082 #endif
68083 #ifdef EL2HLT
68084 case EL2HLT: return DRMP3_ERROR;
68085 #endif
68086 #ifdef EBADE
68087 case EBADE: return DRMP3_ERROR;
68088 #endif
68089 #ifdef EBADR
68090 case EBADR: return DRMP3_ERROR;
68091 #endif
68092 #ifdef EXFULL
68093 case EXFULL: return DRMP3_ERROR;
68094 #endif
68095 #ifdef ENOANO
68096 case ENOANO: return DRMP3_ERROR;
68097 #endif
68098 #ifdef EBADRQC
68099 case EBADRQC: return DRMP3_ERROR;
68100 #endif
68101 #ifdef EBADSLT
68102 case EBADSLT: return DRMP3_ERROR;
68103 #endif
68104 #ifdef EBFONT
68105 case EBFONT: return DRMP3_INVALID_FILE;
68106 #endif
68107 #ifdef ENOSTR
68108 case ENOSTR: return DRMP3_ERROR;
68109 #endif
68110 #ifdef ENODATA
68111 case ENODATA: return DRMP3_NO_DATA_AVAILABLE;
68112 #endif
68113 #ifdef ETIME
68114 case ETIME: return DRMP3_TIMEOUT;
68115 #endif
68116 #ifdef ENOSR
68117 case ENOSR: return DRMP3_NO_DATA_AVAILABLE;
68118 #endif
68119 #ifdef ENONET
68120 case ENONET: return DRMP3_NO_NETWORK;
68121 #endif
68122 #ifdef ENOPKG
68123 case ENOPKG: return DRMP3_ERROR;
68124 #endif
68125 #ifdef EREMOTE
68126 case EREMOTE: return DRMP3_ERROR;
68127 #endif
68128 #ifdef ENOLINK
68129 case ENOLINK: return DRMP3_ERROR;
68130 #endif
68131 #ifdef EADV
68132 case EADV: return DRMP3_ERROR;
68133 #endif
68134 #ifdef ESRMNT
68135 case ESRMNT: return DRMP3_ERROR;
68136 #endif
68137 #ifdef ECOMM
68138 case ECOMM: return DRMP3_ERROR;
68139 #endif
68140 #ifdef EPROTO
68141 case EPROTO: return DRMP3_ERROR;
68142 #endif
68143 #ifdef EMULTIHOP
68144 case EMULTIHOP: return DRMP3_ERROR;
68145 #endif
68146 #ifdef EDOTDOT
68147 case EDOTDOT: return DRMP3_ERROR;
68148 #endif
68149 #ifdef EBADMSG
68150 case EBADMSG: return DRMP3_BAD_MESSAGE;
68151 #endif
68152 #ifdef EOVERFLOW
68153 case EOVERFLOW: return DRMP3_TOO_BIG;
68154 #endif
68155 #ifdef ENOTUNIQ
68156 case ENOTUNIQ: return DRMP3_NOT_UNIQUE;
68157 #endif
68158 #ifdef EBADFD
68159 case EBADFD: return DRMP3_ERROR;
68160 #endif
68161 #ifdef EREMCHG
68162 case EREMCHG: return DRMP3_ERROR;
68163 #endif
68164 #ifdef ELIBACC
68165 case ELIBACC: return DRMP3_ACCESS_DENIED;
68166 #endif
68167 #ifdef ELIBBAD
68168 case ELIBBAD: return DRMP3_INVALID_FILE;
68169 #endif
68170 #ifdef ELIBSCN
68171 case ELIBSCN: return DRMP3_INVALID_FILE;
68172 #endif
68173 #ifdef ELIBMAX
68174 case ELIBMAX: return DRMP3_ERROR;
68175 #endif
68176 #ifdef ELIBEXEC
68177 case ELIBEXEC: return DRMP3_ERROR;
68178 #endif
68179 #ifdef EILSEQ
68180 case EILSEQ: return DRMP3_INVALID_DATA;
68181 #endif
68182 #ifdef ERESTART
68183 case ERESTART: return DRMP3_ERROR;
68184 #endif
68185 #ifdef ESTRPIPE
68186 case ESTRPIPE: return DRMP3_ERROR;
68187 #endif
68188 #ifdef EUSERS
68189 case EUSERS: return DRMP3_ERROR;
68190 #endif
68191 #ifdef ENOTSOCK
68192 case ENOTSOCK: return DRMP3_NOT_SOCKET;
68193 #endif
68194 #ifdef EDESTADDRREQ
68195 case EDESTADDRREQ: return DRMP3_NO_ADDRESS;
68196 #endif
68197 #ifdef EMSGSIZE
68198 case EMSGSIZE: return DRMP3_TOO_BIG;
68199 #endif
68200 #ifdef EPROTOTYPE
68201 case EPROTOTYPE: return DRMP3_BAD_PROTOCOL;
68202 #endif
68203 #ifdef ENOPROTOOPT
68204 case ENOPROTOOPT: return DRMP3_PROTOCOL_UNAVAILABLE;
68205 #endif
68206 #ifdef EPROTONOSUPPORT
68207 case EPROTONOSUPPORT: return DRMP3_PROTOCOL_NOT_SUPPORTED;
68208 #endif
68209 #ifdef ESOCKTNOSUPPORT
68210 case ESOCKTNOSUPPORT: return DRMP3_SOCKET_NOT_SUPPORTED;
68211 #endif
68212 #ifdef EOPNOTSUPP
68213 case EOPNOTSUPP: return DRMP3_INVALID_OPERATION;
68214 #endif
68215 #ifdef EPFNOSUPPORT
68216 case EPFNOSUPPORT: return DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED;
68217 #endif
68218 #ifdef EAFNOSUPPORT
68219 case EAFNOSUPPORT: return DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED;
68220 #endif
68221 #ifdef EADDRINUSE
68222 case EADDRINUSE: return DRMP3_ALREADY_IN_USE;
68223 #endif
68224 #ifdef EADDRNOTAVAIL
68225 case EADDRNOTAVAIL: return DRMP3_ERROR;
68226 #endif
68227 #ifdef ENETDOWN
68228 case ENETDOWN: return DRMP3_NO_NETWORK;
68229 #endif
68230 #ifdef ENETUNREACH
68231 case ENETUNREACH: return DRMP3_NO_NETWORK;
68232 #endif
68233 #ifdef ENETRESET
68234 case ENETRESET: return DRMP3_NO_NETWORK;
68235 #endif
68236 #ifdef ECONNABORTED
68237 case ECONNABORTED: return DRMP3_NO_NETWORK;
68238 #endif
68239 #ifdef ECONNRESET
68240 case ECONNRESET: return DRMP3_CONNECTION_RESET;
68241 #endif
68242 #ifdef ENOBUFS
68243 case ENOBUFS: return DRMP3_NO_SPACE;
68244 #endif
68245 #ifdef EISCONN
68246 case EISCONN: return DRMP3_ALREADY_CONNECTED;
68247 #endif
68248 #ifdef ENOTCONN
68249 case ENOTCONN: return DRMP3_NOT_CONNECTED;
68250 #endif
68251 #ifdef ESHUTDOWN
68252 case ESHUTDOWN: return DRMP3_ERROR;
68253 #endif
68254 #ifdef ETOOMANYREFS
68255 case ETOOMANYREFS: return DRMP3_ERROR;
68256 #endif
68257 #ifdef ETIMEDOUT
68258 case ETIMEDOUT: return DRMP3_TIMEOUT;
68259 #endif
68260 #ifdef ECONNREFUSED
68261 case ECONNREFUSED: return DRMP3_CONNECTION_REFUSED;
68262 #endif
68263 #ifdef EHOSTDOWN
68264 case EHOSTDOWN: return DRMP3_NO_HOST;
68265 #endif
68266 #ifdef EHOSTUNREACH
68267 case EHOSTUNREACH: return DRMP3_NO_HOST;
68268 #endif
68269 #ifdef EALREADY
68270 case EALREADY: return DRMP3_IN_PROGRESS;
68271 #endif
68272 #ifdef EINPROGRESS
68273 case EINPROGRESS: return DRMP3_IN_PROGRESS;
68274 #endif
68275 #ifdef ESTALE
68276 case ESTALE: return DRMP3_INVALID_FILE;
68277 #endif
68278 #ifdef EUCLEAN
68279 case EUCLEAN: return DRMP3_ERROR;
68280 #endif
68281 #ifdef ENOTNAM
68282 case ENOTNAM: return DRMP3_ERROR;
68283 #endif
68284 #ifdef ENAVAIL
68285 case ENAVAIL: return DRMP3_ERROR;
68286 #endif
68287 #ifdef EISNAM
68288 case EISNAM: return DRMP3_ERROR;
68289 #endif
68290 #ifdef EREMOTEIO
68291 case EREMOTEIO: return DRMP3_IO_ERROR;
68292 #endif
68293 #ifdef EDQUOT
68294 case EDQUOT: return DRMP3_NO_SPACE;
68295 #endif
68296 #ifdef ENOMEDIUM
68297 case ENOMEDIUM: return DRMP3_DOES_NOT_EXIST;
68298 #endif
68299 #ifdef EMEDIUMTYPE
68300 case EMEDIUMTYPE: return DRMP3_ERROR;
68301 #endif
68302 #ifdef ECANCELED
68303 case ECANCELED: return DRMP3_CANCELLED;
68304 #endif
68305 #ifdef ENOKEY
68306 case ENOKEY: return DRMP3_ERROR;
68307 #endif
68308 #ifdef EKEYEXPIRED
68309 case EKEYEXPIRED: return DRMP3_ERROR;
68310 #endif
68311 #ifdef EKEYREVOKED
68312 case EKEYREVOKED: return DRMP3_ERROR;
68313 #endif
68314 #ifdef EKEYREJECTED
68315 case EKEYREJECTED: return DRMP3_ERROR;
68316 #endif
68317 #ifdef EOWNERDEAD
68318 case EOWNERDEAD: return DRMP3_ERROR;
68319 #endif
68320 #ifdef ENOTRECOVERABLE
68321 case ENOTRECOVERABLE: return DRMP3_ERROR;
68322 #endif
68323 #ifdef ERFKILL
68324 case ERFKILL: return DRMP3_ERROR;
68325 #endif
68326 #ifdef EHWPOISON
68327 case EHWPOISON: return DRMP3_ERROR;
68328 #endif
68329 default: return DRMP3_ERROR;
68330 }
68331}
68332static drmp3_result drmp3_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
68333{
68334#if defined(_MSC_VER) && _MSC_VER >= 1400
68335 errno_t err;
68336#endif
68337 if (ppFile != NULL) {
68338 *ppFile = NULL;
68339 }
68340 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
68341 return DRMP3_INVALID_ARGS;
68342 }
68343#if defined(_MSC_VER) && _MSC_VER >= 1400
68344 err = fopen_s(ppFile, pFilePath, pOpenMode);
68345 if (err != 0) {
68346 return drmp3_result_from_errno(err);
68347 }
68348#else
68349#if defined(_WIN32) || defined(__APPLE__)
68350 *ppFile = fopen(pFilePath, pOpenMode);
68351#else
68352 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
68353 *ppFile = fopen64(pFilePath, pOpenMode);
68354 #else
68355 *ppFile = fopen(pFilePath, pOpenMode);
68356 #endif
68357#endif
68358 if (*ppFile == NULL) {
68359 drmp3_result result = drmp3_result_from_errno(errno);
68360 if (result == DRMP3_SUCCESS) {
68361 result = DRMP3_ERROR;
68362 }
68363 return result;
68364 }
68365#endif
68366 return DRMP3_SUCCESS;
68367}
68368#if defined(_WIN32)
68369 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
68370 #define DRMP3_HAS_WFOPEN
68371 #endif
68372#endif
68373static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drmp3_allocation_callbacks* pAllocationCallbacks)
68374{
68375 if (ppFile != NULL) {
68376 *ppFile = NULL;
68377 }
68378 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
68379 return DRMP3_INVALID_ARGS;
68380 }
68381#if defined(DRMP3_HAS_WFOPEN)
68382 {
68383 #if defined(_MSC_VER) && _MSC_VER >= 1400
68384 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
68385 if (err != 0) {
68386 return drmp3_result_from_errno(err);
68387 }
68388 #else
68389 *ppFile = _wfopen(pFilePath, pOpenMode);
68390 if (*ppFile == NULL) {
68391 return drmp3_result_from_errno(errno);
68392 }
68393 #endif
68394 (void)pAllocationCallbacks;
68395 }
68396#else
68397 {
68398 mbstate_t mbs;
68399 size_t lenMB;
68400 const wchar_t* pFilePathTemp = pFilePath;
68401 char* pFilePathMB = NULL;
68402 char pOpenModeMB[32] = {0};
68403 DRMP3_ZERO_OBJECT(&mbs);
68404 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
68405 if (lenMB == (size_t)-1) {
68406 return drmp3_result_from_errno(errno);
68407 }
68408 pFilePathMB = (char*)drmp3__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
68409 if (pFilePathMB == NULL) {
68410 return DRMP3_OUT_OF_MEMORY;
68411 }
68412 pFilePathTemp = pFilePath;
68413 DRMP3_ZERO_OBJECT(&mbs);
68414 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
68415 {
68416 size_t i = 0;
68417 for (;;) {
68418 if (pOpenMode[i] == 0) {
68419 pOpenModeMB[i] = '\0';
68420 break;
68421 }
68422 pOpenModeMB[i] = (char)pOpenMode[i];
68423 i += 1;
68424 }
68425 }
68426 *ppFile = fopen(pFilePathMB, pOpenModeMB);
68427 drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
68428 }
68429 if (*ppFile == NULL) {
68430 return DRMP3_ERROR;
68431 }
68432#endif
68433 return DRMP3_SUCCESS;
68434}
68435static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
68436{
68437 return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
68438}
68439static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin)
68440{
68441 return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
68442}
68443DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
68444{
68445 drmp3_bool32 result;
68446 FILE* pFile;
68447 if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) {
68448 return DRMP3_FALSE;
68449 }
68450 result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
68451 if (result != DRMP3_TRUE) {
68452 fclose(pFile);
68453 return result;
68454 }
68455 return DRMP3_TRUE;
68456}
68457DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
68458{
68459 drmp3_bool32 result;
68460 FILE* pFile;
68461 if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) {
68462 return DRMP3_FALSE;
68463 }
68464 result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
68465 if (result != DRMP3_TRUE) {
68466 fclose(pFile);
68467 return result;
68468 }
68469 return DRMP3_TRUE;
68470}
68471#endif
68472DRMP3_API void drmp3_uninit(drmp3* pMP3)
68473{
68474 if (pMP3 == NULL) {
68475 return;
68476 }
68477#ifndef DR_MP3_NO_STDIO
68478 if (pMP3->onRead == drmp3__on_read_stdio) {
68479 FILE* pFile = (FILE*)pMP3->pUserData;
68480 if (pFile != NULL) {
68481 fclose(pFile);
68482 pMP3->pUserData = NULL;
68483 }
68484 }
68485#endif
68486 drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
68487}
68488#if defined(DR_MP3_FLOAT_OUTPUT)
68489static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sampleCount)
68490{
68491 drmp3_uint64 i;
68492 drmp3_uint64 i4;
68493 drmp3_uint64 sampleCount4;
68494 i = 0;
68495 sampleCount4 = sampleCount >> 2;
68496 for (i4 = 0; i4 < sampleCount4; i4 += 1) {
68497 float x0 = src[i+0];
68498 float x1 = src[i+1];
68499 float x2 = src[i+2];
68500 float x3 = src[i+3];
68501 x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
68502 x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
68503 x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
68504 x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
68505 x0 = x0 * 32767.0f;
68506 x1 = x1 * 32767.0f;
68507 x2 = x2 * 32767.0f;
68508 x3 = x3 * 32767.0f;
68509 dst[i+0] = (drmp3_int16)x0;
68510 dst[i+1] = (drmp3_int16)x1;
68511 dst[i+2] = (drmp3_int16)x2;
68512 dst[i+3] = (drmp3_int16)x3;
68513 i += 4;
68514 }
68515 for (; i < sampleCount; i += 1) {
68516 float x = src[i];
68517 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
68518 x = x * 32767.0f;
68519 dst[i] = (drmp3_int16)x;
68520 }
68521}
68522#endif
68523#if !defined(DR_MP3_FLOAT_OUTPUT)
68524static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sampleCount)
68525{
68526 drmp3_uint64 i;
68527 for (i = 0; i < sampleCount; i += 1) {
68528 float x = (float)src[i];
68529 x = x * 0.000030517578125f;
68530 dst[i] = x;
68531 }
68532}
68533#endif
68534static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesToRead, void* pBufferOut)
68535{
68536 drmp3_uint64 totalFramesRead = 0;
68537 DRMP3_ASSERT(pMP3 != NULL);
68538 DRMP3_ASSERT(pMP3->onRead != NULL);
68539 while (framesToRead > 0) {
68540 drmp3_uint32 framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead);
68541 if (pBufferOut != NULL) {
68542 #if defined(DR_MP3_FLOAT_OUTPUT)
68543 float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels);
68544 float* pFramesInF32 = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
68545 DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels);
68546 #else
68547 drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalFramesRead * pMP3->channels);
68548 drmp3_int16* pFramesInS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
68549 DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels);
68550 #endif
68551 }
68552 pMP3->currentPCMFrame += framesToConsume;
68553 pMP3->pcmFramesConsumedInMP3Frame += framesToConsume;
68554 pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume;
68555 totalFramesRead += framesToConsume;
68556 framesToRead -= framesToConsume;
68557 if (framesToRead == 0) {
68558 break;
68559 }
68560 DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0);
68561 if (drmp3_decode_next_frame(pMP3) == 0) {
68562 break;
68563 }
68564 }
68565 return totalFramesRead;
68566}
68567DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut)
68568{
68569 if (pMP3 == NULL || pMP3->onRead == NULL) {
68570 return 0;
68571 }
68572#if defined(DR_MP3_FLOAT_OUTPUT)
68573 return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
68574#else
68575 {
68576 drmp3_int16 pTempS16[8192];
68577 drmp3_uint64 totalPCMFramesRead = 0;
68578 while (totalPCMFramesRead < framesToRead) {
68579 drmp3_uint64 framesJustRead;
68580 drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
68581 drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels;
68582 if (framesToReadNow > framesRemaining) {
68583 framesToReadNow = framesRemaining;
68584 }
68585 framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16);
68586 if (framesJustRead == 0) {
68587 break;
68588 }
68589 drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels);
68590 totalPCMFramesRead += framesJustRead;
68591 }
68592 return totalPCMFramesRead;
68593 }
68594#endif
68595}
68596DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut)
68597{
68598 if (pMP3 == NULL || pMP3->onRead == NULL) {
68599 return 0;
68600 }
68601#if !defined(DR_MP3_FLOAT_OUTPUT)
68602 return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
68603#else
68604 {
68605 float pTempF32[4096];
68606 drmp3_uint64 totalPCMFramesRead = 0;
68607 while (totalPCMFramesRead < framesToRead) {
68608 drmp3_uint64 framesJustRead;
68609 drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
68610 drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels;
68611 if (framesToReadNow > framesRemaining) {
68612 framesToReadNow = framesRemaining;
68613 }
68614 framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32);
68615 if (framesJustRead == 0) {
68616 break;
68617 }
68618 drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels);
68619 totalPCMFramesRead += framesJustRead;
68620 }
68621 return totalPCMFramesRead;
68622 }
68623#endif
68624}
68625static void drmp3_reset(drmp3* pMP3)
68626{
68627 DRMP3_ASSERT(pMP3 != NULL);
68628 pMP3->pcmFramesConsumedInMP3Frame = 0;
68629 pMP3->pcmFramesRemainingInMP3Frame = 0;
68630 pMP3->currentPCMFrame = 0;
68631 pMP3->dataSize = 0;
68632 pMP3->atEnd = DRMP3_FALSE;
68633 drmp3dec_init(&pMP3->decoder);
68634}
68635static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3)
68636{
68637 DRMP3_ASSERT(pMP3 != NULL);
68638 DRMP3_ASSERT(pMP3->onSeek != NULL);
68639 if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) {
68640 return DRMP3_FALSE;
68641 }
68642 drmp3_reset(pMP3);
68643 return DRMP3_TRUE;
68644}
68645static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset)
68646{
68647 drmp3_uint64 framesRead;
68648#if defined(DR_MP3_FLOAT_OUTPUT)
68649 framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL);
68650#else
68651 framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL);
68652#endif
68653 if (framesRead != frameOffset) {
68654 return DRMP3_FALSE;
68655 }
68656 return DRMP3_TRUE;
68657}
68658static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex)
68659{
68660 DRMP3_ASSERT(pMP3 != NULL);
68661 if (frameIndex == pMP3->currentPCMFrame) {
68662 return DRMP3_TRUE;
68663 }
68664 if (frameIndex < pMP3->currentPCMFrame) {
68665 if (!drmp3_seek_to_start_of_stream(pMP3)) {
68666 return DRMP3_FALSE;
68667 }
68668 }
68669 DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame);
68670 return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame));
68671}
68672static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex)
68673{
68674 drmp3_uint32 iSeekPoint;
68675 DRMP3_ASSERT(pSeekPointIndex != NULL);
68676 *pSeekPointIndex = 0;
68677 if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) {
68678 return DRMP3_FALSE;
68679 }
68680 for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) {
68681 if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) {
68682 break;
68683 }
68684 *pSeekPointIndex = iSeekPoint;
68685 }
68686 return DRMP3_TRUE;
68687}
68688static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex)
68689{
68690 drmp3_seek_point seekPoint;
68691 drmp3_uint32 priorSeekPointIndex;
68692 drmp3_uint16 iMP3Frame;
68693 drmp3_uint64 leftoverFrames;
68694 DRMP3_ASSERT(pMP3 != NULL);
68695 DRMP3_ASSERT(pMP3->pSeekPoints != NULL);
68696 DRMP3_ASSERT(pMP3->seekPointCount > 0);
68697 if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) {
68698 seekPoint = pMP3->pSeekPoints[priorSeekPointIndex];
68699 } else {
68700 seekPoint.seekPosInBytes = 0;
68701 seekPoint.pcmFrameIndex = 0;
68702 seekPoint.mp3FramesToDiscard = 0;
68703 seekPoint.pcmFramesToDiscard = 0;
68704 }
68705 if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) {
68706 return DRMP3_FALSE;
68707 }
68708 drmp3_reset(pMP3);
68709 for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) {
68710 drmp3_uint32 pcmFramesRead;
68711 drmp3d_sample_t* pPCMFrames;
68712 pPCMFrames = NULL;
68713 if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) {
68714 pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames;
68715 }
68716 pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames);
68717 if (pcmFramesRead == 0) {
68718 return DRMP3_FALSE;
68719 }
68720 }
68721 pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard;
68722 leftoverFrames = frameIndex - pMP3->currentPCMFrame;
68723 return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames);
68724}
68725DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex)
68726{
68727 if (pMP3 == NULL || pMP3->onSeek == NULL) {
68728 return DRMP3_FALSE;
68729 }
68730 if (frameIndex == 0) {
68731 return drmp3_seek_to_start_of_stream(pMP3);
68732 }
68733 if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) {
68734 return drmp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex);
68735 } else {
68736 return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex);
68737 }
68738}
68739DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount)
68740{
68741 drmp3_uint64 currentPCMFrame;
68742 drmp3_uint64 totalPCMFrameCount;
68743 drmp3_uint64 totalMP3FrameCount;
68744 if (pMP3 == NULL) {
68745 return DRMP3_FALSE;
68746 }
68747 if (pMP3->onSeek == NULL) {
68748 return DRMP3_FALSE;
68749 }
68750 currentPCMFrame = pMP3->currentPCMFrame;
68751 if (!drmp3_seek_to_start_of_stream(pMP3)) {
68752 return DRMP3_FALSE;
68753 }
68754 totalPCMFrameCount = 0;
68755 totalMP3FrameCount = 0;
68756 for (;;) {
68757 drmp3_uint32 pcmFramesInCurrentMP3Frame;
68758 pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL);
68759 if (pcmFramesInCurrentMP3Frame == 0) {
68760 break;
68761 }
68762 totalPCMFrameCount += pcmFramesInCurrentMP3Frame;
68763 totalMP3FrameCount += 1;
68764 }
68765 if (!drmp3_seek_to_start_of_stream(pMP3)) {
68766 return DRMP3_FALSE;
68767 }
68768 if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
68769 return DRMP3_FALSE;
68770 }
68771 if (pMP3FrameCount != NULL) {
68772 *pMP3FrameCount = totalMP3FrameCount;
68773 }
68774 if (pPCMFrameCount != NULL) {
68775 *pPCMFrameCount = totalPCMFrameCount;
68776 }
68777 return DRMP3_TRUE;
68778}
68779DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3)
68780{
68781 drmp3_uint64 totalPCMFrameCount;
68782 if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) {
68783 return 0;
68784 }
68785 return totalPCMFrameCount;
68786}
68787DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3)
68788{
68789 drmp3_uint64 totalMP3FrameCount;
68790 if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) {
68791 return 0;
68792 }
68793 return totalMP3FrameCount;
68794}
68795static void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart)
68796{
68797 float srcRatio;
68798 float pcmFrameCountOutF;
68799 drmp3_uint32 pcmFrameCountOut;
68800 srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate;
68801 DRMP3_ASSERT(srcRatio > 0);
68802 pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio);
68803 pcmFrameCountOut = (drmp3_uint32)pcmFrameCountOutF;
68804 *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut;
68805 *pRunningPCMFrameCount += pcmFrameCountOut;
68806}
68807typedef struct
68808{
68809 drmp3_uint64 bytePos;
68810 drmp3_uint64 pcmFrameIndex;
68811} drmp3__seeking_mp3_frame_info;
68812DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints)
68813{
68814 drmp3_uint32 seekPointCount;
68815 drmp3_uint64 currentPCMFrame;
68816 drmp3_uint64 totalMP3FrameCount;
68817 drmp3_uint64 totalPCMFrameCount;
68818 if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) {
68819 return DRMP3_FALSE;
68820 }
68821 seekPointCount = *pSeekPointCount;
68822 if (seekPointCount == 0) {
68823 return DRMP3_FALSE;
68824 }
68825 currentPCMFrame = pMP3->currentPCMFrame;
68826 if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) {
68827 return DRMP3_FALSE;
68828 }
68829 if (totalMP3FrameCount < DRMP3_SEEK_LEADING_MP3_FRAMES+1) {
68830 seekPointCount = 1;
68831 pSeekPoints[0].seekPosInBytes = 0;
68832 pSeekPoints[0].pcmFrameIndex = 0;
68833 pSeekPoints[0].mp3FramesToDiscard = 0;
68834 pSeekPoints[0].pcmFramesToDiscard = 0;
68835 } else {
68836 drmp3_uint64 pcmFramesBetweenSeekPoints;
68837 drmp3__seeking_mp3_frame_info mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES+1];
68838 drmp3_uint64 runningPCMFrameCount = 0;
68839 float runningPCMFrameCountFractionalPart = 0;
68840 drmp3_uint64 nextTargetPCMFrame;
68841 drmp3_uint32 iMP3Frame;
68842 drmp3_uint32 iSeekPoint;
68843 if (seekPointCount > totalMP3FrameCount-1) {
68844 seekPointCount = (drmp3_uint32)totalMP3FrameCount-1;
68845 }
68846 pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1);
68847 if (!drmp3_seek_to_start_of_stream(pMP3)) {
68848 return DRMP3_FALSE;
68849 }
68850 for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) {
68851 drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
68852 DRMP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize);
68853 mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize;
68854 mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount;
68855 pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL);
68856 if (pcmFramesInCurrentMP3FrameIn == 0) {
68857 return DRMP3_FALSE;
68858 }
68859 drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
68860 }
68861 nextTargetPCMFrame = 0;
68862 for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) {
68863 nextTargetPCMFrame += pcmFramesBetweenSeekPoints;
68864 for (;;) {
68865 if (nextTargetPCMFrame < runningPCMFrameCount) {
68866 pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
68867 pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
68868 pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES;
68869 pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
68870 break;
68871 } else {
68872 size_t i;
68873 drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
68874 for (i = 0; i < DRMP3_COUNTOF(mp3FrameInfo)-1; ++i) {
68875 mp3FrameInfo[i] = mp3FrameInfo[i+1];
68876 }
68877 mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize;
68878 mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount;
68879 pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL);
68880 if (pcmFramesInCurrentMP3FrameIn == 0) {
68881 pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
68882 pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
68883 pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES;
68884 pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
68885 break;
68886 }
68887 drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
68888 }
68889 }
68890 }
68891 if (!drmp3_seek_to_start_of_stream(pMP3)) {
68892 return DRMP3_FALSE;
68893 }
68894 if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
68895 return DRMP3_FALSE;
68896 }
68897 }
68898 *pSeekPointCount = seekPointCount;
68899 return DRMP3_TRUE;
68900}
68901DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints)
68902{
68903 if (pMP3 == NULL) {
68904 return DRMP3_FALSE;
68905 }
68906 if (seekPointCount == 0 || pSeekPoints == NULL) {
68907 pMP3->seekPointCount = 0;
68908 pMP3->pSeekPoints = NULL;
68909 } else {
68910 pMP3->seekPointCount = seekPointCount;
68911 pMP3->pSeekPoints = pSeekPoints;
68912 }
68913 return DRMP3_TRUE;
68914}
68915static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
68916{
68917 drmp3_uint64 totalFramesRead = 0;
68918 drmp3_uint64 framesCapacity = 0;
68919 float* pFrames = NULL;
68920 float temp[4096];
68921 DRMP3_ASSERT(pMP3 != NULL);
68922 for (;;) {
68923 drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels;
68924 drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);
68925 if (framesJustRead == 0) {
68926 break;
68927 }
68928 if (framesCapacity < totalFramesRead + framesJustRead) {
68929 drmp3_uint64 oldFramesBufferSize;
68930 drmp3_uint64 newFramesBufferSize;
68931 drmp3_uint64 newFramesCap;
68932 float* pNewFrames;
68933 newFramesCap = framesCapacity * 2;
68934 if (newFramesCap < totalFramesRead + framesJustRead) {
68935 newFramesCap = totalFramesRead + framesJustRead;
68936 }
68937 oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float);
68938 newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float);
68939 if (newFramesBufferSize > DRMP3_SIZE_MAX) {
68940 break;
68941 }
68942 pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
68943 if (pNewFrames == NULL) {
68944 drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
68945 break;
68946 }
68947 pFrames = pNewFrames;
68948 framesCapacity = newFramesCap;
68949 }
68950 DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float)));
68951 totalFramesRead += framesJustRead;
68952 if (framesJustRead != framesToReadRightNow) {
68953 break;
68954 }
68955 }
68956 if (pConfig != NULL) {
68957 pConfig->channels = pMP3->channels;
68958 pConfig->sampleRate = pMP3->sampleRate;
68959 }
68960 drmp3_uninit(pMP3);
68961 if (pTotalFrameCount) {
68962 *pTotalFrameCount = totalFramesRead;
68963 }
68964 return pFrames;
68965}
68966static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
68967{
68968 drmp3_uint64 totalFramesRead = 0;
68969 drmp3_uint64 framesCapacity = 0;
68970 drmp3_int16* pFrames = NULL;
68971 drmp3_int16 temp[4096];
68972 DRMP3_ASSERT(pMP3 != NULL);
68973 for (;;) {
68974 drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels;
68975 drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp);
68976 if (framesJustRead == 0) {
68977 break;
68978 }
68979 if (framesCapacity < totalFramesRead + framesJustRead) {
68980 drmp3_uint64 newFramesBufferSize;
68981 drmp3_uint64 oldFramesBufferSize;
68982 drmp3_uint64 newFramesCap;
68983 drmp3_int16* pNewFrames;
68984 newFramesCap = framesCapacity * 2;
68985 if (newFramesCap < totalFramesRead + framesJustRead) {
68986 newFramesCap = totalFramesRead + framesJustRead;
68987 }
68988 oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(drmp3_int16);
68989 newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(drmp3_int16);
68990 if (newFramesBufferSize > DRMP3_SIZE_MAX) {
68991 break;
68992 }
68993 pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
68994 if (pNewFrames == NULL) {
68995 drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
68996 break;
68997 }
68998 pFrames = pNewFrames;
68999 framesCapacity = newFramesCap;
69000 }
69001 DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(drmp3_int16)));
69002 totalFramesRead += framesJustRead;
69003 if (framesJustRead != framesToReadRightNow) {
69004 break;
69005 }
69006 }
69007 if (pConfig != NULL) {
69008 pConfig->channels = pMP3->channels;
69009 pConfig->sampleRate = pMP3->sampleRate;
69010 }
69011 drmp3_uninit(pMP3);
69012 if (pTotalFrameCount) {
69013 *pTotalFrameCount = totalFramesRead;
69014 }
69015 return pFrames;
69016}
69017DRMP3_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)
69018{
69019 drmp3 mp3;
69020 if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {
69021 return NULL;
69022 }
69023 return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
69024}
69025DRMP3_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)
69026{
69027 drmp3 mp3;
69028 if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {
69029 return NULL;
69030 }
69031 return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
69032}
69033DRMP3_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)
69034{
69035 drmp3 mp3;
69036 if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
69037 return NULL;
69038 }
69039 return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
69040}
69041DRMP3_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)
69042{
69043 drmp3 mp3;
69044 if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
69045 return NULL;
69046 }
69047 return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
69048}
69049#ifndef DR_MP3_NO_STDIO
69050DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
69051{
69052 drmp3 mp3;
69053 if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
69054 return NULL;
69055 }
69056 return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
69057}
69058DRMP3_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)
69059{
69060 drmp3 mp3;
69061 if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
69062 return NULL;
69063 }
69064 return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
69065}
69066#endif
69067DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks)
69068{
69069 if (pAllocationCallbacks != NULL) {
69070 return drmp3__malloc_from_callbacks(sz, pAllocationCallbacks);
69071 } else {
69072 return drmp3__malloc_default(sz, NULL);
69073 }
69074}
69075DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks)
69076{
69077 if (pAllocationCallbacks != NULL) {
69078 drmp3__free_from_callbacks(p, pAllocationCallbacks);
69079 } else {
69080 drmp3__free_default(p, NULL);
69081 }
69082}
69083#endif
69084/* dr_mp3_c end */
69085#endif /* DRMP3_IMPLEMENTATION */
69086#endif /* MA_NO_MP3 */
69087
69088
69089/* End globally disabled warnings. */
69090#if defined(_MSC_VER)
69091 #pragma warning(pop)
69092#endif
69093
69094#endif /* miniaudio_c */
69095#endif /* MINIAUDIO_IMPLEMENTATION */
69096
69097/*
69098RELEASE NOTES - VERSION 0.10.x
69099==============================
69100Version 0.10 includes major API changes and refactoring, mostly concerned with the data conversion system. Data conversion is performed internally to convert
69101audio data between the format requested when initializing the `ma_device` object and the format of the internal device used by the backend. The same applies
69102to the `ma_decoder` object. The previous design has several design flaws and missing features which necessitated a complete redesign.
69103
69104
69105Changes to Data Conversion
69106--------------------------
69107The previous data conversion system used callbacks to deliver input data for conversion. This design works well in some specific situations, but in other
69108situations it has some major readability and maintenance issues. The decision was made to replace this with a more iterative approach where you just pass in a
69109pointer to the input data directly rather than dealing with a callback.
69110
69111The following are the data conversion APIs that have been removed and their replacements:
69112
69113 - ma_format_converter -> ma_convert_pcm_frames_format()
69114 - ma_channel_router -> ma_channel_converter
69115 - ma_src -> ma_resampler
69116 - ma_pcm_converter -> ma_data_converter
69117
69118The previous conversion APIs accepted a callback in their configs. There are no longer any callbacks to deal with. Instead you just pass the data into the
69119`*_process_pcm_frames()` function as a pointer to a buffer.
69120
69121The simplest aspect of data conversion is sample format conversion. To convert between two formats, just call `ma_convert_pcm_frames_format()`. Channel
69122conversion is also simple which you can do with `ma_channel_converter` via `ma_channel_converter_process_pcm_frames()`.
69123
69124Resampling is more complicated because the number of output frames that are processed is different to the number of input frames that are consumed. When you
69125call `ma_resampler_process_pcm_frames()` you need to pass in the number of input frames available for processing and the number of output frames you want to
69126output. Upon returning they will receive the number of input frames that were consumed and the number of output frames that were generated.
69127
69128The `ma_data_converter` API is a wrapper around format, channel and sample rate conversion and handles all of the data conversion you'll need which probably
69129makes it the best option if you need to do data conversion.
69130
69131In addition to changes to the API design, a few other changes have been made to the data conversion pipeline:
69132
69133 - The sinc resampler has been removed. This was completely broken and never actually worked properly.
69134 - The linear resampler now uses low-pass filtering to remove aliasing. The quality of the low-pass filter can be controlled via the resampler config with the
69135 `lpfOrder` option, which has a maximum value of MA_MAX_FILTER_ORDER.
69136 - Data conversion now supports s16 natively which runs through a fixed point pipeline. Previously everything needed to be converted to floating point before
69137 processing, whereas now both s16 and f32 are natively supported. Other formats still require conversion to either s16 or f32 prior to processing, however
69138 `ma_data_converter` will handle this for you.
69139
69140
69141Custom Memory Allocators
69142------------------------
69143miniaudio has always supported macro level customization for memory allocation via MA_MALLOC, MA_REALLOC and MA_FREE, however some scenarios require more
69144flexibility by allowing a user data pointer to be passed to the custom allocation routines. Support for this has been added to version 0.10 via the
69145`ma_allocation_callbacks` structure. Anything making use of heap allocations has been updated to accept this new structure.
69146
69147The `ma_context_config` structure has been updated with a new member called `allocationCallbacks`. Leaving this set to it's defaults returned by
69148`ma_context_config_init()` will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. Likewise, The `ma_decoder_config` structure has been updated in the same
69149way, and leaving everything as-is after `ma_decoder_config_init()` will cause it to use the same defaults.
69150
69151The following APIs have been updated to take a pointer to a `ma_allocation_callbacks` object. Setting this parameter to NULL will cause it to use defaults.
69152Otherwise they will use the relevant callback in the structure.
69153
69154 - ma_malloc()
69155 - ma_realloc()
69156 - ma_free()
69157 - ma_aligned_malloc()
69158 - ma_aligned_free()
69159 - ma_rb_init() / ma_rb_init_ex()
69160 - ma_pcm_rb_init() / ma_pcm_rb_init_ex()
69161
69162Note that you can continue to use MA_MALLOC, MA_REALLOC and MA_FREE as per normal. These will continue to be used by default if you do not specify custom
69163allocation callbacks.
69164
69165
69166Buffer and Period Configuration Changes
69167---------------------------------------
69168The way in which the size of the internal buffer and periods are specified in the device configuration have changed. In previous versions, the config variables
69169`bufferSizeInFrames` and `bufferSizeInMilliseconds` defined the size of the entire buffer, with the size of a period being the size of this variable divided by
69170the period count. This became confusing because people would expect the value of `bufferSizeInFrames` or `bufferSizeInMilliseconds` to independantly determine
69171latency, when in fact it was that value divided by the period count that determined it. These variables have been removed and replaced with new ones called
69172`periodSizeInFrames` and `periodSizeInMilliseconds`.
69173
69174These new configuration variables work in the same way as their predecessors in that if one is set to 0, the other will be used, but the main difference is
69175that you now set these to you desired latency rather than the size of the entire buffer. The benefit of this is that it's much easier and less confusing to
69176configure latency.
69177
69178The following unused APIs have been removed:
69179
69180 ma_get_default_buffer_size_in_milliseconds()
69181 ma_get_default_buffer_size_in_frames()
69182
69183The following macros have been removed:
69184
69185 MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY
69186 MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE
69187
69188
69189Other API Changes
69190-----------------
69191Other less major API changes have also been made in version 0.10.
69192
69193`ma_device_set_stop_callback()` has been removed. If you require a stop callback, you must now set it via the device config just like the data callback.
69194
69195The `ma_sine_wave` API has been replaced with a more general API called `ma_waveform`. This supports generation of different types of waveforms, including
69196sine, square, triangle and sawtooth. Use `ma_waveform_init()` in place of `ma_sine_wave_init()` to initialize the waveform object. This takes a configuration
69197object called `ma_waveform_config` which defines the properties of the waveform. Use `ma_waveform_config_init()` to initialize a `ma_waveform_config` object.
69198Use `ma_waveform_read_pcm_frames()` in place of `ma_sine_wave_read_f32()` and `ma_sine_wave_read_f32_ex()`.
69199
69200`ma_convert_frames()` and `ma_convert_frames_ex()` have been changed. Both of these functions now take a new parameter called `frameCountOut` which specifies
69201the size of the output buffer in PCM frames. This has been added for safety. In addition to this, the parameters for `ma_convert_frames_ex()` have changed to
69202take a pointer to a `ma_data_converter_config` object to specify the input and output formats to convert between. This was done to make it more flexible, to
69203prevent the parameter list getting too long, and to prevent API breakage whenever a new conversion property is added.
69204
69205`ma_calculate_frame_count_after_src()` has been renamed to `ma_calculate_frame_count_after_resampling()` for consistency with the new `ma_resampler` API.
69206
69207
69208Filters
69209-------
69210The following filters have been added:
69211
69212 |-------------|-------------------------------------------------------------------|
69213 | API | Description |
69214 |-------------|-------------------------------------------------------------------|
69215 | ma_biquad | Biquad filter (transposed direct form 2) |
69216 | ma_lpf1 | First order low-pass filter |
69217 | ma_lpf2 | Second order low-pass filter |
69218 | ma_lpf | High order low-pass filter (Butterworth) |
69219 | ma_hpf1 | First order high-pass filter |
69220 | ma_hpf2 | Second order high-pass filter |
69221 | ma_hpf | High order high-pass filter (Butterworth) |
69222 | ma_bpf2 | Second order band-pass filter |
69223 | ma_bpf | High order band-pass filter |
69224 | ma_peak2 | Second order peaking filter |
69225 | ma_notch2 | Second order notching filter |
69226 | ma_loshelf2 | Second order low shelf filter |
69227 | ma_hishelf2 | Second order high shelf filter |
69228 |-------------|-------------------------------------------------------------------|
69229
69230These filters all support 32-bit floating point and 16-bit signed integer formats natively. Other formats need to be converted beforehand.
69231
69232
69233Sine, Square, Triangle and Sawtooth Waveforms
69234---------------------------------------------
69235Previously miniaudio supported only sine wave generation. This has now been generalized to support sine, square, triangle and sawtooth waveforms. The old
69236`ma_sine_wave` API has been removed and replaced with the `ma_waveform` API. Use `ma_waveform_config_init()` to initialize a config object, and then pass it
69237into `ma_waveform_init()`. Then use `ma_waveform_read_pcm_frames()` to read PCM data.
69238
69239
69240Noise Generation
69241----------------
69242A noise generation API has been added. This is used via the `ma_noise` API. Currently white, pink and Brownian noise is supported. The `ma_noise` API is
69243similar to the waveform API. Use `ma_noise_config_init()` to initialize a config object, and then pass it into `ma_noise_init()` to initialize a `ma_noise`
69244object. Then use `ma_noise_read_pcm_frames()` to read PCM data.
69245
69246
69247Miscellaneous Changes
69248---------------------
69249The MA_NO_STDIO option has been removed. This would disable file I/O APIs, however this has proven to be too hard to maintain for it's perceived value and was
69250therefore removed.
69251
69252Internal functions have all been made static where possible. If you get warnings about unused functions, please submit a bug report.
69253
69254The `ma_device` structure is no longer defined as being aligned to MA_SIMD_ALIGNMENT. This resulted in a possible crash when allocating a `ma_device` object on
69255the heap, but not aligning it to MA_SIMD_ALIGNMENT. This crash would happen due to the compiler seeing the alignment specified on the structure and assuming it
69256was always aligned as such and thinking it was safe to emit alignment-dependant SIMD instructions. Since miniaudio's philosophy is for things to just work,
69257this has been removed from all structures.
69258
69259Results codes have been overhauled. Unnecessary result codes have been removed, and some have been renumbered for organisation purposes. If you are are binding
69260maintainer you will need to update your result codes. Support has also been added for retrieving a human readable description of a given result code via the
69261`ma_result_description()` API.
69262
69263ALSA: The automatic format conversion, channel conversion and resampling performed by ALSA is now disabled by default as they were causing some compatibility
69264issues with certain devices and configurations. These can be individually enabled via the device config:
69265
69266 ```c
69267 deviceConfig.alsa.noAutoFormat = MA_TRUE;
69268 deviceConfig.alsa.noAutoChannels = MA_TRUE;
69269 deviceConfig.alsa.noAutoResample = MA_TRUE;
69270 ```
69271*/
69272
69273/*
69274RELEASE NOTES - VERSION 0.9.x
69275=============================
69276Version 0.9 includes major API changes, centered mostly around full-duplex and the rebrand to "miniaudio". Before I go into detail about the major changes I
69277would like to apologize. I know it's annoying dealing with breaking API changes, but I think it's best to get these changes out of the way now while the
69278library is still relatively young and unknown.
69279
69280There's been a lot of refactoring with this release so there's a good chance a few bugs have been introduced. I apologize in advance for this. You may want to
69281hold off on upgrading for the short term if you're worried. If mini_al v0.8.14 works for you, and you don't need full-duplex support, you can avoid upgrading
69282(though you won't be getting future bug fixes).
69283
69284
69285Rebranding to "miniaudio"
69286-------------------------
69287The decision was made to rename mini_al to miniaudio. Don't worry, it's the same project. The reason for this is simple:
69288
692891) Having the word "audio" in the title makes it immediately clear that the library is related to audio; and
692902) I don't like the look of the underscore.
69291
69292This rebrand has necessitated a change in namespace from "mal" to "ma". I know this is annoying, and I apologize, but it's better to get this out of the road
69293now rather than later. Also, since there are necessary API changes for full-duplex support I think it's better to just get the namespace change over and done
69294with at the same time as the full-duplex changes. I'm hoping this will be the last of the major API changes. Fingers crossed!
69295
69296The implementation define is now "#define MINIAUDIO_IMPLEMENTATION". You can also use "#define MA_IMPLEMENTATION" if that's your preference.
69297
69298
69299Full-Duplex Support
69300-------------------
69301The major feature added to version 0.9 is full-duplex. This has necessitated a few API changes.
69302
693031) The data callback has now changed. Previously there was one type of callback for playback and another for capture. I wanted to avoid a third callback just
69304 for full-duplex so the decision was made to break this API and unify the callbacks. Now, there is just one callback which is the same for all three modes
69305 (playback, capture, duplex). The new callback looks like the following:
69306
69307 void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
69308
69309 This callback allows you to move data straight out of the input buffer and into the output buffer in full-duplex mode. In playback-only mode, pInput will be
69310 null. Likewise, pOutput will be null in capture-only mode. The sample count is no longer returned from the callback since it's not necessary for miniaudio
69311 anymore.
69312
693132) The device config needed to change in order to support full-duplex. Full-duplex requires the ability to allow the client to choose a different PCM format
69314 for the playback and capture sides. The old ma_device_config object simply did not allow this and needed to change. With these changes you now specify the
69315 device ID, format, channels, channel map and share mode on a per-playback and per-capture basis (see example below). The sample rate must be the same for
69316 playback and capture.
69317
69318 Since the device config API has changed I have also decided to take the opportunity to simplify device initialization. Now, the device ID, device type and
69319 callback user data are set in the config. ma_device_init() is now simplified down to taking just the context, device config and a pointer to the device
69320 object being initialized. The rationale for this change is that it just makes more sense to me that these are set as part of the config like everything
69321 else.
69322
69323 Example device initialization:
69324
69325 ma_device_config config = ma_device_config_init(ma_device_type_duplex); // Or ma_device_type_playback or ma_device_type_capture.
69326 config.playback.pDeviceID = &myPlaybackDeviceID; // Or NULL for the default playback device.
69327 config.playback.format = ma_format_f32;
69328 config.playback.channels = 2;
69329 config.capture.pDeviceID = &myCaptureDeviceID; // Or NULL for the default capture device.
69330 config.capture.format = ma_format_s16;
69331 config.capture.channels = 1;
69332 config.sampleRate = 44100;
69333 config.dataCallback = data_callback;
69334 config.pUserData = &myUserData;
69335
69336 result = ma_device_init(&myContext, &config, &device);
69337 if (result != MA_SUCCESS) {
69338 ... handle error ...
69339 }
69340
69341 Note that the "onDataCallback" member of ma_device_config has been renamed to "dataCallback". Also, "onStopCallback" has been renamed to "stopCallback".
69342
69343This is the first pass for full-duplex and there is a known bug. You will hear crackling on the following backends when sample rate conversion is required for
69344the playback device:
69345 - Core Audio
69346 - JACK
69347 - AAudio
69348 - OpenSL
69349 - WebAudio
69350
69351In addition to the above, not all platforms have been absolutely thoroughly tested simply because I lack the hardware for such thorough testing. If you
69352experience a bug, an issue report on GitHub or an email would be greatly appreciated (and a sample program that reproduces the issue if possible).
69353
69354
69355Other API Changes
69356-----------------
69357In addition to the above, the following API changes have been made:
69358
69359- The log callback is no longer passed to ma_context_config_init(). Instead you need to set it manually after initialization.
69360- The onLogCallback member of ma_context_config has been renamed to "logCallback".
69361- The log callback now takes a logLevel parameter. The new callback looks like: void log_callback(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
69362 - You can use ma_log_level_to_string() to convert the logLevel to human readable text if you want to log it.
69363- Some APIs have been renamed:
69364 - mal_decoder_read() -> ma_decoder_read_pcm_frames()
69365 - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame()
69366 - mal_sine_wave_read() -> ma_sine_wave_read_f32()
69367 - mal_sine_wave_read_ex() -> ma_sine_wave_read_f32_ex()
69368- Some APIs have been removed:
69369 - mal_device_get_buffer_size_in_bytes()
69370 - mal_device_set_recv_callback()
69371 - mal_device_set_send_callback()
69372 - mal_src_set_input_sample_rate()
69373 - mal_src_set_output_sample_rate()
69374- Error codes have been rearranged. If you're a binding maintainer you will need to update.
69375- The ma_backend enums have been rearranged to priority order. The rationale for this is to simplify automatic backend selection and to make it easier to see
69376 the priority. If you're a binding maintainer you will need to update.
69377- ma_dsp has been renamed to ma_pcm_converter. The rationale for this change is that I'm expecting "ma_dsp" to conflict with some future planned high-level
69378 APIs.
69379- For functions that take a pointer/count combo, such as ma_decoder_read_pcm_frames(), the parameter order has changed so that the pointer comes before the
69380 count. The rationale for this is to keep it consistent with things like memcpy().
69381
69382
69383Miscellaneous Changes
69384---------------------
69385The following miscellaneous changes have also been made.
69386
69387- The AAudio backend has been added for Android 8 and above. This is Android's new "High-Performance Audio" API. (For the record, this is one of the nicest
69388 audio APIs out there, just behind the BSD audio APIs).
69389- The WebAudio backend has been added. This is based on ScriptProcessorNode. This removes the need for SDL.
69390- The SDL and OpenAL backends have been removed. These were originally implemented to add support for platforms for which miniaudio was not explicitly
69391 supported. These are no longer needed and have therefore been removed.
69392- Device initialization now fails if the requested share mode is not supported. If you ask for exclusive mode, you either get an exclusive mode device, or an
69393 error. The rationale for this change is to give the client more control over how to handle cases when the desired shared mode is unavailable.
69394- A lock-free ring buffer API has been added. There are two varients of this. "ma_rb" operates on bytes, whereas "ma_pcm_rb" operates on PCM frames.
69395- The library is now licensed as a choice of Public Domain (Unlicense) _or_ MIT-0 (No Attribution) which is the same as MIT, but removes the attribution
69396 requirement. The rationale for this is to support countries that don't recognize public domain.
69397*/
69398
69399/*
69400REVISION HISTORY
69401================
69402v0.10.40 - 2021-07-23
69403 - Fix a bug when converting from stereo to mono.
69404 - PulseAudio: Fix a glitch when pausing and resuming a device.
69405
69406v0.10.39 - 2021-07-20
69407 - Core Audio: Fix a deadlock when the default device is changed.
69408 - Core Audio: Fix compilation errors on macOS and iOS.
69409 - PulseAudio: Fix a bug where the stop callback is not fired when a device is unplugged.
69410 - PulseAudio: Fix a null pointer dereference.
69411
69412v0.10.38 - 2021-07-14
69413 - Fix a linking error when MA_DEBUG_OUTPUT is not enabled.
69414 - Fix an error where ma_log_postv() does not return a value.
69415 - OpenSL: Fix a bug with setting of stream types and recording presets.
69416
694170.10.37 - 2021-07-06
69418 - Fix a bug with log message formatting.
69419 - Fix build when compiling with MA_NO_THREADING.
69420 - Minor updates to channel mapping.
69421
694220.10.36 - 2021-07-03
69423 - Add support for custom decoding backends.
69424 - Fix some bugs with the Vorbis decoder.
69425 - PulseAudio: Fix a bug with channel mapping.
69426 - PulseAudio: Fix a bug where miniaudio does not fall back to a supported format when PulseAudio
69427 defaults to a format not known to miniaudio.
69428 - OpenSL: Fix a crash when initializing a capture device when a recording preset other than the
69429 default is specified.
69430 - Silence some warnings when compiling with MA_DEBUG_OUTPUT
69431 - Improvements to logging. See the `ma_log` API for details. The logCallback variable used by
69432 ma_context has been deprecated and will be replaced with the new system in version 0.11.
69433 - Initialize an `ma_log` object with `ma_log_init()`.
69434 - Register a callback with `ma_log_register_callback()`.
69435 - In the context config, set `pLog` to your `ma_log` object and stop using `logCallback`.
69436 - Prep work for some upcoming changes to data sources. These changes are still compatible with
69437 existing code, however code will need to be updated in preparation for version 0.11 which will
69438 be breaking. You should make these changes now for any custom data sources:
69439 - Change your base data source object from `ma_data_source_callbacks` to `ma_data_source_base`.
69440 - Call `ma_data_source_init()` for your base object in your custom data source's initialization
69441 routine. This takes a config object which includes a pointer to a vtable which is now where
69442 your custom callbacks are defined.
69443 - Call `ma_data_source_uninit()` in your custom data source's uninitialization routine. This
69444 doesn't currently do anything, but it placeholder in case some future uninitialization code
69445 is required to be added at a later date.
69446
69447v0.10.35 - 2021-04-27
69448 - Fix the C++ build.
69449
69450v0.10.34 - 2021-04-26
69451 - WASAPI: Fix a bug where a result code is not getting checked at initialization time.
69452 - WASAPI: Bug fixes for loopback mode.
69453 - ALSA: Fix a possible deadlock when stopping devices.
69454 - Mark devices as default on the null backend.
69455
69456v0.10.33 - 2021-04-04
69457 - Core Audio: Fix a memory leak.
69458 - Core Audio: Fix a bug where the performance profile is not being used by playback devices.
69459 - JACK: Fix loading of 64-bit JACK on Windows.
69460 - Fix a calculation error and add a safety check to the following APIs to prevent a division by zero:
69461 - ma_calculate_buffer_size_in_milliseconds_from_frames()
69462 - ma_calculate_buffer_size_in_frames_from_milliseconds()
69463 - Fix compilation errors relating to c89atomic.
69464 - Update FLAC decoder.
69465
69466v0.10.32 - 2021-02-23
69467 - WASAPI: Fix a deadlock in exclusive mode.
69468 - WASAPI: No longer return an error from ma_context_get_device_info() when an exclusive mode format
69469 cannot be retrieved.
69470 - WASAPI: Attempt to fix some bugs with device uninitialization.
69471 - PulseAudio: Yet another refactor, this time to remove the dependency on `pa_threaded_mainloop`.
69472 - Web Audio: Fix a bug on Chrome and any other browser using the same engine.
69473 - Web Audio: Automatically start the device on some user input if the device has been started. This
69474 is to work around Google's policy of not starting audio if no user input has yet been performed.
69475 - Fix a bug where thread handles are not being freed.
69476 - Fix some static analysis warnings in FLAC, WAV and MP3 decoders.
69477 - Fix a warning due to referencing _MSC_VER when it is undefined.
69478 - Update to latest version of c89atomic.
69479 - Internal refactoring to migrate over to the new backend callback system for the following backends:
69480 - PulseAudio
69481 - ALSA
69482 - Core Audio
69483 - AAudio
69484 - OpenSL|ES
69485 - OSS
69486 - audio(4)
69487 - sndio
69488
69489v0.10.31 - 2021-01-17
69490 - Make some functions const correct.
69491 - Update ma_data_source_read_pcm_frames() to initialize pFramesRead to 0 for safety.
69492 - Add the MA_ATOMIC annotation for use with variables that should be used atomically and remove unnecessary volatile qualifiers.
69493 - Add support for enabling only specific backends at compile time. This is the reverse of the pre-existing system. With the new
69494 system, all backends are first disabled with `MA_ENABLE_ONLY_SPECIFIC_BACKENDS`, which is then followed with `MA_ENABLE_*`. The
69495 old system where you disable backends with `MA_NO_*` still exists and is still the default.
69496
69497v0.10.30 - 2021-01-10
69498 - Fix a crash in ma_audio_buffer_read_pcm_frames().
69499 - Update spinlock APIs to take a volatile parameter as input.
69500 - Silence some unused parameter warnings.
69501 - Fix a warning on GCC when compiling as C++.
69502
69503v0.10.29 - 2020-12-26
69504 - Fix some subtle multi-threading bugs on non-x86 platforms.
69505 - Fix a bug resulting in superfluous memory allocations when enumerating devices.
69506 - Core Audio: Fix a compilation error when compiling for iOS.
69507
69508v0.10.28 - 2020-12-16
69509 - Fix a crash when initializing a POSIX thread.
69510 - OpenSL|ES: Respect the MA_NO_RUNTIME_LINKING option.
69511
69512v0.10.27 - 2020-12-04
69513 - Add support for dynamically configuring some properties of `ma_noise` objects post-initialization.
69514 - Add support for configuring the channel mixing mode in the device config.
69515 - Fix a bug with simple channel mixing mode (drop or silence excess channels).
69516 - Fix some bugs with trying to access uninitialized variables.
69517 - Fix some errors with stopping devices for synchronous backends where the backend's stop callback would get fired twice.
69518 - Fix a bug in the decoder due to using an uninitialized variable.
69519 - Fix some data race errors.
69520
69521v0.10.26 - 2020-11-24
69522 - WASAPI: Fix a bug where the exclusive mode format may not be retrieved correctly due to accessing freed memory.
69523 - Fix a bug with ma_waveform where glitching occurs after changing frequency.
69524 - Fix compilation with OpenWatcom.
69525 - Fix compilation with TCC.
69526 - Fix compilation with Digital Mars.
69527 - Fix compilation warnings.
69528 - Remove bitfields from public structures to aid in binding maintenance.
69529
69530v0.10.25 - 2020-11-15
69531 - PulseAudio: Fix a bug where the stop callback isn't fired.
69532 - WebAudio: Fix an error that occurs when Emscripten increases the size of it's heap.
69533 - Custom Backends: Change the onContextInit and onDeviceInit callbacks to take a parameter which is a pointer to the config that was
69534 passed into ma_context_init() and ma_device_init(). This replaces the deviceType parameter of onDeviceInit.
69535 - Fix compilation warnings on older versions of GCC.
69536
69537v0.10.24 - 2020-11-10
69538 - Fix a bug where initialization of a backend can fail due to some bad state being set from a prior failed attempt at initializing a
69539 lower priority backend.
69540
69541v0.10.23 - 2020-11-09
69542 - AAudio: Add support for configuring a playback stream's usage.
69543 - Fix a compilation error when all built-in asynchronous backends are disabled at compile time.
69544 - Fix compilation errors when compiling as C++.
69545
69546v0.10.22 - 2020-11-08
69547 - Add support for custom backends.
69548 - Add support for detecting default devices during device enumeration and with `ma_context_get_device_info()`.
69549 - Refactor to the PulseAudio backend. This simplifies the implementation and fixes a capture bug.
69550 - ALSA: Fix a bug in `ma_context_get_device_info()` where the PCM handle is left open in the event of an error.
69551 - Core Audio: Further improvements to sample rate selection.
69552 - Core Audio: Fix some bugs with capture mode.
69553 - OpenSL: Add support for configuring stream types and recording presets.
69554 - AAudio: Add support for configuring content types and input presets.
69555 - Fix bugs in `ma_decoder_init_file*()` where the file handle is not closed after a decoding error.
69556 - Fix some compilation warnings on GCC and Clang relating to the Speex resampler.
69557 - Fix a compilation error for the Linux build when the ALSA and JACK backends are both disabled.
69558 - Fix a compilation error for the BSD build.
69559 - Fix some compilation errors on older versions of GCC.
69560 - Add documentation for `MA_NO_RUNTIME_LINKING`.
69561
69562v0.10.21 - 2020-10-30
69563 - Add ma_is_backend_enabled() and ma_get_enabled_backends() for retrieving enabled backends at run-time.
69564 - WASAPI: Fix a copy and paste bug relating to loopback mode.
69565 - Core Audio: Fix a bug when using multiple contexts.
69566 - Core Audio: Fix a compilation warning.
69567 - Core Audio: Improvements to sample rate selection.
69568 - Core Audio: Improvements to format/channels/rate selection when requesting defaults.
69569 - Core Audio: Add notes regarding the Apple notarization process.
69570 - Fix some bugs due to null pointer dereferences.
69571
69572v0.10.20 - 2020-10-06
69573 - Fix build errors with UWP.
69574 - Minor documentation updates.
69575
69576v0.10.19 - 2020-09-22
69577 - WASAPI: Return an error when exclusive mode is requested, but the native format is not supported by miniaudio.
69578 - Fix a bug where ma_decoder_seek_to_pcm_frames() never returns MA_SUCCESS even though it was successful.
69579 - Store the sample rate in the `ma_lpf` and `ma_hpf` structures.
69580
69581v0.10.18 - 2020-08-30
69582 - Fix build errors with VC6.
69583 - Fix a bug in channel converter for s32 format.
69584 - Change channel converter configs to use the default channel map instead of a blank channel map when no channel map is specified when initializing the
69585 config. This fixes an issue where the optimized mono expansion path would never get used.
69586 - Use a more appropriate default format for FLAC decoders. This will now use ma_format_s16 when the FLAC is encoded as 16-bit.
69587 - Update FLAC decoder.
69588 - Update links to point to the new repository location (https://github.com/mackron/miniaudio).
69589
69590v0.10.17 - 2020-08-28
69591 - Fix an error where the WAV codec is incorrectly excluded from the build depending on which compile time options are set.
69592 - Fix a bug in ma_audio_buffer_read_pcm_frames() where it isn't returning the correct number of frames processed.
69593 - Fix compilation error on Android.
69594 - Core Audio: Fix a bug with full-duplex mode.
69595 - Add ma_decoder_get_cursor_in_pcm_frames().
69596 - Update WAV codec.
69597
69598v0.10.16 - 2020-08-14
69599 - WASAPI: Fix a potential crash due to using an uninitialized variable.
69600 - OpenSL: Enable runtime linking.
69601 - OpenSL: Fix a multithreading bug when initializing and uninitializing multiple contexts at the same time.
69602 - iOS: Improvements to device enumeration.
69603 - Fix a crash in ma_data_source_read_pcm_frames() when the output frame count parameter is NULL.
69604 - Fix a bug in ma_data_source_read_pcm_frames() where looping doesn't work.
69605 - Fix some compilation warnings on Windows when both DirectSound and WinMM are disabled.
69606 - Fix some compilation warnings when no decoders are enabled.
69607 - Add ma_audio_buffer_get_available_frames().
69608 - Add ma_decoder_get_available_frames().
69609 - Add sample rate to ma_data_source_get_data_format().
69610 - Change volume APIs to take 64-bit frame counts.
69611 - Updates to documentation.
69612
69613v0.10.15 - 2020-07-15
69614 - Fix a bug when converting bit-masked channel maps to miniaudio channel maps. This affects the WASAPI and OpenSL backends.
69615
69616v0.10.14 - 2020-07-14
69617 - Fix compilation errors on Android.
69618 - Fix compilation errors with -march=armv6.
69619 - Updates to the documentation.
69620
69621v0.10.13 - 2020-07-11
69622 - Fix some potential buffer overflow errors with channel maps when channel counts are greater than MA_MAX_CHANNELS.
69623 - Fix compilation error on Emscripten.
69624 - Silence some unused function warnings.
69625 - Increase the default buffer size on the Web Audio backend. This fixes glitching issues on some browsers.
69626 - Bring FLAC decoder up-to-date with dr_flac.
69627 - Bring MP3 decoder up-to-date with dr_mp3.
69628
69629v0.10.12 - 2020-07-04
69630 - Fix compilation errors on the iOS build.
69631
69632v0.10.11 - 2020-06-28
69633 - Fix some bugs with device tracking on Core Audio.
69634 - Updates to documentation.
69635
69636v0.10.10 - 2020-06-26
69637 - Add include guard for the implementation section.
69638 - Mark ma_device_sink_info_callback() as static.
69639 - Fix compilation errors with MA_NO_DECODING and MA_NO_ENCODING.
69640 - Fix compilation errors with MA_NO_DEVICE_IO
69641
69642v0.10.9 - 2020-06-24
69643 - Amalgamation of dr_wav, dr_flac and dr_mp3. With this change, including the header section of these libraries before the implementation of miniaudio is no
69644 longer required. Decoding of WAV, FLAC and MP3 should be supported seamlessly without any additional libraries. Decoders can be excluded from the build
69645 with the following options:
69646 - MA_NO_WAV
69647 - MA_NO_FLAC
69648 - MA_NO_MP3
69649 If you get errors about multiple definitions you need to either enable the options above, move the implementation of dr_wav, dr_flac and/or dr_mp3 to before
69650 the implementation of miniaudio, or update dr_wav, dr_flac and/or dr_mp3.
69651 - Changes to the internal atomics library. This has been replaced with c89atomic.h which is embedded within this file.
69652 - Fix a bug when a decoding backend reports configurations outside the limits of miniaudio's decoder abstraction.
69653 - Fix the UWP build.
69654 - Fix the Core Audio build.
69655 - Fix the -std=c89 build on GCC.
69656
69657v0.10.8 - 2020-06-22
69658 - Remove dependency on ma_context from mutexes.
69659 - Change ma_data_source_read_pcm_frames() to return a result code and output the frames read as an output parameter.
69660 - Change ma_data_source_seek_pcm_frames() to return a result code and output the frames seeked as an output parameter.
69661 - Change ma_audio_buffer_unmap() to return MA_AT_END when the end has been reached. This should be considered successful.
69662 - Change playback.pDeviceID and capture.pDeviceID to constant pointers in ma_device_config.
69663 - Add support for initializing decoders from a virtual file system object. This is achieved via the ma_vfs API and allows the application to customize file
69664 IO for the loading and reading of raw audio data. Passing in NULL for the VFS will use defaults. New APIs:
69665 - ma_decoder_init_vfs()
69666 - ma_decoder_init_vfs_wav()
69667 - ma_decoder_init_vfs_flac()
69668 - ma_decoder_init_vfs_mp3()
69669 - ma_decoder_init_vfs_vorbis()
69670 - ma_decoder_init_vfs_w()
69671 - ma_decoder_init_vfs_wav_w()
69672 - ma_decoder_init_vfs_flac_w()
69673 - ma_decoder_init_vfs_mp3_w()
69674 - ma_decoder_init_vfs_vorbis_w()
69675 - Add support for memory mapping to ma_data_source.
69676 - ma_data_source_map()
69677 - ma_data_source_unmap()
69678 - Add ma_offset_pcm_frames_ptr() and ma_offset_pcm_frames_const_ptr() which can be used for offsetting a pointer by a specified number of PCM frames.
69679 - Add initial implementation of ma_yield() which is useful for spin locks which will be used in some upcoming work.
69680 - Add documentation for log levels.
69681 - The ma_event API has been made public in preparation for some uncoming work.
69682 - Fix a bug in ma_decoder_seek_to_pcm_frame() where the internal sample rate is not being taken into account for determining the seek location.
69683 - Fix some bugs with the linear resampler when dynamically changing the sample rate.
69684 - Fix compilation errors with MA_NO_DEVICE_IO.
69685 - Fix some warnings with GCC and -std=c89.
69686 - Fix some formatting warnings with GCC and -Wall and -Wpedantic.
69687 - Fix some warnings with VC6.
69688 - Minor optimization to ma_copy_pcm_frames(). This is now a no-op when the input and output buffers are the same.
69689
69690v0.10.7 - 2020-05-25
69691 - Fix a compilation error in the C++ build.
69692 - Silence a warning.
69693
69694v0.10.6 - 2020-05-24
69695 - Change ma_clip_samples_f32() and ma_clip_pcm_frames_f32() to take a 64-bit sample/frame count.
69696 - Change ma_zero_pcm_frames() to clear to 128 for ma_format_u8.
69697 - Add ma_silence_pcm_frames() which replaces ma_zero_pcm_frames(). ma_zero_pcm_frames() will be removed in version 0.11.
69698 - Add support for u8, s24 and s32 formats to ma_channel_converter.
69699 - Add compile-time and run-time version querying.
69700 - MA_VERSION_MINOR
69701 - MA_VERSION_MAJOR
69702 - MA_VERSION_REVISION
69703 - MA_VERSION_STRING
69704 - ma_version()
69705 - ma_version_string()
69706 - Add ma_audio_buffer for reading raw audio data directly from memory.
69707 - Fix a bug in shuffle mode in ma_channel_converter.
69708 - Fix compilation errors in certain configurations for ALSA and PulseAudio.
69709 - The data callback now initializes the output buffer to 128 when the playback sample format is ma_format_u8.
69710
69711v0.10.5 - 2020-05-05
69712 - Change ma_zero_pcm_frames() to take a 64-bit frame count.
69713 - Add ma_copy_pcm_frames().
69714 - Add MA_NO_GENERATION build option to exclude the `ma_waveform` and `ma_noise` APIs from the build.
69715 - Add support for formatted logging to the VC6 build.
69716 - Fix a crash in the linear resampler when LPF order is 0.
69717 - Fix compilation errors and warnings with older versions of Visual Studio.
69718 - Minor documentation updates.
69719
69720v0.10.4 - 2020-04-12
69721 - Fix a data conversion bug when converting from the client format to the native device format.
69722
69723v0.10.3 - 2020-04-07
69724 - Bring up to date with breaking changes to dr_mp3.
69725 - Remove MA_NO_STDIO. This was causing compilation errors and the maintenance cost versus practical benefit is no longer worthwhile.
69726 - Fix a bug with data conversion where it was unnecessarily converting to s16 or f32 and then straight back to the original format.
69727 - Fix compilation errors and warnings with Visual Studio 2005.
69728 - ALSA: Disable ALSA's automatic data conversion by default and add configuration options to the device config:
69729 - alsa.noAutoFormat
69730 - alsa.noAutoChannels
69731 - alsa.noAutoResample
69732 - WASAPI: Add some overrun recovery for ma_device_type_capture devices.
69733
69734v0.10.2 - 2020-03-22
69735 - Decorate some APIs with MA_API which were missed in the previous version.
69736 - Fix a bug in ma_linear_resampler_set_rate() and ma_linear_resampler_set_rate_ratio().
69737
69738v0.10.1 - 2020-03-17
69739 - Add MA_API decoration. This can be customized by defining it before including miniaudio.h.
69740 - Fix a bug where opening a file would return a success code when in fact it failed.
69741 - Fix compilation errors with Visual Studio 6 and 2003.
69742 - Fix warnings on macOS.
69743
69744v0.10.0 - 2020-03-07
69745 - API CHANGE: Refactor data conversion APIs
69746 - ma_format_converter has been removed. Use ma_convert_pcm_frames_format() instead.
69747 - ma_channel_router has been replaced with ma_channel_converter.
69748 - ma_src has been replaced with ma_resampler
69749 - ma_pcm_converter has been replaced with ma_data_converter
69750 - API CHANGE: Add support for custom memory allocation callbacks. The following APIs have been updated to take an extra parameter for the allocation
69751 callbacks:
69752 - ma_malloc()
69753 - ma_realloc()
69754 - ma_free()
69755 - ma_aligned_malloc()
69756 - ma_aligned_free()
69757 - ma_rb_init() / ma_rb_init_ex()
69758 - ma_pcm_rb_init() / ma_pcm_rb_init_ex()
69759 - API CHANGE: Simplify latency specification in device configurations. The bufferSizeInFrames and bufferSizeInMilliseconds parameters have been replaced with
69760 periodSizeInFrames and periodSizeInMilliseconds respectively. The previous variables defined the size of the entire buffer, whereas the new ones define the
69761 size of a period. The following APIs have been removed since they are no longer relevant:
69762 - ma_get_default_buffer_size_in_milliseconds()
69763 - ma_get_default_buffer_size_in_frames()
69764 - API CHANGE: ma_device_set_stop_callback() has been removed. If you require a stop callback, you must now set it via the device config just like the data
69765 callback.
69766 - API CHANGE: The ma_sine_wave API has been replaced with ma_waveform. The following APIs have been removed:
69767 - ma_sine_wave_init()
69768 - ma_sine_wave_read_f32()
69769 - ma_sine_wave_read_f32_ex()
69770 - API CHANGE: ma_convert_frames() has been updated to take an extra parameter which is the size of the output buffer in PCM frames. Parameters have also been
69771 reordered.
69772 - API CHANGE: ma_convert_frames_ex() has been changed to take a pointer to a ma_data_converter_config object to specify the input and output formats to
69773 convert between.
69774 - API CHANGE: ma_calculate_frame_count_after_src() has been renamed to ma_calculate_frame_count_after_resampling().
69775 - Add support for the following filters:
69776 - Biquad (ma_biquad)
69777 - First order low-pass (ma_lpf1)
69778 - Second order low-pass (ma_lpf2)
69779 - Low-pass with configurable order (ma_lpf)
69780 - First order high-pass (ma_hpf1)
69781 - Second order high-pass (ma_hpf2)
69782 - High-pass with configurable order (ma_hpf)
69783 - Second order band-pass (ma_bpf2)
69784 - Band-pass with configurable order (ma_bpf)
69785 - Second order peaking EQ (ma_peak2)
69786 - Second order notching (ma_notch2)
69787 - Second order low shelf (ma_loshelf2)
69788 - Second order high shelf (ma_hishelf2)
69789 - Add waveform generation API (ma_waveform) with support for the following:
69790 - Sine
69791 - Square
69792 - Triangle
69793 - Sawtooth
69794 - Add noise generation API (ma_noise) with support for the following:
69795 - White
69796 - Pink
69797 - Brownian
69798 - Add encoding API (ma_encoder). This only supports outputting to WAV files via dr_wav.
69799 - Add ma_result_description() which is used to retrieve a human readable description of a given result code.
69800 - Result codes have been changed. Binding maintainers will need to update their result code constants.
69801 - More meaningful result codes are now returned when a file fails to open.
69802 - Internal functions have all been made static where possible.
69803 - Fix potential crash when ma_device object's are not aligned to MA_SIMD_ALIGNMENT.
69804 - Fix a bug in ma_decoder_get_length_in_pcm_frames() where it was returning the length based on the internal sample rate rather than the output sample rate.
69805 - Fix bugs in some backends where the device is not drained properly in ma_device_stop().
69806 - Improvements to documentation.
69807
69808v0.9.10 - 2020-01-15
69809 - Fix compilation errors due to #if/#endif mismatches.
69810 - WASAPI: Fix a bug where automatic stream routing is being performed for devices that are initialized with an explicit device ID.
69811 - iOS: Fix a crash on device uninitialization.
69812
69813v0.9.9 - 2020-01-09
69814 - Fix compilation errors with MinGW.
69815 - Fix compilation errors when compiling on Apple platforms.
69816 - WASAPI: Add support for disabling hardware offloading.
69817 - WASAPI: Add support for disabling automatic stream routing.
69818 - Core Audio: Fix bugs in the case where the internal device uses deinterleaved buffers.
69819 - Core Audio: Add support for controlling the session category (AVAudioSessionCategory) and options (AVAudioSessionCategoryOptions).
69820 - JACK: Fix bug where incorrect ports are connected.
69821
69822v0.9.8 - 2019-10-07
69823 - WASAPI: Fix a potential deadlock when starting a full-duplex device.
69824 - WASAPI: Enable automatic resampling by default. Disable with config.wasapi.noAutoConvertSRC.
69825 - Core Audio: Fix bugs with automatic stream routing.
69826 - Add support for controlling whether or not the content of the output buffer passed in to the data callback is pre-initialized
69827 to zero. By default it will be initialized to zero, but this can be changed by setting noPreZeroedOutputBuffer in the device
69828 config. Setting noPreZeroedOutputBuffer to true will leave the contents undefined.
69829 - Add support for clipping samples after the data callback has returned. This only applies when the playback sample format is
69830 configured as ma_format_f32. If you are doing clipping yourself, you can disable this overhead by setting noClip to true in
69831 the device config.
69832 - Add support for master volume control for devices.
69833 - Use ma_device_set_master_volume() to set the volume to a factor between 0 and 1, where 0 is silence and 1 is full volume.
69834 - Use ma_device_set_master_gain_db() to set the volume in decibels where 0 is full volume and < 0 reduces the volume.
69835 - Fix warnings emitted by GCC when `__inline__` is undefined or defined as nothing.
69836
69837v0.9.7 - 2019-08-28
69838 - Add support for loopback mode (WASAPI only).
69839 - To use this, set the device type to ma_device_type_loopback, and then fill out the capture section of the device config.
69840 - If you need to capture from a specific output device, set the capture device ID to that of a playback device.
69841 - Fix a crash when an error is posted in ma_device_init().
69842 - Fix a compilation error when compiling for ARM architectures.
69843 - Fix a bug with the audio(4) backend where the device is incorrectly being opened in non-blocking mode.
69844 - Fix memory leaks in the Core Audio backend.
69845 - Minor refactoring to the WinMM, ALSA, PulseAudio, OSS, audio(4), sndio and null backends.
69846
69847v0.9.6 - 2019-08-04
69848 - Add support for loading decoders using a wchar_t string for file paths.
69849 - Don't trigger an assert when ma_device_start() is called on a device that is already started. This will now log a warning
69850 and return MA_INVALID_OPERATION. The same applies for ma_device_stop().
69851 - Try fixing an issue with PulseAudio taking a long time to start playback.
69852 - Fix a bug in ma_convert_frames() and ma_convert_frames_ex().
69853 - Fix memory leaks in the WASAPI backend.
69854 - Fix a compilation error with Visual Studio 2010.
69855
69856v0.9.5 - 2019-05-21
69857 - Add logging to ma_dlopen() and ma_dlsym().
69858 - Add ma_decoder_get_length_in_pcm_frames().
69859 - Fix a bug with capture on the OpenSL|ES backend.
69860 - Fix a bug with the ALSA backend where a device would not restart after being stopped.
69861
69862v0.9.4 - 2019-05-06
69863 - Add support for C89. With this change, miniaudio should compile clean with GCC/Clang with "-std=c89 -ansi -pedantic" and
69864 Microsoft compilers back to VC6. Other compilers should also work, but have not been tested.
69865
69866v0.9.3 - 2019-04-19
69867 - Fix compiler errors on GCC when compiling with -std=c99.
69868
69869v0.9.2 - 2019-04-08
69870 - Add support for per-context user data.
69871 - Fix a potential bug with context configs.
69872 - Fix some bugs with PulseAudio.
69873
69874v0.9.1 - 2019-03-17
69875 - Fix a bug where the output buffer is not getting zeroed out before calling the data callback. This happens when
69876 the device is running in passthrough mode (not doing any data conversion).
69877 - Fix an issue where the data callback is getting called too frequently on the WASAPI and DirectSound backends.
69878 - Fix error on the UWP build.
69879 - Fix a build error on Apple platforms.
69880
69881v0.9 - 2019-03-06
69882 - Rebranded to "miniaudio". All namespaces have been renamed from "mal" to "ma".
69883 - API CHANGE: ma_device_init() and ma_device_config_init() have changed significantly:
69884 - The device type, device ID and user data pointer have moved from ma_device_init() to the config.
69885 - All variations of ma_device_config_init_*() have been removed in favor of just ma_device_config_init().
69886 - ma_device_config_init() now takes only one parameter which is the device type. All other properties need
69887 to be set on the returned object directly.
69888 - The onDataCallback and onStopCallback members of ma_device_config have been renamed to "dataCallback"
69889 and "stopCallback".
69890 - The ID of the physical device is now split into two: one for the playback device and the other for the
69891 capture device. This is required for full-duplex. These are named "pPlaybackDeviceID" and "pCaptureDeviceID".
69892 - API CHANGE: The data callback has changed. It now uses a unified callback for all device types rather than
69893 being separate for each. It now takes two pointers - one containing input data and the other output data. This
69894 design in required for full-duplex. The return value is now void instead of the number of frames written. The
69895 new callback looks like the following:
69896 void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
69897 - API CHANGE: Remove the log callback parameter from ma_context_config_init(). With this change,
69898 ma_context_config_init() now takes no parameters and the log callback is set via the structure directly. The
69899 new policy for config initialization is that only mandatory settings are passed in to *_config_init(). The
69900 "onLog" member of ma_context_config has been renamed to "logCallback".
69901 - API CHANGE: Remove ma_device_get_buffer_size_in_bytes().
69902 - API CHANGE: Rename decoding APIs to "pcm_frames" convention.
69903 - mal_decoder_read() -> ma_decoder_read_pcm_frames()
69904 - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame()
69905 - API CHANGE: Rename sine wave reading APIs to f32 convention.
69906 - mal_sine_wave_read() -> ma_sine_wave_read_f32()
69907 - mal_sine_wave_read_ex() -> ma_sine_wave_read_f32_ex()
69908 - API CHANGE: Remove some deprecated APIs
69909 - mal_device_set_recv_callback()
69910 - mal_device_set_send_callback()
69911 - mal_src_set_input_sample_rate()
69912 - mal_src_set_output_sample_rate()
69913 - API CHANGE: Add log level to the log callback. New signature:
69914 - void on_log(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
69915 - API CHANGE: Changes to result codes. Constants have changed and unused codes have been removed. If you're
69916 a binding mainainer you will need to update your result code constants.
69917 - API CHANGE: Change the order of the ma_backend enums to priority order. If you are a binding maintainer, you
69918 will need to update.
69919 - API CHANGE: Rename mal_dsp to ma_pcm_converter. All functions have been renamed from mal_dsp_*() to
69920 ma_pcm_converter_*(). All structures have been renamed from mal_dsp* to ma_pcm_converter*.
69921 - API CHANGE: Reorder parameters of ma_decoder_read_pcm_frames() to be consistent with the new parameter order scheme.
69922 - The resampling algorithm has been changed from sinc to linear. The rationale for this is that the sinc implementation
69923 is too inefficient right now. This will hopefully be improved at a later date.
69924 - Device initialization will no longer fall back to shared mode if exclusive mode is requested but is unusable.
69925 With this change, if you request an device in exclusive mode, but exclusive mode is not supported, it will not
69926 automatically fall back to shared mode. The client will need to reinitialize the device in shared mode if that's
69927 what they want.
69928 - Add ring buffer API. This is ma_rb and ma_pcm_rb, the difference being that ma_rb operates on bytes and
69929 ma_pcm_rb operates on PCM frames.
69930 - Add Web Audio backend. This is used when compiling with Emscripten. The SDL backend, which was previously
69931 used for web support, will be removed in a future version.
69932 - Add AAudio backend (Android Audio). This is the new priority backend for Android. Support for AAudio starts
69933 with Android 8. OpenSL|ES is used as a fallback for older versions of Android.
69934 - Remove OpenAL and SDL backends.
69935 - Fix a possible deadlock when rapidly stopping the device after it has started.
69936 - Update documentation.
69937 - Change licensing to a choice of public domain _or_ MIT-0 (No Attribution).
69938
69939v0.8.14 - 2018-12-16
69940 - Core Audio: Fix a bug where the device state is not set correctly after stopping.
69941 - Add support for custom weights to the channel router.
69942 - Update decoders to use updated APIs in dr_flac, dr_mp3 and dr_wav.
69943
69944v0.8.13 - 2018-12-04
69945 - Core Audio: Fix a bug with channel mapping.
69946 - Fix a bug with channel routing where the back/left and back/right channels have the wrong weight.
69947
69948v0.8.12 - 2018-11-27
69949 - Drop support for SDL 1.2. The Emscripten build now requires "-s USE_SDL=2".
69950 - Fix a linking error with ALSA.
69951 - Fix a bug on iOS where the device name is not set correctly.
69952
69953v0.8.11 - 2018-11-21
69954 - iOS bug fixes.
69955 - Minor tweaks to PulseAudio.
69956
69957v0.8.10 - 2018-10-21
69958 - Core Audio: Fix a hang when uninitializing a device.
69959 - Fix a bug where an incorrect value is returned from mal_device_stop().
69960
69961v0.8.9 - 2018-09-28
69962 - Fix a bug with the SDL backend where device initialization fails.
69963
69964v0.8.8 - 2018-09-14
69965 - Fix Linux build with the ALSA backend.
69966 - Minor documentation fix.
69967
69968v0.8.7 - 2018-09-12
69969 - Fix a bug with UWP detection.
69970
69971v0.8.6 - 2018-08-26
69972 - Automatically switch the internal device when the default device is unplugged. Note that this is still in the
69973 early stages and not all backends handle this the same way. As of this version, this will not detect a default
69974 device switch when changed from the operating system's audio preferences (unless the backend itself handles
69975 this automatically). This is not supported in exclusive mode.
69976 - WASAPI and Core Audio: Add support for stream routing. When the application is using a default device and the
69977 user switches the default device via the operating system's audio preferences, miniaudio will automatically switch
69978 the internal device to the new default. This is not supported in exclusive mode.
69979 - WASAPI: Add support for hardware offloading via IAudioClient2. Only supported on Windows 8 and newer.
69980 - WASAPI: Add support for low-latency shared mode via IAudioClient3. Only supported on Windows 10 and newer.
69981 - Add support for compiling the UWP build as C.
69982 - mal_device_set_recv_callback() and mal_device_set_send_callback() have been deprecated. You must now set this
69983 when the device is initialized with mal_device_init*(). These will be removed in version 0.9.0.
69984
69985v0.8.5 - 2018-08-12
69986 - Add support for specifying the size of a device's buffer in milliseconds. You can still set the buffer size in
69987 frames if that suits you. When bufferSizeInFrames is 0, bufferSizeInMilliseconds will be used. If both are non-0
69988 then bufferSizeInFrames will take priority. If both are set to 0 the default buffer size is used.
69989 - Add support for the audio(4) backend to OpenBSD.
69990 - Fix a bug with the ALSA backend that was causing problems on Raspberry Pi. This significantly improves the
69991 Raspberry Pi experience.
69992 - Fix a bug where an incorrect number of samples is returned from sinc resampling.
69993 - Add support for setting the value to be passed to internal calls to CoInitializeEx().
69994 - WASAPI and WinMM: Stop the device when it is unplugged.
69995
69996v0.8.4 - 2018-08-06
69997 - Add sndio backend for OpenBSD.
69998 - Add audio(4) backend for NetBSD.
69999 - Drop support for the OSS backend on everything except FreeBSD and DragonFly BSD.
70000 - Formats are now native-endian (were previously little-endian).
70001 - Mark some APIs as deprecated:
70002 - mal_src_set_input_sample_rate() and mal_src_set_output_sample_rate() are replaced with mal_src_set_sample_rate().
70003 - mal_dsp_set_input_sample_rate() and mal_dsp_set_output_sample_rate() are replaced with mal_dsp_set_sample_rate().
70004 - Fix a bug when capturing using the WASAPI backend.
70005 - Fix some aliasing issues with resampling, specifically when increasing the sample rate.
70006 - Fix warnings.
70007
70008v0.8.3 - 2018-07-15
70009 - Fix a crackling bug when resampling in capture mode.
70010 - Core Audio: Fix a bug where capture does not work.
70011 - ALSA: Fix a bug where the worker thread can get stuck in an infinite loop.
70012 - PulseAudio: Fix a bug where mal_context_init() succeeds when PulseAudio is unusable.
70013 - JACK: Fix a bug where mal_context_init() succeeds when JACK is unusable.
70014
70015v0.8.2 - 2018-07-07
70016 - Fix a bug on macOS with Core Audio where the internal callback is not called.
70017
70018v0.8.1 - 2018-07-06
70019 - Fix compilation errors and warnings.
70020
70021v0.8 - 2018-07-05
70022 - Changed MAL_IMPLEMENTATION to MINI_AL_IMPLEMENTATION for consistency with other libraries. The old
70023 way is still supported for now, but you should update as it may be removed in the future.
70024 - API CHANGE: Replace device enumeration APIs. mal_enumerate_devices() has been replaced with
70025 mal_context_get_devices(). An additional low-level device enumration API has been introduced called
70026 mal_context_enumerate_devices() which uses a callback to report devices.
70027 - API CHANGE: Rename mal_get_sample_size_in_bytes() to mal_get_bytes_per_sample() and add
70028 mal_get_bytes_per_frame().
70029 - API CHANGE: Replace mal_device_config.preferExclusiveMode with mal_device_config.shareMode.
70030 - This new config can be set to mal_share_mode_shared (default) or mal_share_mode_exclusive.
70031 - API CHANGE: Remove excludeNullDevice from mal_context_config.alsa.
70032 - API CHANGE: Rename MAL_MAX_SAMPLE_SIZE_IN_BYTES to MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES.
70033 - API CHANGE: Change the default channel mapping to the standard Microsoft mapping.
70034 - API CHANGE: Remove backend-specific result codes.
70035 - API CHANGE: Changes to the format conversion APIs (mal_pcm_f32_to_s16(), etc.)
70036 - Add support for Core Audio (Apple).
70037 - Add support for PulseAudio.
70038 - This is the highest priority backend on Linux (higher priority than ALSA) since it is commonly
70039 installed by default on many of the popular distros and offer's more seamless integration on
70040 platforms where PulseAudio is used. In addition, if PulseAudio is installed and running (which
70041 is extremely common), it's better to just use PulseAudio directly rather than going through the
70042 "pulse" ALSA plugin (which is what the "default" ALSA device is likely set to).
70043 - Add support for JACK.
70044 - Remove dependency on asound.h for the ALSA backend. This means the ALSA development packages are no
70045 longer required to build miniaudio.
70046 - Remove dependency on dsound.h for the DirectSound backend. This fixes build issues with some
70047 distributions of MinGW.
70048 - Remove dependency on audioclient.h for the WASAPI backend. This fixes build issues with some
70049 distributions of MinGW.
70050 - Add support for dithering to format conversion.
70051 - Add support for configuring the priority of the worker thread.
70052 - Add a sine wave generator.
70053 - Improve efficiency of sample rate conversion.
70054 - Introduce the notion of standard channel maps. Use mal_get_standard_channel_map().
70055 - Introduce the notion of default device configurations. A default config uses the same configuration
70056 as the backend's internal device, and as such results in a pass-through data transmission pipeline.
70057 - Add support for passing in NULL for the device config in mal_device_init(), which uses a default
70058 config. This requires manually calling mal_device_set_send/recv_callback().
70059 - Add support for decoding from raw PCM data (mal_decoder_init_raw(), etc.)
70060 - Make mal_device_init_ex() more robust.
70061 - Make some APIs more const-correct.
70062 - Fix errors with SDL detection on Apple platforms.
70063 - Fix errors with OpenAL detection.
70064 - Fix some memory leaks.
70065 - Fix a bug with opening decoders from memory.
70066 - Early work on SSE2, AVX2 and NEON optimizations.
70067 - Miscellaneous bug fixes.
70068 - Documentation updates.
70069
70070v0.7 - 2018-02-25
70071 - API CHANGE: Change mal_src_read_frames() and mal_dsp_read_frames() to use 64-bit sample counts.
70072 - Add decoder APIs for loading WAV, FLAC, Vorbis and MP3 files.
70073 - Allow opening of devices without a context.
70074 - In this case the context is created and managed internally by the device.
70075 - Change the default channel mapping to the same as that used by FLAC.
70076 - Fix build errors with macOS.
70077
70078v0.6c - 2018-02-12
70079 - Fix build errors with BSD/OSS.
70080
70081v0.6b - 2018-02-03
70082 - Fix some warnings when compiling with Visual C++.
70083
70084v0.6a - 2018-01-26
70085 - Fix errors with channel mixing when increasing the channel count.
70086 - Improvements to the build system for the OpenAL backend.
70087 - Documentation fixes.
70088
70089v0.6 - 2017-12-08
70090 - API CHANGE: Expose and improve mutex APIs. If you were using the mutex APIs before this version you'll
70091 need to update.
70092 - API CHANGE: SRC and DSP callbacks now take a pointer to a mal_src and mal_dsp object respectively.
70093 - API CHANGE: Improvements to event and thread APIs. These changes make these APIs more consistent.
70094 - Add support for SDL and Emscripten.
70095 - Simplify the build system further for when development packages for various backends are not installed.
70096 With this change, when the compiler supports __has_include, backends without the relevant development
70097 packages installed will be ignored. This fixes the build for old versions of MinGW.
70098 - Fixes to the Android build.
70099 - Add mal_convert_frames(). This is a high-level helper API for performing a one-time, bulk conversion of
70100 audio data to a different format.
70101 - Improvements to f32 -> u8/s16/s24/s32 conversion routines.
70102 - Fix a bug where the wrong value is returned from mal_device_start() for the OpenSL backend.
70103 - Fixes and improvements for Raspberry Pi.
70104 - Warning fixes.
70105
70106v0.5 - 2017-11-11
70107 - API CHANGE: The mal_context_init() function now takes a pointer to a mal_context_config object for
70108 configuring the context. The works in the same kind of way as the device config. The rationale for this
70109 change is to give applications better control over context-level properties, add support for backend-
70110 specific configurations, and support extensibility without breaking the API.
70111 - API CHANGE: The alsa.preferPlugHW device config variable has been removed since it's not really useful for
70112 anything anymore.
70113 - ALSA: By default, device enumeration will now only enumerate over unique card/device pairs. Applications
70114 can enable verbose device enumeration by setting the alsa.useVerboseDeviceEnumeration context config
70115 variable.
70116 - ALSA: When opening a device in shared mode (the default), the dmix/dsnoop plugin will be prioritized. If
70117 this fails it will fall back to the hw plugin. With this change the preferExclusiveMode config is now
70118 honored. Note that this does not happen when alsa.useVerboseDeviceEnumeration is set to true (see above)
70119 which is by design.
70120 - ALSA: Add support for excluding the "null" device using the alsa.excludeNullDevice context config variable.
70121 - ALSA: Fix a bug with channel mapping which causes an assertion to fail.
70122 - Fix errors with enumeration when pInfo is set to NULL.
70123 - OSS: Fix a bug when starting a device when the client sends 0 samples for the initial buffer fill.
70124
70125v0.4 - 2017-11-05
70126 - API CHANGE: The log callback is now per-context rather than per-device and as is thus now passed to
70127 mal_context_init(). The rationale for this change is that it allows applications to capture diagnostic
70128 messages at the context level. Previously this was only available at the device level.
70129 - API CHANGE: The device config passed to mal_device_init() is now const.
70130 - Added support for OSS which enables support on BSD platforms.
70131 - Added support for WinMM (waveOut/waveIn).
70132 - Added support for UWP (Universal Windows Platform) applications. Currently C++ only.
70133 - Added support for exclusive mode for selected backends. Currently supported on WASAPI.
70134 - POSIX builds no longer require explicit linking to libpthread (-lpthread).
70135 - ALSA: Explicit linking to libasound (-lasound) is no longer required.
70136 - ALSA: Latency improvements.
70137 - ALSA: Use MMAP mode where available. This can be disabled with the alsa.noMMap config.
70138 - ALSA: Use "hw" devices instead of "plughw" devices by default. This can be disabled with the
70139 alsa.preferPlugHW config.
70140 - WASAPI is now the highest priority backend on Windows platforms.
70141 - Fixed an error with sample rate conversion which was causing crackling when capturing.
70142 - Improved error handling.
70143 - Improved compiler support.
70144 - Miscellaneous bug fixes.
70145
70146v0.3 - 2017-06-19
70147 - API CHANGE: Introduced the notion of a context. The context is the highest level object and is required for
70148 enumerating and creating devices. Now, applications must first create a context, and then use that to
70149 enumerate and create devices. The reason for this change is to ensure device enumeration and creation is
70150 tied to the same backend. In addition, some backends are better suited to this design.
70151 - API CHANGE: Removed the rewinding APIs because they're too inconsistent across the different backends, hard
70152 to test and maintain, and just generally unreliable.
70153 - Added helper APIs for initializing mal_device_config objects.
70154 - Null Backend: Fixed a crash when recording.
70155 - Fixed build for UWP.
70156 - Added support for f32 formats to the OpenSL|ES backend.
70157 - Added initial implementation of the WASAPI backend.
70158 - Added initial implementation of the OpenAL backend.
70159 - Added support for low quality linear sample rate conversion.
70160 - Added early support for basic channel mapping.
70161
70162v0.2 - 2016-10-28
70163 - API CHANGE: Add user data pointer as the last parameter to mal_device_init(). The rationale for this
70164 change is to ensure the logging callback has access to the user data during initialization.
70165 - API CHANGE: Have device configuration properties be passed to mal_device_init() via a structure. Rationale:
70166 1) The number of parameters is just getting too much.
70167 2) It makes it a bit easier to add new configuration properties in the future. In particular, there's a
70168 chance there will be support added for backend-specific properties.
70169 - Dropped support for f64, A-law and Mu-law formats since they just aren't common enough to justify the
70170 added maintenance cost.
70171 - DirectSound: Increased the default buffer size for capture devices.
70172 - Added initial implementation of the OpenSL|ES backend.
70173
70174v0.1 - 2016-10-21
70175 - Initial versioned release.
70176*/
70177
70178
70179/*
70180This software is available as a choice of the following licenses. Choose
70181whichever you prefer.
70182
70183===============================================================================
70184ALTERNATIVE 1 - Public Domain (www.unlicense.org)
70185===============================================================================
70186This is free and unencumbered software released into the public domain.
70187
70188Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
70189software, either in source code form or as a compiled binary, for any purpose,
70190commercial or non-commercial, and by any means.
70191
70192In jurisdictions that recognize copyright laws, the author or authors of this
70193software dedicate any and all copyright interest in the software to the public
70194domain. We make this dedication for the benefit of the public at large and to
70195the detriment of our heirs and successors. We intend this dedication to be an
70196overt act of relinquishment in perpetuity of all present and future rights to
70197this software under copyright law.
70198
70199THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
70200IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
70201FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
70202AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
70203ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
70204WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
70205
70206For more information, please refer to <http://unlicense.org/>
70207
70208===============================================================================
70209ALTERNATIVE 2 - MIT No Attribution
70210===============================================================================
70211Copyright 2020 David Reid
70212
70213Permission is hereby granted, free of charge, to any person obtaining a copy of
70214this software and associated documentation files (the "Software"), to deal in
70215the Software without restriction, including without limitation the rights to
70216use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
70217of the Software, and to permit persons to whom the Software is furnished to do
70218so.
70219
70220THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
70221IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
70222FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
70223AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
70224LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
70225OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
70226SOFTWARE.
70227*/
70228